Loading

GNU Make总结

整理自文档 <<跟我一起写Makefile>>

核心规则
target ... : target所依赖的target ...
    生成target的命令

其中target可以是object文件,也可以是一个可执行文件,亦可以是一个标签。

本质上讲,make就是一个以目标为终点的系统,而目标与目标之间通过依赖关系组成一个关系链。

make工作机制

在默认的方式下,也就是我们只输入 make 命令。那么,

  1. make 会在当前目录下找名字叫 Makefile或makefile的文件。

  2. 如果找到,它会找文件中的第一个目标文件(target) 你也可以指定具体的目标。

  3. 如果目标文件不存在,或是目标所依赖的文件的修改时间要比目标文件新,那么,他就会执行后面所定义的命令来生成依赖的目标文件。

  4. 如果目标所依赖的文件也不存在,那么 make 会在当前文件中找依赖文件的依赖性,如果找到则再根据那一个规则生成依赖文件。(这有点像一个堆栈的过程)

这就是整个 make 的依赖性, make 会一层又一层地去找文件的依赖关系,直到最终编译出第一个目
标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么 make 就会直接退出,并
报错,而对于所定义的命令的错误,或是编译不成功, make 根本不理。 make 只管文件的依赖性,即,如
果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作。

工作步骤

  1. 读入所有的makefile
  2. 读入被 include 的其它 Makefile
  3. 初始化文件中的变量。
  4. 推导隐晦规则,并分析所有规则。
  5. 为所有的目标文件创建依赖关系链。
  6. 根据依赖关系,决定哪些目标要重新生成。
  7. 执行生成命令。
make变量的定义与引用

定义:
objects = main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o

引用:
$(objects)

make自动推导

make可以根据文件名后缀推导出生成目标的规则。比如
main.o : defs.h 并不需要指定main.o 是根据main.o 是根据main.c, defs.h生成main.o

伪目标
.PHONY : clean
clean :
-rm edit $(objects)

其中, .PHONY 为伪目标,也就是它并不会产生输出。 -rm表示,如果删除文件中出错,比如某些文件不存在,忽略它,继续执行。

makefile 的组成成分
  • 显示规则
    明确声明如何生成一个目标或多个目标。
  • 隐晦规则
    自动推导生成目标
  • 变量定义
  • 文件指示
    在文件中引用其他的makefile文件,
    include
    -include 无论filename文件是否存在,都不报错。
  • 注释
    注释符号为 #

注意: 在 Makefile 中的命令,必须要以 Tab 键开始

命令行参数
  • -I 或 --include-dir
    make 就会在这个参数所指定的目录寻找导入的文件。如果目录 /include
    (一般是:/usr/local/bin 或 /usr/include )存在的话, make 也会去找。
规则

规则包含两个部分,一个是依赖关系,一个是生成目标的方法。
make 支持三个通配符:

  • , ? 和 ~ 。这是和 Unix 的 B-Shell 是相同的。
多目标

Makefile 的规则中的目标可以不止一个,其支持多目标,有可能我们的多个目标同时依赖于一个文
件,并且其生成的命令大体类似。于是我们就能把其合并起来。当然,多个目标的生成规则的执行命令不
是同一个,这可能会给我们带来麻烦,不过好在我们可以使用一个自动化变量 $@ (关于自动化变量,将
在后面讲述),这个变量表示着目前规则中所有的目标的集合,这样说可能很抽象,还是看一个例子吧。

bigoutput littleoutput : text.g
	generate text.g -$(subst output,,$@) > $@
	
上面的规则等价与
	
bigoutput : text.g
	generate text.g -big > bigoutput
littleoutput : text.g
	generate text.g -little > littleoutput

其中, -$(subst output,,$@) 中的 $ 表示执行一个 Makefile 的函数,函数名为 subst,后面的为
参数。关于函数,将在后面讲述。这里的这个函数是替换字符串的意思, $@ 表示目标的集合,就像一个
数组, $@ 依次取出目标,并执于命令。

静态模式
自动生成依赖
条件判断
libs_for_gcc = -lgnu
normal_libs =

foo: $(objects)
ifeq ($(CC),gcc)
	$(CC) -o foo $(objects) $(libs_for_gcc)
else
	$(CC) -o foo $(objects) $(normal_libs)
endif

其他语法ifneq(参数1,参数2) ,ifndef(参数1, 参数2), ifdef(参数1, 参数2)

make的运行
  • 退出码 0 代表执行成功,1 代表 make遇到任何错误, 2 如果你使用了 make 的“-q”选项,并且 make 使得一些目标不需要更新,那么返回 2。
  • 指定 makefile文件 make -f filename
其他
sources = foo.c bar.c
ifneq ( $(MAKECMDGOALS),clean)
  include $(sources:.c=.d)
endif

有一个 make 的环境变量叫 MAKECMDGOALS ,这个变量中会存放你所指定的终极目标的列表,如果
在命令行上,你没有指定目标,那么,这个变量是空值。

默认规则

• all: 这个伪目标是所有目标的目标,其功能一般是编译所有的目标。
• clean: 这个伪目标功能是删除所有被 make 创建的文件。
• install: 这个伪目标功能是安装已编译好的程序,其实就是把目标执行文件拷贝到指定的目标中去。
• print: 这个伪目标的功能是例出改变过的源文件。
• tar: 这个伪目标功能是把源程序打包备份。也就是一个 tar 文件。
• dist: 这个伪目标功能是创建一个压缩文件,一般是把 tar 文件压成 Z 文件。或是 gz 文件。
• TAGS: 这个伪目标功能是更新所有的目标,以备完整地重编译使用。
• check 和 test: 这两个伪目标一般用来测试 makefile 的流程。

检查规则

