前言
1)Make简介
- 工程管理器,顾名思义,是指管理较多的文件
- Make工程管理器也就是个“自动编译管理器”,这里的“自动”是指它能够根据文件时间戳自动发现更新过的文件而减少编译的工作量,同时,它通过读入Makefile文件的内容来执行大量的编译工作
- Make将只编译改动的代码文件,而不用完全编译。
2)Makefile基本结构
Makefile是Make读入的唯一配置文件
- 由make工具创建的目标体(target),通常是目标文件或可执行文件
- 要创建的目标体所依赖的文件(dependency_file)
- 创建每个目标体时需要运行的命令(command)
- 注意:命令行前面必须是一个”TAB键”,否则编译错误为:*** missing separator. Stop.
Makefile格式
target : dependency_files
TAB command
如果“依赖文件”比“目标文件”更加新,那么执行“命令”来重新生成“目标文件”。
命令被执行的 2 个条件:依赖文件比目标文件新,或是 目标文件还没生成。
例子
hello.o : hello.c hello.h
gcc –c hello.c –o hello.o
一个复杂一些的例子
sunq:kang.o yul.o
gcc kang.o yul.o -o sunq
kang.o : kang.c kang.h
gcc –Wall –O -g –c kang.c -o kang.o
yul.o : yul.c
gcc - Wall –O -g –c yul.c -o yul.o
注释:-Wall:表示允许发出gcc所有有用的报警信息.
-c:只是编译不链接,生成目标文件”.o”
-o file:表示把输出文件输出到file里
关于更多的用man工具
1 变量
$符号表示取变量的值,当变量名多于一个字符时,使用"( )"
$符的其他用法
$* 不包含扩展名的目标文件名称
$+ 所有的依赖文件,以空格分开,并以出现的先后为序,可能 包含重复的依赖文件
$< 第一个依赖文件的名称(重要,常见)
$? 所有时间戳比目标文件晚的的依赖文件,并以空格分开
$@ 目标文件的完整名称(重要,常见)
$^ 所有不重复的目标依赖文件,以空格分开(重要,常见)
$% 如果目标是归档成员,则该变量表示目标的归档成员名称
OBJS=f1.o f2.o
OBJS+=main.o
CFLAGS=-c -Wall
test:$(OBJS)
gcc $(OBJS) -o test
f2.o:$<
gcc -c -Wall f2.c -o $@
f1.o:f1.c
gcc $(CFLAGS) f1.c -o $@
main.o:main.c
gcc -c -Wall main.c -o main.o
.PHONY:clean
clean:
rm *.o test
2. 变量赋值
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、"+="和平时写代码的理解是一样的,表示将符号后面的值添加到前面的变量上
3. 常用函数介绍
1、$(foreach var,list,text)
对 list 中的每一个元素,取出来赋给 var,然后把 var 改为 text 所描述的形式。
例如
objs := a.o b.o
dep_files := $(foreach f, $(objs), .$(f).d) // 最终 dep_files := .a.o.d .b.o.d
2、$(wildcard pattern)
pattern 所列出的文件是否存在,把存在的文件都列出来。
src_files := $( wildcard *.c) // 最终 src_files 中列出了当前目录下的所有.c 文件
3、$(patsubst %.c, %.o, $(SRC)
这个函数有三个参数,意思是取出SRC中的所有值,然后将.c 替换为.o 最后赋值给OBJ变量
OBJ = $(patsubst %.c, %.o, $(SRC))
例如
这里先将所有.c 文件编译为 .o 文件,这样后面更改某个 .c 文件时,其他的 .c 文件将不在编译,而只是编译有更改的 .c 文件,可以大大提高大项目中的编译速度。
//方法1:
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))
//这是一个伪目标,表示默认目标是 hello.out。当你在命令行中只运行 make 而不指定任何目标时,
//Make 会尝试生成 hello.out
ALL: hello.out
hello.out: $(OBJ)
gcc $(OBJ) -o hello.out
/*gcc -c $< -o $@ 命令表示使用 GNU 编译器将源文件($<)编译为目标文件($@)。$<
是第一个依赖文件的名字,即源文件 .c,$@ 是目标文件的名字,即 .o 文件。*/
%.o: %.c
gcc -c $< -o $@
objs := main.o sub.o
test : $(objs)
gcc -o test $^
# 需要判断是否存在依赖文件
# .main.o.d .sub.o.d
dep_files := $(foreach f, $(objs), .$(f).d) //将所有objs文件改成.objs.d
dep_files := $(wildcard $(dep_files)) //找出.main.o.d .sub.o.d并赋值给dep_files
# 把依赖文件包含进来
ifneq ($(dep_files),)
include $(dep_files)
endif
/*这条规则表示用 gcc 编译器将 .c 文件编译为 .o 文件,同时生成一个依赖文件(.d 文件),这个
.d 文件将列出编译所需的所有依赖关系。
*-Wp,-MD,.$@.d:这是用于生成依赖文件的选项
*-Wp:告诉编译器传递选项给预处理器。
*-MD:生成一个依赖文件。这个文件会列出编译这个目标文件所需要的所有源文件。生成的文件名是
.$@.d。
*.$@.d:依赖文件的名字,其中 $@ 表示目标文件名(即 .o 文件名)。例如,如果目标文件是
foo.o,那么依赖文件将是 .foo.o.d
*-c:表示编译源文件而不进行链接
*-o $@:指定输出文件的名字为目标文件(即 .o 文件)。
*$<:表示第一个依赖文件,即源文件(即 .c 文件)。
*/
%.o : %.c
gcc -Wp,-MD,.$@.d -c -o $@ $<
clean:
rm *.o test -f
distclean:
rm $(dep_files) *.o test -f
4. 伪目标 .PHONY
伪目标只是一个标签,clean是个伪目标没有依赖文件,只有用make来调用时才会执行。如果前目录下恰好有名为“clean”的文件,那么执行“make clean”时它就不会执行那些删除命令。这时我们需要把“clean”这个目标,设置为“假想目标”,这样可以确保执行“make clean”时那些删除命令肯定可以得到执行
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))
ALL: hello.out
hello.out: $(OBJ)
gcc $< -o $@
$(OBJ): $(SRC)
gcc -c $< -o $@
clean:
rm -rf $(OBJ) hello.out
.PHONY: clean ALL