描述如何重新编译某个程序的 makefile,可以有多种用法。最简单的用法是把所有过时(需要更新)的文件全部重新编译。大多数 makefile 都写成在不带任何参数运行 make 时正好执行这一动作。
不过,你有时可能只想更新一部分文件。或者你想使用别的编译器或别的编译选项,也可能你根本不改动文件本身,只想查看哪些文件已经过时。
运行 make 时给出参数,就能实现这些以及许多其他操作。
make 的退出状态总是下面三个值之一:
0make 成功时,退出状态为零。
2make 遇到某种错误时,退出状态为 2。此时会显示说明各个错误的消息。
1如果使用了 ‘-q’ 标志,并且 make 判定某个目标尚未最新,退出状态为 1。请参阅代替执行命令的动作。
要指定所用 makefile 的名字,可以使用 ‘-f’ 或 ‘--file’ 选项(‘--makefile’ 作用相同)。例如,‘-f altmake’ 指示把名为 altmake 的文件用作 makefile。
如果多次使用 ‘-f’ 标志,每次在 ‘-f’ 后面跟一个参数,那么所有指定的文件会被合并起来一起用作 makefile。
如果没有使用 ‘-f’ 或 ‘--file’ 标志,则默认依次尝试 GNUmakefile、makefile、Makefile,并使用这三个中最先存在或能够生成的那一个(请参阅如何编写 Makefile)。
目标 (goal) 是指 make 最终想要更新的目标。除此之外的其他目标,如果作为该目标的前置条件 (prerequisite)、或目标的前置条件的前置条件,这样层层关联,也会一并更新。
默认情况下,目标是 makefile 中的第一个目标(以句点开头的目标不计入)。因此 makefile 通常写成让第一个目标完成整个程序(或多个程序)的编译。如果 makefile 的第一条规则有多个目标,成为默认目标的只是该规则的第一个目标,而不是整个列表。默认目标的选取方式也可以在 makefile 中使用 .DEFAULT_GOAL 变量来控制(请参阅其他特殊变量)。
此外,还可以通过 make 的命令行参数指定另外一个或多个目标。请把目标的名字作为参数给出。如果指定了多个目标,make 会按照所列举的顺序逐个处理它们。
makefile 中的任意目标都可以指定为目标(但以 ‘-’ 开头的、或含有 ‘=’ 的除外,因为它们会分别被解释为开关和变量定义)。此外,即使是 makefile 中没有列出的目标,只要 make 能找到说明其制作方法的隐含规则,也可以指定为目标。
make 会把命令行上指定的目标列表设置到特殊变量 MAKECMDGOALS 中。如果命令行上没有给出任何目标,该变量为空。请注意,这个变量只应在特殊场合使用。
一个恰当的用法是,在执行 clean 规则期间避免把 .d 文件 include 进来(请参阅自动生成前置条件)。这样可以避免 make 好不容易生成的 .d 文件随即被删除这种无用功:
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 中虽有它们的规则,但它们并不是默认目标的前置条件,就属于这种情形。
指定目标的再一种用途是,执行对应于伪目标 (phony)(请参阅伪目标 (phony))或空目标(请参阅记录事件的空目标文件)的命令 (recipe)。许多 makefile 都包含一个名为 clean 的伪目标,用于删除源文件以外的所有内容。理所当然,它只在你明确请求 ‘make clean’ 时才会执行。下面列出一些常用的伪目标和空目标的名字。关于 GNU 软件包所用的标准目标名的详细列表,请参阅面向用户的标准目标。
制作该 makefile 所知道的所有顶层目标。
删除运行 make 通常会生成的所有文件。
与 ‘clean’ 类似,但可能会保留一些通常不希望重新编译的文件而不删除。例如,GCC 的 ‘mostlyclean’ 目标不会删除 libgcc.a,因为很少需要重新编译它,而且重新编译又很耗时。
这些目标都可能被定义为删除比 ‘clean’ 更多的文件。例如,删除你为编译做准备而通常会创建的配置文件或链接,就是这种情况。即便 makefile 自身无法生成这些文件,也照样会删除。
把可执行文件复制到用户查找命令时通常会搜索的目录。此外,如果该可执行文件使用了辅助文件,也把它们复制到可执行文件会去查找的目录。
打印已改动的源文件列表。
创建源文件的 tar 文件。
创建源文件的 shell 归档(shar 文件)。
创建源文件的分发用文件。它可以是 tar 文件、shar 文件,或它们的压缩形式,甚至是上述若干种的组合。
更新本程序所用的标签表。
对该 makefile 所构建的程序运行自检。
makefile 告诉 make 如何判断某个目标是否最新,以及如何更新各个目标。但更新目标并不总是你想要的动作。有些选项就是为了让 make 做更新以外的工作。
「什么也不做(No-op)」。让 make 显示为使目标变为最新所需的命令,但不实际执行。不过请注意,即便加了这个标志,仍有一些命令会被执行(请参阅 MAKE 变量的作用)。另外,为更新 include 进来的 makefile 所需的命令,也照样会执行(请参阅Makefile 重新生成的机制)。
「触碰(Touch)」。不实际改动目标,而把它标记为最新。换句话说,make 只是假装更新了目标,并不真正改变内容,只改写更新时刻。
「询问(Question)」。在不输出任何内容的情况下检查目标是否最新,但不执行命令。是否需要更新由退出码表示。
「假如(What if)」。每个 ‘-W’ 标志后面都跟一个文件名。在保持所指定文件实际更新时刻不变的同时,make 会把这些文件的更新时刻记为当前时刻。把 ‘-W’ 标志与 ‘-n’ 标志组合使用,就能确认改动某个特定文件后会发生什么。
加上 ‘-n’ 标志后,make 会显示通常应当执行的命令,但大多数情况下并不执行它们。
加上 ‘-t’ 标志后,make 会忽略规则中的命令,对每个需要重新制作的目标(实质上)使用 touch 命令。只要没有使用 ‘-s’ 或 .SILENT,该 touch 命令也会被显示出来。需要说明的是,出于速度考虑,make 并不真正启动 touch 程序,而是直接完成这项工作。
加上 ‘-q’ 标志后,make 什么也不显示,也完全不执行命令,仅在目标已经最新时才返回零作为退出状态码。退出状态为 1 表示需要某种更新。如果 make 遇到错误,退出状态为 2,因此可以区分错误与「尚未最新的目标」。
在同一次 make 调用中使用这三个标志中的两个或更多,会导致错误。
‘-n’、‘-t’、‘-q’ 这几个选项不会影响以 ‘+’ 字符开头的命令(recipe)行,也不会影响含有 ‘$(MAKE)’ 或 ‘${MAKE}’ 字符串的命令(recipe)行。请注意,无论这些选项如何,会被执行的只有含 ‘+’ 字符的行,或含有 ‘$(MAKE)’、‘${MAKE}’ 字符串的行。同一规则中的其他行,除非自身以 ‘+’ 开头,或含有 ‘$(MAKE)’ 或 ‘${MAKE}’,否则不会被执行(请参阅 MAKE 变量的作用)。
‘-t’ 标志可防止伪目标 (phony)(请参阅伪目标 (phony))被更新。但如果存在以 ‘+’ 开头的命令(recipe)行,或含有 ‘$(MAKE)’ 或 ‘${MAKE}’ 的命令(recipe)行,则属例外。
‘-W’ 标志提供两项功能:
make 会做什么。
make 正在实际执行命令时,‘-W’ 标志可以让 make 表现得仿佛某些文件已被改动。此时,针对这些文件的命令并不会实际执行。
另外,使用 ‘-p’ 或 ‘-v’ 选项,可以获得关于 make 自身或所用 makefile 的其他信息(请参阅选项汇总)。
有时你改动了某个源文件,却并不想把所有依赖它的文件全部重新编译。例如,你在许多文件都依赖的某个头文件里增加了一个宏或声明。make 为稳妥起见,会假定头文件只要有一点改动,就需要重新编译所有依赖它的文件。但你心里清楚它们并不需要重新编译,又不想浪费时间等待编译完成。
如果在改动头文件之前就能预料到这个问题,可以使用 ‘-t’ 标志。该标志指示 make 不执行规则中的命令,而改为通过改写最后修改时刻来把目标标记为最新。具体步骤如下:
make 时,头文件的改动就不会引发重新编译。
如果你已经在某些文件确实需要重新编译的时机改动了头文件,这个方法就为时已晚了。这种情况下,可以改用 ‘-o file’ 标志,它把指定的文件标记为「过时(old)」(请参阅选项汇总)。这样一来,该文件本身不会被重新制作,也不会因为该文件改动而导致别的东西被重新制作。具体步骤如下:
含有 ‘=’ 的参数用于指定变量的值。‘v=x’ 把变量 v 的值设置为 x。以这种方式指定值后,makefile 中对同一变量的所有普通赋值都会被忽略。我们把这称为变量被命令行参数覆盖(override)了。
这个功能最常见的用法,是向编译器传递额外的标志。例如,在编写规范的 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’ 进行。(这同时也示范了,在覆盖变量时,如何用 shell 的引号把值中包含的空白和其他特殊字符括起来处理。)
变量 CFLAGS 只是众多标准变量之一,这些变量正是为了能这样改动而准备的。完整列表请参阅隐含规则所用的变量。
此外,你也可以对 makefile 进行编程,让它另外引用你自定义的变量。这样一来,使用者就能通过改动变量来控制 makefile 行为的其他方面。
用命令行参数覆盖变量时,既可以定义为递归展开变量,也可以定义为简单展开变量。上面给出的例子创建的是递归展开变量。若要创建简单展开变量,请用 ‘:=’ 或 ‘::=’ 代替 ‘=’。不过,除非你想在所指定的值中包含变量引用或函数调用,否则创建哪种变量都没有区别。
makefile 有一种、也是唯一一种办法可以改动你所覆盖的变量,那就是使用 override 指令 (directive)。它是形如下面这样的一行: ‘override variable = value’(请参阅 override 指令)。
通常,shell 命令执行过程中一旦发生错误,make 就会立刻放弃工作并返回非零状态。从此之后,不会再对任何目标执行命令。因为发生错误意味着无法正确地重新制作目标,所以 make 一旦发现就立即报告。
然而,当你正在编译刚刚改动过的程序时,这并不是你想要的动作。相反,你更希望 make 尽可能多地编译能编译的文件,把尽量多的编译错误一次性展示出来。
这种情况下,应当使用 ‘-k’ 或 ‘--keep-going’ 标志。它指示 make 在放弃并返回非零状态之前,继续考察正在处理的目标的其他前置条件,必要时予以重新制作。例如,即便某个目标文件 (object) 的编译发生了错误,‘make -k’ 仍会继续编译其他目标文件,尽管已经知道再也无法把它们链接起来。除了在失败的 shell 命令之后继续处理外,‘make -k’ 在判明无法得知某个目标或前置条件文件的制作方法之后,也会尽可能地继续处理。这必定会引发错误消息,而若没有 ‘-k’,这本会成为致命错误(请参阅选项汇总)。
make 的通常行为假定你的目的是把目标变为最新。因此,make 一旦发现这不可能,就最好立即报告失败。‘-k’ 标志表示,真正的目的是尽可能多地测试你对程序所做的改动,大概是想找出彼此独立的多个问题,并在尝试下一次编译之前把它们一并修正。这也是 Emacs 的 M-x compile 命令默认传递 ‘-k’ 标志的原因。
在某些情形下,make 需要创建自己的临时文件。在 make 运行期间(包括所有被递归调用的 make 实例),这些文件不得被扰动。
如果设置了环境变量 MAKE_TMPDIR,make 创建的所有临时文件都会放在那里。
如果没有设置 MAKE_TMPDIR,则使用当前操作系统中临时文件的标准位置。在 POSIX 系统上,这是环境变量 TMPDIR 所设置的位置,或系统默认位置(例如 /tmp)。在 Windows 上,会先查 TMP,再查 TEMP,接着查 TMPDIR,最后使用系统默认的临时文件存放处。
请注意,该目录必须事先存在,否则 make 会失败。make 不会尝试创建该目录。
这些变量不能在 makefile 中设置,因为 GNU make 需要在开始读取 makefile 之前就能访问该位置。
下面是 make 能够理解的所有选项的列表:
为与其他版本的 make 兼容,这些选项被忽略。
把所有目标都视为过时(需要更新)的状态。GNU make 会按通常的算法考察目标及其前置条件,但这样被考察的目标无论其前置条件的状态如何,都总是被重新制作。为避免无限递归,当 MAKE_RESTARTS(请参阅其他特殊变量)被设置为大于 0 的数时,在考察是否应重新制作 makefile 的过程中,此选项会失效(请参阅Makefile 重新生成的机制)。
在读取 makefile 之前切换到目录 dir。如果指定了多个 ‘-C’ 选项,每个都相对于前一个来解释。‘-C / -C etc’ 等价于 ‘-C /etc’。这通常用于 make 的递归调用(请参阅递归使用 make)。
在通常处理之外,还显示调试信息。调试信息会把 make 如何决定该做什么的种种有意思的内容全部告诉你,例如:哪些文件被作为重新制作的对象加以考察、哪些文件的时刻被比较以及结果如何、哪些文件确实需要重新制作、考察了哪些隐含规则又应用了哪些。-d 选项等价于 ‘--debug=a’(见下文)。
在通常处理之外,还显示调试信息。输出的级别和种类可以多样选择。不带参数时,显示「基本(basic)」级别的调试。可指定的参数如下。只判定首字符,各值之间须用逗号或空格分隔。
a (all)启用所有种类的调试输出。这等价于使用 ‘-d’。
b (basic)基本调试。显示每个被判定为过时的目标,以及构建是否成功。
v (verbose)比 ‘basic’ 高一级。包含关于解析了哪些 makefile、哪些前置条件无需重新构建等的消息。此选项也会启用 ‘basic’ 的消息。
i (implicit)显示说明针对各个目标的隐含规则搜索的消息。此选项也会启用 ‘basic’ 的消息。
j (jobs)显示传达各个子命令启动详情的消息。
m (makefile)默认情况下,上述消息在尝试重新制作 makefile 期间不会启用。此选项让消息在重新构建 makefile 期间也保持启用。需要说明的是,‘all’ 选项也会启用此选项。此选项也会启用 ‘basic’ 的消息。
p (print)即便是通常(因 .SILENT 或 ‘@’ 而)不会输出的命令,也会显示所执行的命令。此外还会显示该命令所定义的 makefile 名和行号。
w (why)通过指出哪些前置条件比目标新,来说明每个目标必须重新制作的理由。
n (none)禁用当前已启用的所有调试。如果此后又出现额外的调试标志,它们会继续生效。
让从环境取得的变量优先于 makefile 内的变量。请参阅从环境取得的变量。
把 string 作为 makefile 语法来求值。这是 eval 函数(请参阅 eval 函数)的命令行版本。求值在默认规则和变量定义之后、读取 makefile 之前进行。
把名为 file 的文件作为 makefile 读取。请参阅如何编写 Makefile。
提示你 make 能够理解的选项,然后退出。
忽略为重新制作文件而执行的命令中的所有错误。请参阅命令中的错误。
指定搜索被 include 的 makefile 的目录 dir。请参阅include 其他 Makefile。如果用多个 ‘-I’ 选项指定了多个目录,会按指定顺序搜索这些目录。如果目录 dir 是单个连字符(-),则在此之前已经指定的所有目录(包括默认目录路径)都会被丢弃。要搜索的当前目录列表可以通过 .INCLUDE_DIRS 变量查看。
指定同时执行的命令(作业 (job))数。如果没有参数,make 会尽可能多地同时执行命令。如果有多个 ‘-j’ 选项,最后一个生效。关于命令如何执行的详细信息,请参阅并行执行。需要说明的是,此选项在 MS-DOS 上被忽略。
选择所用的 jobserver 方式。此选项仅在并行构建已启用时才有效(请参阅并行执行)。在 POSIX 系统上,style 是 fifo(默认)或 pipe 之一。在 Windows 上,可接受的 style 只有 sem(默认)。当你需要使用旧版本的 GNU make,或需要特定 jobserver 方式的另一种工具时,此选项很有用。
在错误之后仍尽可能继续处理。失败的目标以及依赖它的目标无法重新制作,但这些目标的其他前置条件仍可照常处理。请参阅测试程序的编译。
指定在还有其他正在执行的命令、并且负载平均值至少达到 load(浮点数)时,不开始新的命令。不带参数时,解除先前的负载限制。请参阅并行执行。
在支持符号链接的系统上,此选项让 make 在链接所指向文件的时间戳之外,也将符号链接自身的时间戳一并纳入考量。指定此选项后,文件与符号链接中最新的那个时间戳,会被采用为该目标文件的更新时刻。
显示应当执行的命令,但(在特定情况之外)不执行。请参阅代替执行命令的动作。
即使文件 file 比前置条件旧,也不重新制作它,也不因 file 的改动而重新制作别的东西。本质上,该文件被当作极其古老来处理,其规则被忽略。请参阅避免重新编译某些文件。
使来自各条命令的全部输出连贯不断地成片显示。此选项仅在使用 --jobs 选项同时执行多条命令时才有用(请参阅并行执行)。没有此选项时,输出会随着命令产生而即时显示。
不指定 type 或 type 为 ‘target’ 时,来自各个目标的整条命令的输出会被汇成一片。type 为 ‘line’ 时,来自命令各行的输出会被汇成一片。type 为 ‘recurse’ 时,来自整个递归 make 的输出会被汇成一片。type 为 ‘none’ 时,不进行输出的同步。请参阅并行执行期间的输出。
显示读取 makefile 后得到的数据库(规则与变量的值)。然后照常运行,或按另行指定的方式运行。它也会显示 ‘-v’ 开关(见下文)所示的版本信息。要在不尝试重新制作文件的情况下显示数据库,请使用 ‘make -qp’。要显示预先定义的规则和变量的数据库,请使用 ‘make -p -f /dev/null’。数据库的输出中包含关于命令和变量定义的文件名与行号信息,因此在复杂环境中是有用的调试工具。
「询问模式(Question mode)」。不执行任何命令,也不显示任何内容。只返回退出状态:指定的目标已经最新则返回 0,需要某种重新制作则返回 1,遇到错误则返回 2。请参阅代替执行命令的动作。
取消使用内置的隐含规则(请参阅隐含规则的用法)。只要编写模式规则(请参阅模式规则的定义与重新定义),仍然可以定义你自己的规则。‘-r’ 选项还会清除供后缀规则使用的默认后缀列表(请参阅旧式后缀规则)。不过,仍然可以用 .SUFFIXES 规则定义自己的后缀,再定义自己的后缀规则。需要说明的是,-r 选项影响的只是规则,默认变量仍然有效(请参阅隐含规则所用的变量)。另请参阅下文的 ‘-R’ 选项。
取消使用内置的规则专用变量(请参阅隐含规则所用的变量)。当然,仍然可以定义你自己的变量。‘-R’ 选项也会自动启用 ‘-r’ 选项(见上文),因为在完全没有隐含规则所用变量定义的情况下只剩隐含规则是没有意义的。
静默运行。执行命令时不显示它。请参阅命令的回显。
抵消 ‘-k’ 选项的效果。只有在以下场合才会用到它:递归的 make 中 ‘-k’ 通过 MAKEFLAGS 从最上层的 make 继承下来(请参阅递归使用 make),或在环境变量 MAKEFLAGS 中设置了 ‘-k’,除此之外几乎用不到。
此选项对前置条件之间的关系启用一种模糊测试 (fuzz test)。当并行执行已启用(‘-j’)时,目标被构建的顺序的确定性会下降。如果前置条件在 makefile 中没有完整声明,这可能导致断断续续、难以查明原因的构建失败。
‘--shuffle’ 选项让 make 有意地重排目标和前置条件。目标与前置条件之间的关系会被保持,但某个目标的前置条件的排列顺序会按下文说明的方式被重排。
自动变量中前置条件的排列顺序不受此选项影响。
.NOTPARALLEL 伪目标会对该 makefile 禁用 shuffle。此外,含有 .WAIT 的前置条件列表不会被 shuffle。请参阅禁用并行执行。
‘--shuffle=’ 选项接受以下值:
random选择用于 shuffle 的随机种子 (seed)。这是没有指定 mode 时的默认值。所选的种子也会传递给子 make 命令。种子也包含在错误消息中,因此可以在将来的运行中重用以复现问题,或确认问题已经消除。
reverse不进行随机 shuffle,而是把目标和前置条件的顺序反转。
seed使用以指定种子值初始化的 ‘random’ shuffle。seed 是整数。
none禁用 shuffle。这会抵消此前的 ‘--shuffle’ 选项。
不执行命令,而是触碰文件(不实际改动而标记为最新)。这用于假装命令已被执行,以便欺骗将来的 make 调用。请参阅代替执行命令的动作。
显示 make 运行的跟踪信息。使用 --trace 是 --debug=print,why 的简写。
显示 make 程序的版本,以及版权声明、作者名单和无担保声明,然后退出。
在执行 makefile 之前和之后都显示包含工作目录的消息。当递归的 make 命令复杂地嵌套时,这有助于查明错误的根源。请参阅递归使用 make。(实际上,由于 ‘make’ 会自动替你完成,很少需要指定此选项。请参阅‘--print-directory’ 选项。)
禁用 -w 之下的工作目录显示。当 -w 被自动启用,但你又不想看到那些多余消息时,此选项很有用。请参阅‘--print-directory’ 选项。
假装目标 file 刚刚被改动过。与 ‘-n’ 标志一起使用时,可以确认改动该文件后会发生什么。不带 ‘-n’ 时,几乎等同于在运行 make 之前对指定文件执行 touch 命令,区别在于更新时刻只在 make 的想象中改变。请参阅代替执行命令的动作。
每当 make 发现对未定义变量的引用时,发出警告消息。当你试图调试以复杂方式使用变量的 makefile 时,这很有用。