Makefile基础
1. = := +=的区别
- =(递归展开赋值,Recursive Assignment)
行为:变量值在使用时动态展开(延迟展开),可以引用后续定义的变量。
特点:变量实际值在引用时才确定。
示例:
VAR1 = hello
VAR2 = $(VAR1) # VAR2 的值在引用时才会展开
VAR1 = world # 修改 VAR1
all:@echo $(VAR2) # 输出 "world"(使用的是最终的 VAR1 值)
- :=(立即展开赋值,Simple Assignment)
行为:变量值在定义时立即展开,后续变量的修改不会影响已赋值的变量。
特点:变量值在定义时即固定,无法引用后续定义的变量。
示例:
VAR1 = hello
VAR2 := $(VAR1) # VAR2 的值立即展开为 "hello"
VAR1 = world # 修改 VAR1
all:@echo $(VAR2) # 输出 "hello"(保持定义时的值)
- +=(追加赋值,Appending Assignment)
行为:在现有变量值的末尾追加新内容,具体的展开规则取决于原变量的定义方式:
若原变量用 = 定义,+= 会递归展开。
若原变量用 := 定义,+= 会立即展开。
示例:
#### 示例1:原变量使用 =
VAR1 = a
VAR1 += b # 等效于 VAR1 = $(VAR1) b(递归展开)
VAR1 = c # 覆盖之前的赋值all:@echo $(VAR1) # 输出 "c"(覆盖生效)
示例2:原变量使用 :=
VAR2 := a
VAR2 += b # 等效于 VAR2 := $(VAR2) b(立即展开为 "a b")
VAR2 := c # 覆盖之前的赋值all:@echo $(VAR2) # 输出 "c"(覆盖生效)
示例3:追加动态生成的变量
VAR3 = a
VAR3 += $(NEW) # NEW 变量后续定义依然生效
NEW = ball:@echo $(VAR3) # 输出 "a b"(递归展开)
核心对比总结
赋值符号 | 展开时机 | 引用后续变量 | 典型场景 |
---|---|---|---|
= | 使用时展开 | ✅ | 需要动态引用变量的场景 |
:= | 定义时展开 | ❌ | 需要立即固定变量值的场景 |
+= | 依赖原赋值类型 | 依原符号规则 | 在现有值后追加内容 |
内核模块 Makefile 中的应用建议
在内核模块的 Makefile 中,常见做法是:
优先使用 =:确保模块的目标文件($(DRIVER_NAME)-objs)可以灵活引用后续定义的变量。
DRIVER_NAME = axidma-mod
$(DRIVER_NAME)-objs = axi_dma.o axidma_chrdev.o # 允许后续追加或修改
$(DRIVER_NAME)-objs += axidma_dma.o # 追加新文件
仅必要时用 :=:当明确需要立即固定变量值时使用(如快速赋初始值)。
实验验证
可以通过以下命令快速测试变量赋值行为:
cat > Makefile <<EOF
VAR1 = a
VAR1 += b
VAR1 = cVAR2 := a
VAR2 += b
VAR2 := call:@echo "VAR1: $(VAR1), VAR2: $(VAR2)"
EOF# 运行 make
make # 输出: VAR1: c, VAR2: c
解析指令
DRIVER_NAME = axidma-mod
$(DRIVER_NAME)-objs = axi_dma.o axidma_chrdev.o axidma_dma.o axidma_of.o
变量语法:展开后变量名为 axidma-mod-objs。
依赖关系:
axidma-mod.ko 模块由以下目标文件(.o)构成:
axi_dma.o(主驱动逻辑)
axidma_chrdev.o(字符设备接口)
axidma_dma.o(DMA 操作相关)
axidma_of.o(设备树支持)
编译规则:
内核构建系统会自动将 .c 源码文件编译为对应的 .o 文件,并链接成 .ko 模块。