Makefile之运行参数
下一篇:练习10 Makefile之隐式规则,上一篇:练习8 Makefile之使用函数,目录|首页
温故知新
- 练习1 理解编译的基本过程
- 练习2 创建基本的Makefile
- 练习3 Makefile之简单介绍
- 练习4 Makefile之显式规则
- 练习5 Makefile之配方规则
- 练习6 Makefile之使用变量
- 练习7 Makefile之条件部分
- 练习8 Makefile之使用函数
make
工具的用法可以有多种方式,主要取决于你在运行时是否提供参数。最简单的用法是,如果你在没有提供任何参数的情况下运行 make
,它会重新编译所有过期的文件。通常,Makefile 是这样编写的,以便在不带任何参数的情况下运行 make
就能实现这个目标。
但你可能只想更新其中的一些文件;可能希望使用不同的编译器或不同的编译选项;可能只想查找哪些文件已经过期而不进行实际更改。
通过在运行 make
时提供参数,你可以执行这些操作以及其他许多操作。
make
的退出状态总是以下三个值之一:
0
:如果make
成功,则退出状态为零。2
:如果make
遇到任何错误,则退出状态为两。它将打印描述特定错误的消息。1
:如果使用-q
标志,并且make
确定某个目标尚未更新,则退出状态为一。
下一篇:练习10 Makefile之隐式规则,上一篇:练习8 Makefile之使用函数,目录|首页
指定Makefile的参数
在 make
工具中,你可以通过 -f
或 --file
选项(--makefile
也可以)来指定 Makefile 的名称。例如,-f altmake
表示使用 altmake 文件作为 Makefile。
如果你多次使用 -f
标志,并在每个 -f
后跟一个参数,所有指定的文件将联合使用作为 Makefile。
如果你没有使用 -f
或 --file
标志,默认情况下会尝试使用 GNUmakefile
、makefile
和 Makefile
,按照这个顺序,使用首个存在或可创建的文件作为 Makefile。
下一篇:练习10 Makefile之隐式规则,上一篇:练习8 Makefile之使用函数,目录|首页
指定目标的论据
在 make
工具中,目标是 make
应该最终努力更新的目标。其他目标也会被更新,如果它们作为目标的先决条件,或作为目标的先决条件的先决条件等等。
默认情况下,目标是 Makefile 中的第一个目标(不计算以句点开头的目标)。因此,makefiles 通常是这样编写的,使得第一个目标用于编译描述的整个程序或程序。如果 Makefile 中的第一个规则有多个目标,只有规则中的第一个目标成为默认目标,而不是整个列表。你可以使用 .DEFAULT_GOAL
变量(参见其他特殊变量)在 Makefile 中管理默认目标的选择。
你还可以通过 make
命令行参数指定不同的目标。将目标名称作为参数。如果指定了多个目标,make
将按照指定的顺序依次处理它们。
在 Makefile 中的任何目标都可以指定为目标(除非它以 -
开头或包含 =
,在这种情况下,它将被解析为开关或变量定义)。即使 Makefile 中不存在的目标也可以指定,如果 make 能够找到说明如何制作它们的隐含规则。
make
将特殊变量 MAKECMDGOALS
设置为你在命令行上指定的目标列表。如果在命令行上没有指定目标,此变量为空。请注意,此变量只应在特殊情况下使用。
一个适当使用的示例是在 clean
规则中避免包含 .d
文件(参见 Generating Prerequisites Automatically),这样 make
就不会创建它们,只是立即将它们删除:
sources = foo.c bar.c
ifeq (,$(filter clean,$(MAKECMDGOALS))
include $(sources:.c=.d)
endif
指定目标的一个用途是,如果你只想编译程序的一部分,或者只想编译多个程序中的一个。指定每个你想要重新制作的文件作为目标。例如,考虑一个包含多个程序的目录,其中的 makefile 开始如下:
.PHONY: all
all: size nm ld ar as
如果你正在处理 size
程序,你可能想说 make size
,这样只有该程序的文件会被重新编译。
指定目标的另一个用途是制作通常不制作的文件。例如,可能有一个包含调试输出的文件,或者一个专门为测试而编译的程序版本,该版本在 Makefile 中有一个规则但不是默认目标的先决条件。
指定目标的另一个用途是运行与虚拟目标相关联的配方或空目标。许多 Makefile 包含一个名为 clean
的虚拟目标,它删除除源文件以外的所有内容。当你明确使用 make clean
时,才会执行此操作。以下是典型的虚拟和空目标名称列表。
all
: 制作 Makefile 知道的所有顶级目标。clean
: 删除通过运行make
通常创建的所有文件。mostlyclean
: 类似于clean
,但可能不删除人们通常不希望重新编译的一些文件。例如,GCC 的mostlyclean
目标不会删除libgcc.a
,因为很少需要重新编译它,而且需要很长时间。distclean
,realclean
,clobber
: 这些目标中的任何一个都可能被定义为删除比clean
更多的文件。例如,这将删除通常由make
自动创建的配置文件或链接,即使 Makefile 本身不能创建这些文件。install
: 将可执行文件复制到用户通常搜索命令的目录;将可执行文件使用的任何辅助文件复制到它们将查找它们的目录中。print
: 打印已更改的源文件的列表。tar
: 创建源文件的 tar 文件。shar
: 创建源文件的 shell 存档(shar 文件)。dist
: 创建源文件的发布文件。这可能是一个 tar 文件、一个 shar 文件、上述任意一个的压缩版本,甚至是上述多个的压缩版本。TAGS
: 为此程序更新标签表。check
,test
: 对此 Makefile 构建的程序执行自检。
下一篇:练习10 Makefile之隐式规则,上一篇:练习8 Makefile之使用函数,目录|首页
代替执行配方
在 make
工具中,有一些选项可以指定 make
执行的操作而不是更新目标。
-
-n
、--just-print
、--dry-run
、--recon
:这是一个“不执行”操作,它使make
打印出为使目标更新而需要执行的命令,但实际上并不执行它们。请注意,即使使用了此标志,仍然会执行一些配方。此外,仍然会执行用于更新包含的 Makefile 的任何配方。make -n
-
-t
、--touch
:这是一个“Touch”操作,它将目标标记为已更新,但实际上并不更改它们。换句话说,make
假装更新目标,但实际上并没有改变它们的内容;只是它们的修改时间被更新了。make -t
-
-q
、--question
:这是一个“Question”操作,它会静默地检查目标是否已更新,但不执行配方;退出代码显示是否需要进行任何更新。make -q
-
-W file
、--what-if=file
、--assume-new=file
、--new-file=file
:“What if”操作。每个-W
标志后面都跟着一个文件名。make
记录给定文件的修改时间为当前时间,尽管实际的修改时间保持不变。可以与-n
标志一起使用,以查看如果修改特定文件会发生什么。make -n -W file
请注意,-n
、-t
和 -q
选项不会影响以 +
字符开始或包含字符串 $(MAKE)
或 ${MAKE}
的配方行。请注意,只有包含 +
字符或字符串 $(MAKE)
或 ${MAKE}
的行才会运行,而不管这些选项如何。规则中的其他行除非以 +
开头或包含 $(MAKE)
或 ${MAKE}
,否则不会运行。
-t
标志阻止虚拟目标被更新,除非有以 +
开头或包含 $(MAKE)
或 ${MAKE}
的配方行。
-W
标志提供了两个功能:
- 如果还使用
-n
或-q
标志,则可以查看如果修改某些文件,make
会执行什么操作。 - 如果没有
-n
或-q
标志,当make
实际执行配方时,-W
标志可以指示make
假装某些文件已被修改,而实际上并没有运行这些文件的配方。
请注意,选项 -p
和 -v
允许您获取关于 make
或正在使用的 Makefile 的其他信息。
下一篇:练习10 Makefile之隐式规则,上一篇:练习8 Makefile之使用函数,目录|首页
避免重新编译某些文件
在修改了某个源文件但不想重新编译所有依赖它的文件时,可以使用 -t
标志。这个标志告诉 make
不要运行规则中的命令,而是通过更改目标的最后修改日期将其标记为最新。可以按照以下步骤进行操作:
- 使用命令
make
重新编译确实需要重新编译的源文件,确保在开始之前对象文件是最新的。 - 在头文件中进行更改。
- 使用命令
make -t
标记所有对象文件为最新。下次运行make
时,对头文件的更改将不会引起任何重新编译。
如果在一些文件确实需要重新编译的时候已经更改了头文件,那么做此操作已经为时过晚。相反,可以使用 -o file
标志,该标志将指定的文件标记为“旧文件”。这意味着文件本身将不会被重新创建,也不会因此重新创建其他文件。按照以下步骤操作:
- 使用
make -o headerfile
重新编译独立于特定头文件的原因需要编译的源文件。如果涉及多个头文件,为每个头文件使用单独的-o
选项。 - 使用
make -t
触摸所有对象文件。
下一篇:练习10 Makefile之隐式规则,上一篇:练习8 Makefile之使用函数,目录|首页
覆盖变量
在命令行参数中包含 ‘=’ 的参数指定了一个变量的值:‘v=x’ 将变量 v 的值设置为 x。如果以这种方式指定了一个值,那么 Makefile 中对同一变量的所有常规赋值都会被忽略;我们说它们被命令行参数覆盖。
使用这个功能的最常见方式是向编译器传递额外的标志。例如,在一个正确编写的 Makefile 中,变量 CFLAGS 被包含在每个运行 C 编译器的规则中,所以一个名为 foo.c 的文件会被编译成这样:
cc -c $(CFLAGS) foo.c
因此,无论你为 CFLAGS 设置什么值,都会影响每一次编译。Makefile 可能会为 CFLAGS 指定通常的值,例如:
CFLAGS=-g
每次运行 make 时,你都可以覆盖这个值。例如,如果你执行 ‘make CFLAGS=’-g -O’',那么每次 C 编译都会使用 ‘cc -c -g -O’。
变量 CFLAGS 只是许多标准变量中的一个,这样你就可以通过这种方式更改它们。获取完整列表。
你还可以编写 Makefile 来查看用户定义的其他变量,从而使用户能够通过更改这些变量来控制 Makefile 的其他方面。
在用命令行参数覆盖变量时,可以定义递归展开变量或者简单展开变量。上面示例中展示的是递归展开变量;要创建简单展开变量,可以用 ‘:=’ 或 ‘::=’ 代替 ‘=’。但是,除非你想在你指定的值中包含一个变量引用或函数调用,否则创建哪种类型的变量并没有影响。
Makefile 有一种方式可以改变用户覆盖的变量,即使用 override 指令,该指令的语法如下:‘override variable = value’。
下一篇:练习10 Makefile之隐式规则,上一篇:练习8 Makefile之使用函数,目录|首页
测试程序的编译
通常情况下,当在执行 shell 命令时发生错误时,make 会立即放弃,返回非零状态。对于任何目标,都不会执行进一步的规则。错误意味着目标无法正确重建,make 会在了解到错误时立即报告。
当你正在编译刚刚修改的程序时,这通常不是你想要的。相反,你可能希望 make 尝试编译每个可以尝试的文件,以显示尽可能多的编译错误。
在这种情况下,你应该使用 ‘-k’ 或 ‘–keep-going’ 标志。这告诉 make 在放弃并返回非零状态之前继续考虑挂起目标的其他前提,如果需要的话,重新生成它们。例如,在编译一个对象文件时发生错误,‘make -k’ 将继续编译其他对象文件,即使它已经知道链接它们将是不可能的。除了在 shell 命令失败后继续执行外,‘make -k’ 还会在发现无法生成目标或前提文件时尽可能继续。这总会导致错误消息,但是如果没有 ‘-k’,这将是致命错误。
make 的通常行为假定你的目的是使目标保持最新;一旦 make 了解到这是不可能的,它可能会立即报告失败。‘-k’ 标志表示真正的目的是尽可能测试程序中所做的更改,也许是为了找到几个独立的问题,以便在下一次尝试编译之前纠正它们。这就是为什么 Emacs 的 M-x compile 命令默认传递 ‘-k’ 标志的原因。
下一篇:练习10 Makefile之隐式规则,上一篇:练习8 Makefile之使用函数,目录|首页
临时文件
在某些情况下,make 需要创建自己的临时文件。在运行 make 期间,这些文件不能被干扰,包括所有递归调用的 make 实例。
如果环境变量 MAKE_TMPDIR 被设置,那么 make 创建的所有临时文件都会放在那里。
如果 MAKE_TMPDIR 没有设置,那么将使用当前操作系统的标准位置用于临时文件。对于 POSIX 系统,这将是在 TMPDIR 环境变量中设置的位置,否则将使用系统的默认位置(例如,/tmp)。在 Windows 上,首先检查 TMP,然后检查 TEMP,然后检查 TMPDIR,最后使用系统默认的临时文件位置。
请注意,这个目录必须已经存在,否则 make 将失败:make 不会尝试创建它。
这些变量不能在 Makefile 中设置:GNU make 在开始读取 Makefile 之前必须能够访问这个位置。
下一篇:练习10 Makefile之隐式规则,上一篇:练习8 Makefile之使用函数,目录|首页
选项总结
这是 make
命令可以理解的所有选项的列表:
-
‘-b’, ‘-m’: 这些选项为了与其他版本的
make
兼容而被忽略。 -
‘-B’, ‘–always-make’: 将所有目标都视为过时。即使其先决条件的状态不清楚,GNU make 也会继续考虑这些目标,并使其全部重新生成。
-
‘-C dir’, ‘–directory=dir’: 在读取 Makefile 之前切换到目录 dir。如果有多个 ‘-C’ 选项,每个都相对于前一个解释。通常与递归调用 make 一起使用。
-
‘-d’, ‘–debug[=options]’: 打印除正常处理之外的调试信息。具体的调试级别和类型可以通过参数指定。例如,‘-d’ 相当于 ‘–debug=a’。
-
‘-e’, ‘–environment-overrides’: 使来自环境的变量优先于 Makefile 中的变量。
-
‘-E string’, ‘–eval=string’: 将字符串 string 作为 Makefile 语法进行评估。
-
‘-f file’, ‘–file=file’, ‘–makefile=file’: 读取名为 file 的文件作为 Makefile。
-
‘-h’, ‘–help’: 提示 make 可以理解的选项,然后退出。
-
‘-i’, ‘–ignore-errors’: 忽略重新生成文件时执行的所有配方中的错误。
-
‘-I dir’, ‘–include-dir=dir’: 指定搜索包含的 Makefile 的目录 dir。
-
‘-j [jobs]’, ‘–jobs[=jobs]’: 指定同时运行的配方(作业)的数量。
-
‘–jobserver-style=[style]’: 选择使用的作业服务器样式。
-
‘-k’, ‘–keep-going’: 在错误发生后继续尽可能多地执行。
-
‘-l [load]’, ‘–load-average[=load]’, ‘–max-load[=load]’: 指定在有其他正在运行的作业并且负载平均值至少为 load 时,不应启动新的配方。
-
‘-L’, ‘–check-symlink-times’: 在支持符号链接的系统上,使 make 也考虑符号链接的时间戳。
-
‘-n’, ‘–just-print’, ‘–dry-run’, ‘–recon’: 打印将执行的配方,但不执行它们。
-
‘-o file’, ‘–old-file=file’, ‘–assume-old=file’: 不要重新生成文件 file,即使它比其先决条件旧,并且不要因文件的更改而重新生成任何东西。
-
‘-O[type]’, ‘–output-sync[=type]’: 确保每个配方的完整输出以一个不间断的序列打印。
-
‘-p’, ‘–print-data-base’: 打印读取 Makefile 后生成的数据库(规则和变量值)。
-
‘-q’, ‘–question’: “问题模式”。仅返回一个退出状态,指示指定的目标是否已经是最新,是否需要重新生成,或是否遇到错误。
-
‘-r’, ‘–no-builtin-rules’: 消除使用内置隐式规则。
-
‘-R’, ‘–no-builtin-variables’: 消除使用内置规则特定变量。
-
‘-s’, ‘–silent’, ‘–quiet’: 静默操作,不打印正在执行的配方。
-
‘-S’, ‘–no-keep-going’, ‘–stop’: 取消 ‘-k’ 选项的效果。
-
‘–shuffle[=mode]’: 启用前件关系的随机调整。
-
‘-t’, ‘–touch’: 触摸文件而不是运行其配方。
-
‘–trace’: 显示 make 执行的跟踪信息。
-
‘-v’, ‘–version’: 打印 make 程序的版本以及版权、作者列表和无担保声明,然后退出。
-
‘-w’, ‘–print-directory’: 在执行 Makefile 之前和之后打印包含的工作目录。
-
‘–no-print-directory’: 在 -w 下禁用工作目录的打印。
-
‘-W file’, ‘–what-if=file’, ‘–new-file=file’, ‘–assume-new=file’: 假装目标文件刚刚被修改。
-
‘–warn-undefined-variables’: 每当 make 看到对未定义变量的引用时发出警告。
请注意,这是一个长列表,上面列出的选项可能不适用于所有版本的 make
。
下一篇:练习10 Makefile之隐式规则,上一篇:练习8 Makefile之使用函数,目录|首页