makefile几种等于号

本文深入解析Makefile中变量赋值的四种方法:简单展开、递归展开、条件展开和追加方式,详细解释每种方法的工作原理、特点及应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


在make中有四种方式对变量赋值:

1. :=运算符,如MAKE_DEPEND := $(CC) -M

    这种方式叫做“简单展开”,因为在读到makefile中的这一行时等号右边就立即被展开了,等号右边引用的所有变量(如例子中的CC)也会被立即展开。其行为与一般编程和脚本语言相同。
    当等号右边引用的变量(如例子中的CC)还没有被定义时,它被展开成空(nothing)而不是空格之类。

2. =运算符,如MAKE_DEPEND = $(CC) -M

    这种方式叫做“递归展开”,直到该变量被使用时等号右边的内容才会被展开,其实叫做“迟滞展开”更合适。神奇的是,这种展开方式可以不按顺序定义变量。比如:
MAKE_DEPEND = $(CC) -M
...
# Some time later
CC = gcc
只要在此之前没有引用过
MAKE_DEPEND就没问题。

    另外,不止是
“迟滞展开”,事实上每次使用该变量,等号右边的内容都会被重新展开。

3. ?=运算符,如OUTPUT_DIR ?= $(PROJECT_DIR)/out

    这种方式叫“条件展开”,只有当OUTPUT_DIR 还没有被定义过时才进行赋值,否则什么都不做。这种方式在处理环境变量是特别有用。

4. +=运算符,如OUTPUT_DIR += $(PROJECT_DIR)/out

   “追加”方式。 其主要目的是给“递归展开”的变量追加内容。因为简单变量可以用simple := $(simple) new stuff的方式来追加内容;而对于递归展开的变量,recursive = $(recursive) new stuff会导致循环引用。这种情况只能用+=运算符。 
