使用变量
使用C自动编译成*.o的默认规则有个缺陷,由于没有显式地表示*.o依赖于.h头文件,假如修改了头文件的内容,那么*.o并不会更新,这是不可接受的。并且默认规则使用固定的“cc”进行编译,假如想使用ARM-GCC进行交叉编译,那么系统默认的“cc”会导致编译错误。要解决这些问题并且让Makefile变得更加通用,需要引入变量和分支进行处理。
基本语法
Makefile中的变量类似C语言的宏定义,在引用变量的地方使用变量值进行替换。
定义变量的方法:
- “=”:延时赋值,该变量只有在调用的时候,才会被赋值
- “:=”:直接赋值,与延时赋值相反,使用直接赋值的话,变量的值定义时就已经确定了
- “?=”:若变量的值为空,则进行赋值,通常用于设置默认值
- “+=”:追加赋值,可以往变量后面增加新的内容
主要介绍前两种:
A = a
B = $(A)
C = $(A)
A += b
.PHONY:check
check:
echo "A:"$(A)
echo "B:"$(B)
echo "C:"$(C)
通过-f选项指定执行Makefile文件,可以看见只有C是a。这是因为B采用的延时赋值,只有当调用时,才会进行赋值。当调用B时,A的值已经被修改为a b,因此B的变量值也就等于a b。
改造默认规则
使用变量修改默认规则
CC = gcc
CFLAGS = -I .
DEPS = hello_func.h
hello_main:hello_main.o hello_func.o
$(CC) hello_main.o hello_func.o -o hello_main
# %为通配符,让所有.o文件依赖于对应的.c文件
%.o:%.c $(DEPS)
$(CC) $< -c -o $@ $(CFLAGS)
.PHONY:clean
clean:
rm -f *.o hello_main
上面的Makefile文件使用$(CC)替代了gcc,这样编写的Makefile非常容易更换不同的编译器,如要进行交叉编译,只要把开头的编译器名字修改掉即可。
“%”是一个通配符,功能类似“*”,如”%.o”表示所有以”.o”结尾的文件。所以”%.o:%.c”在本例子中等价于”hello_main.o:hello_main.c”、“hello_func.o:hello_func.c”,即等价于o文件依赖于c文件的默认规则。不过这行代码后面的“$(DEPS)”表示它除了依赖c文件,还依赖于变量“$(DEPS)”表示的头文件,所以当头文件修改的话,o文件也会被重新编译。
特殊的变量”$@”,”$<”,可理解为Makefile文件保留的关键字,是系统保留的自动化变量,”$@”代表了目标文件,”$<”代表了第一个依赖文件。即”$@”表示”%.o”,”$<”表示”%.c”。
$(CC) $< -c -o $@ $(CFLAGS)
# 等价于:
gcc hello_main.c -c -o hello_main.o -I .
gcc hello_func.c -c -o hello_func.o -I .
改造链接规则
继续使用变量来修改生成最终目标文件的链接规则:
# 定义变量
TARGET = hello_main
CC = gcc
CFLAGS = -I .
DEPS = hello_func.h
OBJS = hello_main.o hello_func.o
# 目标文件
$(TARGET):$(OBJS)
$(CC) $^ -o $@ $(CFLAGS)
# *.o文件的生成规则
%.o:%.c $(DEPS)
$(CC) $< -c -o $@ $(CFLAGS)
# 伪目标
.PHONY:clean
clean:
rm -f *.o hello_main
其他自动化变量
符号 | 含义 |
---|---|
$@ | 匹配目标文件 |
$% | 与 $@ 类似,但 $% 仅匹配“库”类型的目标文件 |
$< | 依赖中的第一个目标文件 |
$^ | 所有的依赖目标,如果依赖中有重复的,只保留一份 |
$+ | 所有的依赖目标,即使依赖中有重复的也原样保留 |
$? | 所有比目标要新的依赖目标 |
使用分支
ifeq(arg1,arg2)
分支1
else
分支2
endif
分支会比较括号内的参数“arg1”和“arg2”的值是否相同,如果相同,则为真,执行分支 1 的内容,否则的话,执行分支 2 的内容,参数 arg1 和 arg2 可以是变量或者是常量。
使用分支切换GCC编译器的Makefile如下:
ARCH ?= x86
TARGET = hello_main
CFLAGS = -I .
DEPS = hello_func.h
OBJS = hello_main.o hello_func.o
# 根据输入的ARCH变量来选择编译器
ifeq ($(ARCH),x86)
CC = gcc
else
CC = arm-linux-gnueabihf-gcc
endif
$(TARGET):$(OBJS)
$(CC) $^ -o $@ $(CFLAGS)
%.o:%.c $(DEPS)
$(CC) $< -c -o $@ $(CFLAGS)
.PHONY:clean
clean:
rm -f *.o hello_main
Makefile 主要是增加了 ARCH 变量用于选择目标平台,代码中使用“?=”给 ARCH 赋予默认值 x86。
以上示例使用交叉编译进行编译。