目录
1.为什么需要makefile
主要是项目比较大时,方便多人一起开发,管理和编译工程。
2.makefile基本的格式
target: prerequisites
cmd
3.makefile 工作流程
-step1:读入所有的makefile。
-step2:读入被include的其它makefile。
-step3:初始化文件中的变量。
-step4:推导隐晦规则,并分析所有规则。
-step5:为所有的目标文件创建依赖关系链。
-step6:根据依赖关系,决定哪些目标要重新生成。
-step7:执行生成命令
4.基本写法案例
有一个工程,三个 .c文件,分别为 main.c add1.h add1.c add2.h add2.c ,需要编译生成一个 add.out 的可执行文件
按照格式 编写如下:
#格式
#target:prerequisites
# cmd
#
add.out : main.o add1.o add2.o
gcc main.o add1.o add2.o -o add.out
main.o : main.c
gcc main.c -c -Wall -g -o main.o
add1.o : add1.c
gcc add1.c -c -Wall -g -o add1.o
add2.o : add2.c
gcc add2.c -c -Wall -g -o add2.o编写makefile后,只有生成的文件被修改后,才会需要重新编译。
但是我们经常想要重新编译,这个时候,我们会在makefile中,增加一条命令clean命令,见如下代码。
#格式
#target:prerequisites
# cmd
#
add.out : main.o add1.o add2.o
gcc main.o add1.o add2.o -o add.out
main.o : main.c
gcc main.c -c -Wall -g -o main.o
add1.o : add1.c
gcc add1.c -c -Wall -g -o add1.o
add2.o : add2.c
gcc add2.c -c -Wall -g -o add2.o
clean:
rm *.o add.out -rf5.升级写法案例
基本写法只是为了更好让我们理解编译过程,但是文件之间的关联关系需要自己去区分,当工程足够大的时候,对我们来说管理太复杂了,因此需要进一步说明升级写法。
同样基于上面案例说明
高级写法的特点之一,就是用变量以及通配符匹配的一些方式,来省略我们自己去手动区分管理文件关联关系。
如上面 我们定义变量 OBJS = main.o add1.o add2.o ,取变量的值使用符号 $ ,其中注意下 变量RM 相关于 rm -f
变量替换方式案例
#格式
#target:prerequisites
# cmd
#
OBJS = main.o add1.o add2.o
CC = gcc
CFLAGS += -c -Wall -g
add.out : $(OBJS)
$(CC) $(OBJS) -o add.out
main.o : main.c
$(CC) main.c $(CFLAGS) -o main.o
add1.o : add1.c
$(CC) add1.c $(CFLAGS) -o add1.o
add2.o : add2.c
$(CC) add2.c $(CFLAGS) -o add2.o
clean:
$(RM) *.o add.out -r上面的案例我们看着已经有些简洁了,但是依赖关系还是没给我们省掉
再来说明几个 符号,其中的 $^ 表示继承上一句的文件 $@表示继承上一句的目标
add.out : $(OBJS)
$(CC) $(OBJS) -o add.out
--------
add.out : $(OBJS)
$(CC) $^ -o $@
--------
两者是一个效果下面进一步说明,在我们编译 生成.o 时,我们看到语句 其实 都是xx.o: xx.c ,这个时候,我们就可以使用通配符 % 来替代,结合可以得到如下的makefile。
#格式
#target:prerequisites
# cmd
#
OBJS = main.o add1.o add2.o
CC = gcc
CFLAGS += -c -Wall -g
add.out : $(OBJS)
$(CC) $(OBJS) -o $@
%.o : %.c
$(CC) $^ $(CFLAGS) -o $@
clean:
$(RM) *.o add.out -r6. 常用工程写法案例
我们经常使用到的工程都是有多个 文件夹 很多文件,并且很多文件夹的地方包含 makefile,那么针对这类大型工程,makefile如何写呢?
一个makefile,包含其他makefile,基本语法 主要用于包含 其他.mk文件
include <filename>路径包含
VPATH = src:../headers
----
路径用:隔开案例说明
文件目录如下

基本代码



