5 Makefile项目管理
脚本文件:把一系列命令放在一个文件中,批量执行
命名:makefile Makefile
一个规则:
目标:依赖条件
命令(命令前是1个table缩进)
两个函数:
#wildcard函数,配合通配符找出文件夹中所有 .c文件
src=$(wildcard ./*.c)
#将所有的.c的文件的后缀更换成.o
obj=$(patsubst %.c,%.o,$(src))
三个自动变量:
三个自动变量只能用于命令行当中,不能用于目标和依赖条件!!!
$@:代表目标文件;
$<:代表依赖条件的第一条;
$^:代表所有的依赖条件;
5.1 一个规则
基本原则:
1.若想生成目标,检查规则中的依赖条件是否存在,如不存在,则寻找是否有规则生成该依赖文件;
2.检查规则中的目标是否需要更新,必须先检查它的所有依赖,依赖中有更新,则目标必须更新
1)分析各个目标和依赖之间的关系
2)根据依赖关系自底向上执行命令;
3)根据修改时间比目标新,确定更新;
4)如果目标不依赖任何条件,则执行对应命令,以示更新
文件目录如下:
daniel@daniel-Vostro-5471:~/文档/OS/test/make_test/make1$ tree
.
├── add.c
├── cal.h
├── Makefile
├── sub.c
└── test.c
0 directories, 5 files
对应的Makefile的文件如下:
test: test.o add.o sub.o
gcc test.o add.o sub.o -o test -I.
test.o:test.c
gcc -c test.c -o test.o
add.o:add.c
gcc -c add.c -o add.o
sub.o:sub.c
gcc -c sub.c -o sub.o
#每个文件分开的好处是 当单个文件改动时,其它文件不需要重新编译
#只需要编译改动的文件,然后重新链接
make命令将检查makefile文件,并将第一条语句作为终极目标;所以最好将总的生成步骤放在最前面;
解决上面的弊端,可以使用ALL命令指定makefile的最终目的;
ALL:a.out
5.2 两个函数
makefile中常用的两个函数:
#找到当前目录下所有后缀为.c的文件,赋值给src
src=$(wildcard ./*.c)
#把src变量里面所有后缀为.c的文件替换成.o
#将参数3中含有参数1的部分替换为参数2
obj=$(patsubst %.c,%.o,$(src))
$(src)代表将函数取值;上述函数调用完之后,变量
src= add.c sub.c test.c
obj=add.o sub.o test.o
有了两个函数之后的makefile可以改写成如下形式:
#指定最终目标
ALL:main
#使用两个函数,src变量代表源文件,obj代表编译生成的.o文件
src=$(wildcard ./*.c) #add.c sub.c test.c
obj=$(patsubst %.c,%.o,$(src)) #add.o sub.o test.o
main:$(obj)
gcc $(obj) -o main -I.
test.o:test.c
gcc -c test.c -o test.o -I.
add.o:add.c
gcc -c add.c -o add.o
sub.o:sub.c
gcc -c sub.c -o sub.o
#单独执行 make clean命令,会将文件中的 *.o文件和main文件删除
#添加 make clean -n选项,是模拟执行命令,会在命令行显示将要删除的文件
clean:
rm -rf $(obj) main
clean:
-rm -rf $(obj) a.out
#单独执行 make clean命令,会将文件中的 *.o文件和main文件删除
#添加 make clean -n选项,是模拟执行命令
clean命令没有依赖,rm之前的“-”,作用是删除不存在的文件时,不报错;
5.3 三个自动变量
3个自动变量:
$@:在规则的命令中,表示规则中的目标(不可以用于目标或者依赖项中);
$^:在规则的命令中,表示规则的所有依赖条件;
$<:在规则的命令中,表示规则中的第一个依赖条件;如果将该自动变量应用与模式规则中,它可将依赖条件列表中的依赖,依次取出,套用模式规则;
有了3个自动变量,上面的makefile可以进一步的改写为,
ALL:main
src=$(wildcard ./*.c) #add.c sub.c test.c
obj=$(patsubst %.c,%.o,$(src)) #add.o sub.o test.o
main:$(obj)
gcc $^ -o $@ -I.
test.o:test.c
gcc -c $< -o $@ -I.
add.o:add.c
gcc -c $< -o $@
sub.o:sub.c
gcc -c $< -o $@
clean:
-rm -rf $(obj) main
5.4 模式规则
上一节的makefile将每个.c 编译成.o的过程中,命令都是相同的,即:
gcc -c $< -o $@
这样可以使用模式规则将多条指令改写为下面的形式:
%.o:%.c
gcc -c $< -o $@
上面的makefile可以进一步改写成为:
ALL:main
src=$(wildcard ./*.c) #add.c sub.c test.c
obj=$(patsubst %.c,%.o,$(src)) #add.o sub.o test.o
main:$(obj)
gcc $^ -o $@ -I.
%.o:%.c
gcc -c $< -o $@
clean:
-rm -rf $(obj)
#单独执行 make clean命令,会将文件中的 *.o文件和main文件删除
#添加 make clean -n选项,是模拟执行命令
通过多次的更新,实现了makefile的可扩展功能,在原来文件的基础上,添加乘法模块,mul.c,makefile并不需要重新编写,
daniel@daniel-Vostro-5471:~/文档/OS/test/make_test/make3$ tree
├── add.c
├── cal.h
├── makefile
├── mul.c
├── sub.c
└── test.c
0 directories, 6 files
5.5 扩展
静态模式规则:
下面的语句是,向obj变量中的.o文件,生成.c文件;
$(obj):%.o:%c
gcc -c $< -o $@
生成伪目标:
示例:在上面的文件夹中新增一个clean文件,即通过命令 touch clean,文件目录如下:
daniel@daniel-Vostro-5471:~/文档/OS/test/make_test/make3$ tree
.
├── add.c
├── cal.h
├── clean
├── makefile
├── mul.c
├── sub.c
└── test.c
0 directories, 7 files
之后再执行 make clean命令,会出现错误:
daniel@daniel-Vostro-5471:~/文档/OS/test/make_test/make3$ make clean
make: “clean”已是最新。
这是因为文件夹下的clean文件,干扰了make clean文件的执行,尤其是在clean这种没有依赖项的命令中,在这种情况下,可以通过添加伪目标解决,如下:
伪目标:不生成文件,但是要执行目标;
ALL:main
src=$(wildcard ./*.c) #add.c sub.c test.c
obj=$(patsubst %.c,%.o,$(src)) #add.o sub.o test.o
main:$(obj)
gcc $^ -o $@ -I.
%.o:%.c
gcc -c $< -o $@
clean:
-rm -rf $(obj)
#伪目标
.PHONY:clean ALL
添加编译参数:
可以将编译参数添加到 myArgs里面,然后使$(myArgs)将其中的参数取出;
ALL:main
src=$(wildcard ./*.c) #add.c sub.c test.c
obj=$(patsubst %.c,%.o,$(src)) #add.o sub.o test.o
myArgs=-Wall -g
main:$(obj)
gcc $^ -o $@ -I. $(myArgs)
%.o:%.c
gcc -c $< -o $@ $(myArgs)
clean:
-rm -rf $(obj)
#伪目标
.PHONY:clean ALL
库变量
是makefile库中自己定义的变量,有的变量有默认值,由的变量没有默认值
CC:默认是 CC =cc c语言编译工具
CPPFLAGS: C++默认参数
CFLAGS: C默认参数
LDFLAGS: 动态链接参数
同时编译同一个目录下的多个文件实例:
daniel@daniel-Vostro-5471:~/文档/OS/test/make_test/make5$ tree
.
├── add.c
├── makefile
├── mul.c
└── sub.c
0 directories, 4 files
要求每个 %.c文件都编译成为一个单独的 %.out的可执行文件
makefile的编写如下:
src=$(wildcard *.c)
# obj=$(patsubst %.c,%.o,$(src))
out=$(patsubst %.c,%.out,$(src))
ALL: $(out)
$(out):%.out:%.c
gcc $< -o $@
clean:
-rm -rf $(out)
.PHONY:ALL clean
5.6 标准示例
文件的目录如下:
daniel@daniel-Vostro-5471:~/文档/OS/test/make_test/make4$ tree
.
├── inc
│ └── cal.h
├── main
├── makefile
├── obj
└── src
├── add.c
├── mul.c
├── sub.c
└── test.c
要求:将编译文件放到obj目录中
makefile文件编写如下:
#编译总目标文件
ALL:main
#设置变量
src=$(wildcard ./src/*.c) #./src/ add.c sub.c mul.c test.c
# # %是等价代换的意思,将第一个变量中取出的%的值,放到第二个%上
obj=$(patsubst ./src/%.c,./obj/%.o,$(src))#./obj/ add.o sub.o mul.o test.o
# echo:
# echo $(src) $(obj)
#头文件位置
inc_path=./inc
#设置编译参数
myArgs=-Wall -g
#编译
main:$(obj)
gcc $^ -o $@ $(myArgs)
# 将obj目录更换为src目录
# 头文件展开在预处理阶段运行,所以17行不需要添加头文件
$(obj):./obj/%.o:./src/%.c
gcc -c $< -o $@ $(myArgs) -I $(inc_path)
clean:
-rm -rf $(obj)
.PHONY: ALL clean
不使用makefile为命令,也可以运行,比如给文件其名字 m6(mv makefile m6)
运行方法是:make -f m6
很多文件的命名方式是:****.mk,可能就是makefile文件
make 可以进行多线程编译,make -j4或者make -j8