Makefile
make工具:它可以帮助我们找出项目里面修改变更过的文件,并根据依赖关系,找出受修改影响的其他相关文件,然后对这些文件按照规则进行单独的编译,这样一来,就能避免重新编译项目的所有的文件。
Makefile文件:上面提到的规则、依赖关系主要是定义在这个Makefile文件中的,我们在其中合理地定义好文件的依赖关系之后,make工具就能精准地进行编译工作。
-
makefile简介
makefile中最核心的内容:prerequisites中如果有一个以上的文件比target文件要新的话,recipe所定义的命令就会被执行。
- make是如何工作的:
- make 会在当前目录下找名字叫“Makefile”或“makefile”的文件。
- 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文
件,并把这个文件作为最终的目标文件。 - 如果 edit 文件不存在,或是 edit 所依赖的后面的 .o 文件的文件修改时间要比 edit 这个文件新,
那么,他就会执行后面所定义的命令来生成 edit 这个文件。 - 如果 edit 所依赖的 .o 文件也不存在,那么 make 会在当前文件中找目标为 .o 文件的依赖性,如
果找到则再根据那一个规则生成 .o 文件。(这有点像一个堆栈的过程) - 当然,你的 C 文件和头文件是存在的啦,于是 make 会生成 .o 文件,然后再用 .o 文件生成 make
的终极任务,也就是可执行文件 edit 了。
makefile中主要包含:显式规则、隐式规则、变量定义、指令和注释
-
GNU的make工作执行步骤
阶段一:
- 读入所有的 Makefile。
- 读入被 include 的其它 Makefile。
- 初始化文件中的变量。
- 推导隐式规则,并分析所有规则。
- 为所有的目标文件创建依赖关系链。
阶段二:
- 根据依赖关系,决定哪些目标要重新生成。
- 执行生成命令。
- make是如何工作的:
-
书写规则
规则包含两个部分:依赖关系、生成目标的方法
Makefile 中只应该有一个最终目标,其它的目标都是被这个目标所连带出来的,要让 make 知道你的最终目标(第一条规则中的目标)是什么。
规则的语法:
通配符:*(匹配任意长度字符串)、?(匹配一个字符)、~(用户主目录或其他用户主目录)
文件搜寻:写入到VPATH特殊变量中,以:作为路径分隔
VPATH = src:../headers
或者vpath关键字:指定不同的文件在不同的搜索目录中
其中
需要包含%字符(匹配0或者若干个字符) 伪目标.PHONY
自动化变量$@:表示目前规则中所有的目标的集合
静态模式:
objects = foo.o bar.o all: $(objects) $(objects): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@ # $<表示第一个依赖文件,$@表示目标集,$^表示当前规则的所有依赖文件 ##########等价于########### foo.o : foo.c $(CC) -c $(CFLAGS) foo.c -o foo.o bar.o : bar.c $(CC) -c $(CFLAGS) bar.c -o bar.o
自动生成依赖性,-M/-MM参数:
.d 文件中就存放对应 .c 文件的依赖关系。
-
书写命令
make参数:
-n/- -just-print,显示命令,但不执行
-s/- -silent/- -quiet,全面禁止命令的显示要让上一条命令的结果应用在下一条命令时,应该使用分号分隔这两条命令。
定义命令包:如果 Makefile 中出现一些相同命令序列,那么我们可以为这些相同的命令序列定义一个变量。定义
这种命令序列的语法以 define 开始,以 endef 结束, -
使用变量
变量可以使用在许多地方,如规则中的“目标”、“依赖”、“命令”以及新的变量中。
=、:=、?=、+=的含义
替换变量中的共有的部分,其格式是 $(var:a=b) 或是 ${var:a=b}
把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。override指令:确保在makefile中对变量的赋值不会被命令行变量覆盖
命令包:使用define关键字设置多个变量
目标变量(局部变量,只在这条规则以及连带规则中有效)
# CFLAGS = -g只在这部分有效 prog : CFLAGS = -g prog : prog.o foo.o bar.o $(CC) $(CFLAGS) prog.o foo.o bar.o # 在这部分无效,$(CFLAGS)是其他变量 prog.o : prog.c $(CC) $(CFLAGS) prog.c
-
条件判断
表示条件关键字,如 ifeq 。这个关键字有四个 ifeq、ifneq、ifdef、ifndef
make 是在读取 Makefile 时就计算条件表达式的值,并根据条件表达式的值来选择语句,所以,你最好不要把自动化变量(如 $@等)放入条件表达式中,因为自动化变量是在运行时才有的。
-
函数
# 函数调用 $(<function> <arguments>) ${<function> <arguments>}
字符串处理函数subst
功能:把字串
中的 字符串替换成 $(subst <from>,<to>,<text>) # 示例 # 把feet on the street中的ee替换成EE # 返回结果是fEEt on the strEEt $(subst ee,EE,feet on the street)
模式字符串替换函数patsubst
功能:查找
中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式 ,如果匹配的话,则以 替换。这里, 可以包括通配符 % ,表示任意长度的字串。如果 中也包含 % ,那么, 中的这个 % 将是
中的那个 % 所代表的字串。(可以用 \ 来转义,以 % 来表示真实含义的 % 字符) $(patsubst <pattern>,<replacement>,<text>) # 示例, # 把字串 x.c.c bar.c 符合模式 %.c 的单词替换成 %.o # 返回结果是 x.c.o bar.o $(patsubst %.c,%.o,x.c.c bar.c)
去空格函数strip
功能:去掉
字串中开头和结尾的空字符。 $(strip <string>) # 示例 # 把字串去掉开头和结尾的空格 # 结果是 a b c $(strip a b c )
查找字符串函数findstring
功能:在字串
中查找 字串。 -
make的运行
-f/--file 参数,让make执行指定的特殊名字的Makefile
MAKECMDGOALS环境变量:会存放你所指定的终极目标的列表,如果在命令行上,你没有指定目标,那么,这个变量是空值
书写makefile的目标规则:
- all: 这个伪目标是所有目标的目标,其功能一般是编译所有的目标。
- clean: 这个伪目标功能是删除所有被 make 创建的文件。
- install: 这个伪目标功能是安装已编译好的程序,其实就是把目标执行文件拷贝到指定的目标中去。
- print: 这个伪目标的功能是例出改变过的源文件。
- tar: 这个伪目标功能是把源程序打包备份。也就是一个 tar 文件。
- dist: 这个伪目标功能是创建一个压缩文件,一般是把 tar 文件压成 Z 文件。或是 gz 文件。
- TAGS: 这个伪目标功能是更新所有的目标,以备完整地重编译使用。
- check 和 test: 这两个伪目标一般用来测试 makefile 的流程。
-
隐含规则
隐含规则是 make 事先约定好的一些东西
-r/--no-builtin-rules参数来取消所有的预设置的隐含规则
-R/--no–builtin-variables 参数来取消你所定义的变量对隐含规则的作用
产生最终目标过程中,所产生的中间目标文件会被以 rm -f 删除。
定义模式规则:
自动化变量:
- $@ : 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,$@ 就是匹配于目标中模式定义的集合。
- $% : 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是 foo.a(bar.o),那么,$% 就是 bar.o ,$@ 就是 foo.a 。如果目标不是函数库文件(Unix 下是 .a ,Windows下是 .lib ),那么,其值为空。
- $< : 依赖目标中的第一个目标名字。如果依赖目标是以模式(即 % )定义的,那么 $< 将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
- $? : 所有比目标新的依赖目标的集合。以空格分隔。
- $^ : 所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那么这个变量会去除重复的依赖目标,只保留一份。
- $+ : 这个变量很像 $^ ,也是所有依赖目标的集合。只是它不去除重复的依赖目标。
- $* : 这个变量表示目标模式中 % 及其之前的部分。如果目标是 dir/a.foo.b ,并且目标的模式是a.%.b ,那么,$* 的值就是 dir/foo 。这个变量对于构造有关联的文件名是比较有效。如果目标中没有模式的定义,那么 $* 也就不能被推导出,但是,如果目标文件的后缀是 make 所识别的,那么 $* 就是除了后缀的那一部分。例如:如果目标是 foo.c ,因为 .c 是 make 所能识别的后缀名,所以,$* 的值就是 foo 。这个特性是 GNU make 的,很有可能不兼容于其它版本的 make,所以,你应该尽量避免使用 $* ,除非是在隐含规则或是静态模式中。如果目标中的后缀是 make 所不能识别的,那么 $* 就是空值。
这七个自动化变量还可以取得文件的目录名或是在当前目录下的符合模式的文件名,只需要搭配上 D 或 F 字样。这是 GNU make 中老版本的特性,在新版本中,我们使用函数 dir或 notdir 就可以做到了。D 的含义就是 Directory,就是目录,F 的含义就是 File,就是文件。
-
使用 make 更新函数库文件
函数库文件也就是对 Object 文件(程序编译的中间文件)的打包文件。在 Unix 下,一般是由命令ar来完成打包工作。
# 一个函数库文件由多个文件组成。 # 你可以用如下格式指定函数库文件及其组成: archive(member)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通