其他test 代码类似,仅供测试
主makefile
# this is the main makefile
TGT = jy.out
SUB_DIR = main test1 test2 test3
export SUB_TGT = built-in.o
export TOP_DIR = $(shell pwd)
export HEAD_DIR += $(TOP_DIR)/test1 $(TOP_DIR)/test2 $(TOP_DIR)/test3 $(TOP_DIR)/test3/test4
export CC = gcc
export CFGLAGS += -I $(HEAD_DIR) -Wall
export LD =ld
export LDFLAGS =
$(TGT): $(SUB_DIR)
$(CC) $(CFLAGS) $(^:=/$(SUB_TGT))
$(SUB_DIR):
make -C $@
clean:
-rm -vf $(TGT)
for dir in $(SUB_DIR);do \
make -C $$dir clean;\
done
.PHONY:clean &(SUB_DIR)
子makefile
# this is the sub makefile
SRCS = main.c
SUB_DIR =
$(SUB_TGT): $(SRCS:.c=.o) $(SUB_DIR)
$(LD) $(LDFLAGS) $(SRCS:.c=.o) $(SUB_DIR:=/$(SUB_TGT)) -r -o $@
%o: %c
$(CC) $(CFLAGS) $< -c
%.d: %.c
$(CC) $(CFLAGS) $< -MM > $@
sinclude $(SRCS:.c=.d)
$(SUB_DIR):
make -C $@
clean:
-rm -vf $(SUB_TGT) $(SRCS:.c=.o) $(SRCS:.c=.d)
for dir in $(SUB_DIR);do \
make -C $$dir clean;\
done
7.基础语法备查
变量定义
如
OBJ = a.o b.o
OBJ+= c.o # 表示追加 相当于 OBJ = a.o b.o c.o
OBJ:= d.o # 表示替换 相当于 OBJ = d.o
OBJ?= e.o # 表示未定义才重新定义 相当于 OBJ = d.o
变量取值 $(OBJ)
通配符 * ? [....]
如 用 a.o b.o c.o 通配符 替换 为 *.o
如 用 [abcd] --> [a-d]
伪目标
伪目标是一个标签,而不是一个文件,常用语 clean处,如
.PHONY clean:
clean :
rm -rf *.o &(TARGET)
自动取目标
使用 $@
自动取依赖项
使用$^ 或$>
字符串处理函数
subst 字符替换
基本语法 $(subst CC,cc,get cc to CC) 出来的字符 是 get cc to cc 表示把CC 替换成 cc
patsubst 模式替换
基本语法 $(patsubst %.c,%.o,bar.c) 出来的字符 是bar.o 表示把所有.c替换成 o
findstring 查找字符串
基本语法 $(findstring a,a b c) 出来的字符 是a
基本语法 $(findstring a,b c) 出来的字符 是空字符
filter 模式过滤字符串
基本语法 $(filter %c %o,a.c b.o c.s) 出来的字符 是a.c b.o
filter-out 模式反过滤字符串
基本语法 $(filter-out %c %o,a.c b.o c.s) 出来的字符 是c.s
sort 按字符排序
基本语法 $(sort b a c d) 出来的字符 是a b c d
word 取单词数
基本语法 $(word 2, i am a boy) 出来的字符 是am
words 统计单词个数
基本语法 $(words i am a boy) 出来的值 是 4
文件名操作
dir 取目录
基本语法 $(dir usr/src/test/makefile hello.c) 出来的字符 是 usr/src/test 和 ./
notdir 取文件名
基本语法 $(notdir usr/src/test/makefile hello.c) 出来的字符 是 makefile 和 hello.c
suffix 取后缀
基本语法 $(suffix usr/src/test/makefile hello.c) 出来的字符 是 .c
basename取前缀
基本语法 $(basename usr/src/test/makefile hello.c) 出来的字符 是 makefile hello
addsuffix 加后缀
基本语法 $(addsuffix .c ,foo1 foo) 出来的字符 是foo1.c foo.c
addprefix 加前缀
基本语法 $(addprefix /usr/src/ ,foo1.c foo.c) 出来的字符 是 /usr/src/foo1.c /usr/src/foo.c
循环函数
foreach
基本语法举例
names := a b c d
files := $(foreach n,names,$(n).c)
得到$(files) = a.c b.c c.c d.c
条件判断
ifeq ifneq ifdef ifndef
makefile中 一些默认变量
| 变量 | 含义 |
| AR | 函数库打包 同ar |
| AS | 汇编语言编译程序, 同 as |
| CC | C语言编译,同 cc |
| CXX | C++语言编译,同 g++ |
| CO | 从RCS文件中扩展文件程序,同co |
| CPP | C程序的预处理器 同 $(CC) -E |
| GET | 从SCCS文件中扩展文件的程序, 同 get |
| YACC | yacc文法分析器,同 yacc |
| MAKEINFO | 转换texinfo文件.texi到infor文件程序,同 makeinfo |
| RM | 删除文件命令,同 rm -f |
makefile中 一些自动变量
| $@ | 匹配目标中模式定义的集合 |
| $% | 目标是函数库文件是,表示规则中的目标成员名,如果不是函数库文件,则值为空 |
| $< | 依赖目标中的第一个目标名字,如果以来目标是以模式定义,则表示符合模式的一系列的文件集 |
| $^ | 所有的依赖目标的集合 |
| $? | 所有比目标新的依赖目标的集合 |
| $+ | 同$^ |
| $* | 目标模式中 % 及其之前的部分 |
| $(@D) | 针对目录部分 |
| $(@F) | 针对文件部分 |
| 其他 跟D 和F的类似 |
make 参数选项
| -C | dir读入指定目录下的makefile |
| -f | file读入当前目录下的file文件作为makefile |
| -i | 忽略所有命令执行错误 |
| -I | dir制定被包含的makefile所在目录 |
| -n | 只打印要执行的命令,但是不执行这些命令 |
| -p | 显示make变量数据库的隐含规则 |
| -s | 在执行命令时不显示命令 |
| -w | 如果执行make在执行过程中改变目录,打印当前目录名 |