有时候,我们不想让我们的 makefile 中的规则执行起来,我们只想检查一下我们的命令,或是执行
的序列。于是我们可以使用 make 命令的下述参数:
-n, --just-print, --dry-run, --recon 不执行参数,这些参数只是打印命令,不管目标是否更新,把
规则和连带规则下的命令打印出来,但不执行,这些参数对于我们调试 makefile 很有用处。
-t, --touch 这个参数的意思就是把目标文件的时间更新,但不更改目标文件。也就是说, make 假装编
译目标,但不是真正的编译目标,只是把目标变成已编译过的状态。
-q, --question 这个参数的行为是找目标的意思,也就是说,如果目标存在,那么其什么也不会输出,
当然也不会执行编译,如果目标不存在,其会打印出一条出错信息。
-W , --what-if=, --assume-new=, --new-file= 这个参数需要指定一个
文件。一般是是源文件(或依赖文件), Make 会根据规则推导来运行依赖于这个文件的命令,一般
来说,可以和“-n”参数一同使用,来查看这个依赖文件所发生的规则命令。
另外一个很有意思的用法是结合 -p 和 -v 来输出 makefile 被执行时的信息(这个将在后面讲述)。

make 参数

下面列举了所有 GNU make 3.80 版的参数定义。其它版本和产商的 make 大同小异,不过其它产商
的 make 的具体参数还是请参考各自的产品文档。
-b, -m 这两个参数的作用是忽略和其它版本 make 的兼容性。
-B, --always-make 认为所有的目标都需要更新(重编译)。
-C

, --directory= 指定读取 makefile 的目录。如果有多个“-C”参数, make 的解释是
后面的路径以前面的作为相对路径,并以最后的目录作为被指定目录。如:“make -C ~hchen/test
-C prog”等价于“make -C ~hchen/test/prog”。
-debug[=] 输出 make 的调试信息。它有几种不同的级别可供选择,如果没有参数,那就是
输出最简单的调试信息。下面是 的取值:
• a: 也就是 all,输出所有的调试信息。(会非常的多)
• b: 也就是 basic,只输出简单的调试信息。即输出不需要重编译的目标。
• v: 也就是 verbose,在 b 选项的级别之上。输出的信息包括哪个 makefile 被解析,不需要被
重编译的依赖文件(或是依赖目标)等。
• i: 也就是 implicit,输出所以的隐含规则。
• j: 也就是 jobs,输出执行规则中命令的详细信息,如命令的 PID、返回码等。
• m: 也就是 makefile,输出 make 读取 makefile,更新 makefile,执行 makefile 的信息。
-d 相当于“–debug=a”。
-e, --environment-overrides 指明环境变量的值覆盖 makefile 中定义的变量的值。
-f=, --file=, --makefile= 指定需要执行的 makefile。
-h, --help 显示帮助信息。
-i , --ignore-errors 在执行时忽略所有的错误。
-I , --include-dir= 指定一个被包含 makefile 的搜索目标。可以使用多个“-I”参数来
指定多个目录。
-j [], --jobs[=] 指同时运行命令的个数。如果没有这个参数, make 运行命
令时能运行多少就运行多少。如果有一个以上的“-j”参数,那么仅最后一个“-j”才是有效的。(注
意这个参数在 MS-DOS 中是无用的)
-k, --keep-going 出错也不停止运行。如果生成一个目标失败了,那么依赖于其上的目标就不会被执行
了。
-l , --load-average[=], -max-load[=] 指定 make 运行命令的负载。
-n, --just-print, --dry-run, --recon 仅输出执行过程中的命令序列,但并不执行。
-o , --old-file=, --assume-old= 不重新生成的指定的 ,即使这个目标
的依赖文件新于它。
-p, --print-data-base 输出 makefile 中的所有数据,包括所有的规则和变量。这个参数会让一个简单
的 makefile 都会输出一堆信息。如果你只是想输出信息而不想执行 makefile,你可以使用“make
-qp”命令。如果你想查看执行 makefile 前的预设变量和规则,你可以使用“make –p –f /dev/null”。
这个参数输出的信息会包含着你的 makefile 文件的文件名和行号,所以,用这个参数来调试你的
makefile 会是很有用的,特别是当你的环境变量很复杂的时候。
-q, --question 不运行命令,也不输出。仅仅是检查所指定的目标是否需要更新。如果是 0 则说明要更
新,如果是 2 则说明有错误发生。
-r, --no-builtin-rules 禁止 make 使用任何隐含规则。
-R, --no-builtin-variabes 禁止 make 使用任何作用于变量上的隐含规则。
-s, --silent, --quiet 在命令运行时不输出命令的输出。
-S, --no-keep-going, --stop 取消“-k”选项的作用。因为有些时候, make 的选项是从环境变量
“MAKEFLAGS”中继承下来的。所以你可以在命令行中使用这个参数来让环境变量中的“-k”选
项失效。
-t, --touch 相当于 UNIX 的 touch 命令,只是把目标的修改日期变成最新的,也就是阻止生成目标的
命令运行。
-v, --version 输出 make 程序的版本、版权等关于 make 的信息。
-w, --print-directory 输出运行 makefile 之前和之后的信息。这个参数对于跟踪嵌套式调用 make 时
很有用。
--no-print-directory 禁止“-w”选项。

-W , --what-if=, --new-file=, --assume-file= 假定目标 ; 需
要更新,如果和“-n”选项使用,那么这个参数会输出该目标更新时的运行动作。如果没有“-n”那
么就像运行 UNIX 的“touch”命令一样,使得 ; 的修改时间为当前时间。
--warn-undefined-variables 只要 make 发现有未定义的变量,那么就输出警告信息。

posted @ 2021-09-28 17:16  Test002  阅读(17)  评论(0)    收藏  举报