Makefile用法总结
Makefile的编译过程
Makefile的编译过程主要为:makefile文件经过maketools编译后处理目标工程。
语法规则
target : depend1 depenp2
command 1
command 2
depend1 :
...
depend2 :
...
注:Makefile本质上不关注命令如何执行,只是依照规则去执行所有的命令
以下是需要关注的点:
1、目标文件的更新是晚于依赖文件的,若依赖未发生改动,则目标文件不需要重新编译,否则需要重新编译,在构建工程时时常会遇到这种情况。
2、一般情况下Makefile中第一个target就是最终需要执行的目标。
3、每条命令前必须要有tab的缩进,语法要求
4、all将打破第二点规则,会构建所有目标
变量相关内容
$符号
$符号表示取变量的值,当变量名多于一个字符时,需要使用"()"
常用方法:
- $^ 表示所有的依赖文件
- $@ 表示生成的目标文件
- $< 代表第一个依赖文件
^ 表示所有的依赖文件。在规则的命令部分中使用 ^ 可以引用所有的依赖文件。例如:
all: main.o helper.o
gcc $^ -o program
在这个例子中,$^ 将会被扩展为 “main.o helper.o”,因此最终的命令会是 “gcc main.o helper.o -o program”。
$@ 表示生成的目标文件。在规则的命令部分中使用 $@ 可以引用生成的目标文件。例如:
main.o: main.c
gcc -c $< -o $@
在这个例子中,$@ 将会被扩展为 “main.o”,因此最终的命令会是 “gcc -c main.c -o main.o”。
< 代表第一个依赖文件。在规则的命令部分中使用 < 可以引用第一个依赖文件。例如:
main.o: main.c
gcc -c $< -o $@
在这个例子中,$< 将会被扩展为 “main.c”,因此最终的命令会是 “gcc -c main.c -o main.o”。
变量赋值
1、"="是最普通的等号,在Makefile中容易搞错赋值等号,使用 “=”进行赋值,变量的值是整个Makefile中最后被指定的值。
VIR_A = A
VIR_B = $(VIR_A) B
VIR_A = AA
经过上面的赋值后,最后VIR_B的值是AA B,而不是A B,在make时,会把整个Makefile展开,来决定变量的值
2、“:=” 表示直接赋值,赋予当前位置的值。
VIR_A := A
VIR_B := $(VIR_A) B
VIR_A := AA
最后BIR_B的值是A B,即根据当前位置进行赋值。因此相当于“=”,“:=”才是真正意义上的直接赋值
3、“?=” 表示如果该变量没有被赋值,赋值予等号后面的值。
VIR ?= new_value
如果VIR在之前没有被赋值,那么VIR的值就为new_value。
VIR := old_value
VIR ?= new_value
这种情况下,VIR的值就是old_value
4、"+="和平时写代码的理解是一样的,表示将符号后面的值添加到前面的变量上
关键函数 wildcard patsubst
SRC = $(wildcard ./*.c)
匹配目录下所有的.c文件,将其赋值给SRC变量。
OBJ = $(patsubst %.c, %.o, $(SRC))
有三个函数,表示将SRC中所有的.c文件替换成.o文件,将结果保存在OBJ变量中,OBJ变量将包含每个.c文件对应.o目标文件名列表。
伪目标
.PHONY
将 .PHONY 添加到目标将防止Make将phony(假)目标与文件名混淆。
some_file:
touch some_file
touch clean
.PHONY: clean
clean:
rm -f some_file
rm -f clean
在此示例中,如果创建了文件 “clean”,则仍将运行 make clean。
嵌套执行Makefile
-C参数的使用
subsystem:
$(MAKE) -C subdir
make -C指到子目录上执行
export
将某些变量传递到下级Makefile
如果需要传递所有变量,那么只要一个export就行了。后面什么也不用跟,表示传递所有变量
示例:
假设我们有一个主Makefile main.mk 和一个子Makefile sub.mk,我们希望在 main.mk 中定义的变量能够传递给 sub.mk 使用,可以使用 export 指令。
main.mk:
VAR1 = hello
export VAR1
all:
@$(MAKE) -f sub.mk
sub.mk:
all:
@echo "VAR1 from main.mk: $(VAR1)"
在这个示例中,我们定义了一个变量 VAR1,并使用 export 指令将其传递给子 Makefile。然后在 main.mk 的 all 目标中调用 sub.mk。在子 Makefile中,我们可以使用来自主Makefile中的 VAR1 变量。
unexport
如果不想让某些变量传递到下级Makefile,可以使用
示例:
main.mk:
VAR1 = hello
VAR2 = world
export VAR1 VAR2
all:
@$(MAKE) -f sub.mk
sub.mk:
unexport VAR1
all:
@echo "VAR1 from main.mk: $(VAR1)" # 此处不会输出任何东西
@echo "VAR2 from main.mk: $(VAR2)" # 输出 "VAR2 from main.mk: world"
在这个示例中,我们使用了 unexport 指令取消了子Makefile对VAR1的继承。因此在子Makefile中,我们无法访问或者使用来自主Makefile的 VAR1 变量。
头文件和库文件目录添加
头文件
"-I"来指定
在Makefile中则可以这样写:
CFLAGS=-I/home/develop/include
库文件
"-L"来指定库的路径
在Makefile中则这样写:
LDFLAGS=-L/usr/lib -L/path/to/your/lib
"-l"来指定库
在Makefile中则这样写:
LIBS = -lxx -lxxxx
Makefile 显示命令和出错命令
显示相关命令
- Makefile中添加"@"符号,则只执行,不显示命令;
- make -n 表示只显示命令而不执行;
- make -s 表示只执行命令而不显示;
- Makefile中添加"-",表示命令不管怎样都要执行
出错命令
make -i 表示忽略所有错误,强制执行每一条命令;
make -k 表示终止出错指令,继续执行下面的命令;
在Makefile中添加.IGNORE:目标
表示立刻停止该目标和该目标的依赖目标的生成,但是会继续生成此目标之上的其他目标
举例说明:
all: program
program: main.o func.o
gcc -o program main.o func.o
main.o: main.c
gcc -c main.c
func.o: func.c
gcc -c func.c
clean:
rm -f *.o program
.IGNORE: clean
.PHONY: clean
当执行 make clean 时,无论是否出现了清理错误,都应该忽略这些错误,并继续执行make。
Makefile示例
#预定义变量
CC = gcc
#预定义编译目录
SUBDIRS = f1 \
f2 \
main \
obj
#预定义目标
OBJS = f1.o f2.o main.o
BIN = myapp
OBJS_DIR = obj
BIN_DIR = bin
#传递预定义参数
export CC OBJS BIN OBJS_DIR BIN_DIR
all:CHECK_DIR $(SUBDIRS)
CHECK_DIR:
@mkdir -p $(BIN_DIR)
$(SUBDIRS):ECHO
@make -C $@
ECHO:
@echo $(SUBDIRS)
@echo begin compile
clean:
@$(RM) $(OBJS_DIR)/*.o
@rm -rf $(BIN_DIR)
参考:https://zhuanlan.zhihu.com/p/575852387