<think>我们正在讨论的是在Android构建系统中实现条件编译,特别是针对不同Android版本的情况。根据引用[1]和[2],我们知道在Android.bp中直接实现条件编译比较复杂,通常需要结合Android.bp和Makefile,或者通过编写Golang代码(修改Soong)来实现。但引用[1]也提到,修改Soong可能不够优雅,应尽量采用非侵入式的方法。用户问题:如何在不同Android版本中实现条件编译?解决方案:1.**使用Android.bp的条件编译特性(推荐)**:Android.bp支持基于产品变量(product_variables)的条件编译。我们可以为不同Android版本定义条件,然后在模块中根据这些条件选择源文件或依赖。2.**结合Makefile(传统方法)**:在Android.mk中,我们可以使用条件判断(如ifeq)来根据版本选择不同的编译选项,但这种方法在Android.bp中不可用,因此需要将条件逻辑放在Makefile中,然后通过Android.bp调用。3. **使用genrule和Go脚本(动态生成)**:如之前讨论,我们可以编写Go脚本来根据Android版本生成不同的Android.bp片段,然后通过genrule和soong_include将其包含到构建中。下面我们重点介绍第一种方法,因为它是Soong构建系统(Android.bp)原生支持的。 ###方法1:使用product_variables(Android.bp原生支持)在Android.bp中,我们可以使用`product_variables`来定义条件,然后在模块中使用这些条件。示例:根据Android版本(API级别)选择不同的源文件或依赖。步骤1:在设备的产品配置中定义变量(例如在`device.mk`中): ```makefile#设置当前设备的Android版本(例如,Android12对应API级别31) PRODUCT_SHIPPING_API_LEVEL :=31```步骤2:在Android.bp中,我们可以使用`product_variables`来获取这个值,然后进行条件判断: ```soong//定义一个条件:如果设备的API级别大于等于31(Android12)product_variables:{shipping_api_level:{cflags: ["-DANDROID_12_OR_ABOVE"],srcs:["android12_specific.cpp"],},//也可以定义其他条件shipping_api_level: {value:30,cflags:["-DANDROID_11_OR_ABOVE"],srcs:["android11_specific.cpp"],}, },//或者更通用的写法:使用条件表达式cc_library {name: "version_aware_lib",srcs:["common.cpp",] +select {"//build/make:api_level_31":["android12_specific.cpp"],"//build/make:api_level_30":["android11_specific.cpp"],"//conditions:default": ["legacy_support.cpp"],},cflags: select{"//build/make:api_level_31": ["-DANDROID_12_OR_ABOVE"],"//build/make:api_level_30": ["-DANDROID_11_OR_ABOVE"],"//conditions:default":["-DLEGACY"],},} ```注意:上述`select`语句中的条件标签(如`"//build/make:api_level_31"`)是由构建系统预定义的,它们对应于不同的API级别。我们可以使用这些预定义的条件来选择不同的源文件或编译标志。但是,预定义的条件标签可能并不直接对应每个API级别,因此我们可以自定义条件:在Soong中,我们可以通过编写一个Go模块来定义新的条件,但为了简单,我们可以使用现有的条件变量。另一种方法是使用`product_variables`直接传递值: ```soongproduct_variables: {shipping_api_level: {cflags:["-DSHIPPING_API_LEVEL=%s"],}, },```然后在代码中使用宏`SHIPPING_API_LEVEL`来判断版本。###方法2:使用构建系统变量(BoardConfig.mk)和Android.bp的转换在BoardConfig.mk中定义变量: ```makefile#设置Android版本TARGET_ANDROID_VERSION:=12```然后在Android.bp中通过`soong_config_module_type`来使用这个变量(需要Soong支持)。由于这种方式需要修改Soong,可能不够通用,因此我们主要使用方法1。###方法3:使用genrule动态生成(非侵入式)如果条件编译非常复杂,我们可以使用genrule运行一个Go(或Python)脚本来根据Android版本生成不同的Android.bp片段。示例:1.编写一个脚本(如`generate_android_bp.py`),它读取环境变量(如`PLATFORM_VERSION`)然后生成一个Android.bp片段。2.在Android.bp中使用genrule调用这个脚本。3.使用soong_include包含生成的片段。 ```soonggenrule{name:"generate_version_specific_bp",tool_files:["generate_android_bp.py"],cmd: "python $(location generate_android_bp.py)--version$(PLATFORM_VERSION)> $(out)",out: ["version_specific.bp"],} soong_include {makefile:"version_specific.bp", }```在`generate_android_bp.py`中,我们可以根据传入的版本参数生成不同的模块定义。###总结对于不同Android版本的条件编译,推荐使用Android.bp原生的`select`语句和预定义的条件标签,或者使用`product_variables`。如果条件非常复杂,可以考虑使用genrule动态生成构建规则。注意事项:-条件编译会增加构建的复杂性,应谨慎使用。-尽量在代码中使用运行时检查代替条件编译,以保持构建系统的简单性。引用[1]提到,条件编译在Android.bp中实现较为复杂,需要结合Makefile,但通过上述方法,我们可以尽量在Android.bp框架内完成。[^1]:Android.bp中支持条件编译。综上,我们可以看到在Google在引入Android.bp后,想要使用条件编译会变得较为复杂,需要结合Android.bp与Makefile,在两者的共同作用下才能达到我们的目的。当然,这篇文章中所讲的方法并不是唯一的方法,我们还可以通过修改Soong编译系统,编写Golang代码来实现同样的效果,不过在我看来这种方式不够优雅,我们应尽可能在不做侵入式修改的前提下来实现我们的目的。[^2]:使用宏我们可以在编译脚本预先指定程序里面的某些变量值,使用条件宏可以引入不同的代码实现,最终实现按需编译代码,而不是所有的平台代码一起编译,达到减小最终输出的大小。§§相关问题§§1.如何在Soong构建系统中使用预定义的条件标签(如API级别)? 2.在Android.bp中,如何为自定义的产品变量添加条件编译? 3.使用genrule动态生成Android.bp片段时,如何传递构建系统变量(如PLATFORM_VERSION)?</think>在 Android 系统中实现针对不同版本的条件编译,主要有以下几种方法: ### 方法一:使用 Android.bp 原生条件编译(推荐) ```soong:Android.bp cc_library { name: "version_aware_lib", srcs: [ "common.cpp", ] + select { // API 级别条件判断 "//build/make:api_level_31": ["android12_specific.cpp"], "//build/make:api_level_30": ["android11_specific.cpp"], "//conditions:default": ["legacy_support.cpp"], }, cflags: select { "//build/make:api_level_31": ["-DANDROID_12"], "//build/make:api_level_30": ["-DANDROID_11"], "//conditions:default": ["-DLEGACY_SUPPORT"], }, } ``` **机制说明**: 1. 使用 `select()` 函数根据 API 级别选择不同的源文件和编译标志 2. 条件标签如 `//build/make:api_level_31` 对应 Android 12 (API 31) 3. 构建系统自动处理版本检测和条件匹配[^1] ### 方法二:通过 Makefile 传递条件变量 ```makefile:Android.mk # 检测 Android 版本 ifeq ($(call is-platform-sdk-version,31),true) LOCAL_CFLAGS += -DANDROID_12 else ifeq ($(call is-platform-sdk-version,30),true) LOCAL_CFLAGS += -DANDROID_11 else LOCAL_CFLAGS += -DLEGACY_SUPPORT endif ``` ```soong:Android.bp // 在 Android.bp 中引用 Makefile 变量 cc_library { name: "hybrid_lib", cflags: ["$(TARGET_ANDROID_CFLAGS)"], ... } ``` **工作流程**: ```mermaid graph TD A[Makefile] -->|设置条件变量| B[Android.bp] B --> C[构建系统] ``` ### 方法三:使用 Python 脚本动态生成配置 ```python:generate_bp.py import os android_version = os.getenv("PLATFORM_VERSION", "11") with open("dynamic_config.bp", "w") as f: if android_version.startswith("12"): f.write('cflags: ["-DANDROID_12"]') elif android_version.startswith("11"): f.write('cflags: ["-DANDROID_11"]') else: f.write('cflags: ["-DLEGACY"]') ``` ```soong:Android.bp // 集成到构建系统 genrule { name: "generate_config", tool_files: ["generate_bp.py"], cmd: "python $(location) > $(out)", out: ["dynamic_config.bp"], } soong_include { makefile: "dynamic_config.bp", } ``` ### 方法四:代码级条件编译(C/C++/Java) ```c #if __ANDROID_API__ >= 31 // Android 12+ 专用代码 #elif __ANDROID_API__ >= 24 // Android 7.0+ 代码 #else // 兼容旧版本代码 #endif ``` ```java if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // Android 12 特性 } else { // 兼容实现 } ``` ### 最佳实践建议 1. **优先使用 Android.bp 的 `select()`** 进行构建级条件处理 2. 代码中使用 `SDK_INT` 检查实现运行时兼容 3. 复杂场景结合 Python/Go 脚本生成配置 4. 使用版本范围而非精确版本号提高兼容性[^2] 5. 在 `product_variables` 中定义统一版本常量[^1] > **关键区别**:构建时条件编译(Android.bp/Makefile)决定哪些代码被编译,运行时条件编译(SDK_INT)决定代码执行路径[^2]。 [^1]: Android.bp中支持条件编译。综上,我们可以看到在Google在引入Android.bp后,想要使用条件编译会变得较为复杂,需要结合Android.bp与Makefile,在两者的共同作用下才能达到我们的目的。 [^2]: 使用宏我们可以在编译脚本预先指定程序里面的某些变量值,使用条件宏可以引入不同的代码实现,最终实现按需编译代码,而不是所有的平台代码一起编译,达到减小最终输出的大小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值