GNU make official document.

1. 伪目标

Phony targets

  1. two targets:
    • avoid a conflict with a file of the same name
    • improve performance
  2. make several targets in one Makefile
1
2
3
4
5
6
7
8
9
10
11
all: prog1 prog2 prog3
.PHONY: all

prog1: prog1.o utils.o
cc -o prog1 prog1.o utils.o

prog2: prog2.o utils.o
cc -o prog2 prog2.o utils.o

prog3: prog3.o utils.o
cc -o prog3 prog3.o utils.o
  1. one phony target serves as a subroutine of the other
1
2
3
4
5
6
7
8
9
10
.PHONY: cleanall cleanobj cleandiff

cleanall: cleanobj cleandiff
rm program

cleanobj:
rm *.o

cleandiff:
rm *.diff

2. Makefile变量

makefile中的变量,类似于c语言中的宏,在makefile执行的时候会在使用的地方原样展开。变量命名可以包含字符、数字和下划线。Makefile 中变量和函数的展开(除规则命令行中的变量和函数以外),是在make读取 makefile 文件时进行的,这里的变量包括了使用“=”定义和使用指示符“define”定义的。

变量名不包括“:”、“#”、“=”和空白。变量名大小写敏感。

2.1 变量的引用

对一个变量的引用可以在Makefile的任何上下文中,目标、依赖、命令、绝大多数指示符和新变量的赋值中。变量引用的展开过程是严格的文本替换过程。变量在声明时需要赋予初值,在使用时,给变量名之前加上$符号,最好用()或者{}把变量包起来。如果要使用真实的$字符,需要用$$表示。

直接使用$x的格式可以引用变量,但这种做法仅限于变量名为单字符的情况。另外自动化变量也使用这种格式。对于一般多字符变量的引用必须使用括号了标记,否则make 将把变量名的首字母作为作为变量而不是整个字符串。

2.2 自动化变量

  • $@:目标文件
  • $^:所有的依赖文件
  • $<:第一个依赖文件
1
2
3
4
5
6
7
8
9
10
11
main: main.o mytool1.o mytool2.o
gcc -o $@ $^

main.o: main.c mytool2.h mytool1.h
gcc -c $<

mytool1.o: mytool1.c mytool1.h
gcc -c $<

mytool2.o: mytool2.c mytool2.h
gcc -c $<

2.3 隐式规则中的变量

这些变量在Makefile文件中未必能够找到定义。官方文档

  1. Variables used as names of programs in built-in rules
变量 含义
CC Program for compiling C programs; default ‘cc’
CXX Program for compiling C++ programs; default ‘g++’
CPP Program for running C preprocessor, with results to standard output; default ‘$(CC) -E’
RM Command to remove a file; default ‘rm -f’.

usage: $(CC), $(CFLAGS) …

  1. Variables whose values are additional arguments for the programs above.
变量 含义
CFLAGS Extra flags to give to the C compiler.
CXXFLAGS Extra flags to give to the C++ compiler.

2.4 = 递归展开式变量

make会将整个makefile展开后,再决定变量的值。也就是说,变量的值将会是整个makefile中最后被指定的值。

1
2
3
x=foo
y=$(x) bar
x=xyz

y的值是xyz bar。

2.5 := 直接展开式变量

在使用“:=”定义变量时,变量值中对其他量或者函数的引用在定义变量时被展开(对变量进行替换)。即,变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值。

1
2
3
x:=foo
y:=$(x) bar
x:=xyz

y的值是foo bar。

2.6 如何定义一个空格

使用直接扩展式变量定义,可以实现将一个前导空格定义在变量值中。一般变量值中的前导空格字符在变量引用和函数调用时被丢弃。

1
2
nullstring :=
space := $(nullstring) #end of the line

nullstring变量为空,space变量表示的就是一个空格。因为在操作符的右边是很难描述一个空格的,这里先用一个空变量来标明变量的值开始了,后面用“#”注释符来表示变量定义的终止。这样,我们可以定义出其值是一个空格的变量。

这里需要注意,在编写代码时,如果出现以下情况:

1
dir := /foo/bar    #directory to put the frobs in

#之前的空格会被包含进dir目录中。

2.7 ?= 操作符

条件赋值操作符“?=”。只有此变量在之前没有赋值的情况下才会对这个变量进行赋值。

1
FOO ?= bar

等价于

1
2
3
ifeq ($(origin FOO), undefined)
FOO = bar
endif

2.8 追加变量值

1
2
objects = main.o foo.o bar.o utils.o
objects += another.o

上面的操作结果为objects = main.o foo.o bar.o utils.o another.o

相当于:

1
2
objects = main.o foo.o bar.o utils.o
objects := $(objects) another.o

“+=” 规则:

  • 如果被追加值的变量之前没有定义,那么,“+=”会自动变成“=”,此变量就被定义为一个递归展开式的变量。如果之前存在这个变量定义,那么“+=”就继承之前定义时的变量风格。
  • 如果变量使用“:=”定义,之后“+=”操作将会首先替换展开之前此变量的值,然后在末尾添加需要追加的值,并使用“:=”重新给此变量赋值。
1
2
variables := value
variables += more

即为:

1
2
variables := value
variables := $(variable) more
  • 如果变量使用“=”定义,之后“+=”操作时不对之前此变量值中的任何引用进行替换展开,而是按照文本的扩展方式(之前等号右边的文本未发生变化)替换,然后在末尾添加需要追加的值,并使用“=”给此变量重新赋值。实际过程和“:=”类似。make会避免其中的递归定义。
1
2
variable = value
variable += more

即为:

1
2
temp = value
variable = $(temp) more

为了便于理解,这里引入了temp。

2.9 override指示符

通常在执行 make 时,如果通过命令行定义了一个变量,那么它将替代在 Makefile中出现的同名变量的定义。就是说,对于一个在 Makefile 中使用常规方式(使用“=”、“:=”或者“define”)定义的变量,可以在执行make 时通过命令行方式重新指定这个变量的值,命令行指定的值将替代出现在 Makefile 中此变量的值。如果不希望命令行指定的变量值替代在 Makefile 中的变量定义,则需要在 Makefile 中使用指示符“override”来对这个变量进行声明。

1
override VARIABLE = value

或者

1
override VARIABLE := value

可以对变量使用追加方式:

1
override VARIABLE += MORE TEXT

对于追加方式,变量在定义时使用了“override”,则后续对它值进行追加时,也需要使用带有override指示符的追加方式。否则对此变量值的追加不会生效。

指示符“override”并不是用来调整 Makefile 和执行时命令参数的冲突,其存在的目的是为了使用户可以改变或者追加那些使用 make 的命令行指定的变量的定义。从另外一个角度来说,就是实现了在 Makefile 中增加或者修改命令行参数的一种机制。

3. 缺省规则

1
2
.c.o:
gcc -c $<

表示所有.c文件都是依赖其对应的.o文件

1
2
3
4
5
main: main.o mytool1.o mytool2.o
gcc -o $@ $^

.c.o:
gcc -c $<

makefile自动推导功能:

1
2
main.o: main.c header.h
gcc -c main.c

简写如下:

1
main.o: header.h

4. 静态模式

1
2
<targets ...> : <target-pattern> : <prereq-patterns...>
<commands>
  • targets定义了目标文件,其中可以含有通配符。
  • target-pattern和prereq-patterns指明了如何获取每个目标的prerequisite。每个target和target-pattern相匹配,提取出target name的一部分,称为stem。stem替换进prereq-patterns,获得prerequisite name。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
object = main.o mytool1.o mytool2.o

main: $(object)

# main: main.o mytool1.o mytool2.o
# gcc -o main main.o mytool1.o mytool2.o

$(object): %.o : %.c

# main.o: main.c mytool1.h mytool2.h
# gcc -c main.c

# mytool1.o: mytool1.c mytool1.h
# gcc -c mytool1.c

# mytool2.o: mytool2.c mytool2.h
# gcc -c mytool2.c

5. Generating prerequisites automatically

You must always be very careful to update the makefile every time you add or remove an #include in a source file. To avoid this hassle, most moderm C compilers can write these rules for you, by looking at the #include lines in the source files. This is done by the ‘-M’ option to the compiler.

1
2
3
#main.o: main.c defs.h
# gcc -c main.c
cc -M main.c

With the GNU C compiler, you may wish to use the ‘-MM’ flag instead of ‘-M’. This omits prerequisites on system header files.

6. 递归使用make

在大工程中,包含多个子系统。我们可以在每个子系统中包含该子系统的makefile,此时可以递归地使用make命令$(MAKE)。

1
2
subsystem:
cd subdir && $(MAKE)

或者:

1
2
subsystem:
$(MAKE) -C subdir

表示首先进入子系统目录subdir,然后执行make命令。

整体系统中的makefile称为“总控makefile”,总控makefile的变量可以传递到子系统的makefile中,但是不会覆盖子系统makefile中定义的变量,除非指定了“-e”参数。

e.g.:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
all: geometry parsers programoptions thirdparty
.PHONY: geometry parsers programoptions thirdparty

geometry:
$(MAKE) -C geometry

parsers: thirdparty
$(MAKE) -C parsers

programoptions:
$(MAKE) -C programoptions

thirdparty:
$(MAKE) -C thirdparty

6.1 Communicating Variables to a Sub-make 传递变量

If you want to export specific variables to a sub-make, use the export directively, like this:

1
export variable ...

If you want to prevent a variable from being exported, use the unexported directively, like this:

1
unexported variable ...

In both of these forms, the arguments to export and unexported are expanded, and so could be variables or functions which expand to a (list of) variable names to be (un)exported.

As a convenience, you can define a variable and export it at the same time by doing:

1
export variable = value

has the same result as:

1
2
variable = value
export variable

If you want all variables to be exported by default, you can use export by itself:

1
export

The value of the make variable SHELL is not exported. Instead, the value of the SHELL variable from the invoking environment is passed to the sub-make. You can force make to export its value for SHELL by using the export directive.

The special variable MAKEFLAGS is always exported (unless you unexport it). MAKEFILES is exported if you set it to anything.

make automatically passes down variable values that were defined on the command line, by putting them in the MAKEFLAGS variable. Flags such as ‘-s’ and ‘-k’ are passed automatically to the sub-make through the variable MAKEFLAGS. This variable is set up automatically by make to contain the flag letters that makereceived. Thus, if you do ‘make -ks’ then MAKEFLAGS gets the value ‘ks’.

The options ‘-C’, ‘-f’, ‘-o’, and ‘-W’ are not put into MAKEFLAGS; these options are not passed down.

6.2 -w选项

在多级make的递归调用中,选项“-w”或者“—print-directory”可以让make在开始编译一个目录之前和完成目录的编译之后给出相应的提示信息。

通常,-w选项会被自动打开,在主控 Makefile 中当如果使用“-C”参数来为 make 指定一个目录或者使用“cd”进入一个目录时,“-w”选项会被自动打开。主控 make 可以使用选项“-s”(“—slient”)来禁止此选项。另外,make 的命令行选项“—no-print-directory”,将禁止所有关于目录信息的打印。

7. 规则的命令

makefile中的命令和shell命令是一致的。每条命令必须以[Tab]开头,除非命令是跟在规则后面,且规则后有分号。

7.1 命令回显 Recipe echoing

make在执行命令之前会把要执行的命令输出到标准输出设备,称之为“回显”。

如果命令行以@开始,则只执行而不回显这个命令。利用这一点,可以输出一些信息,追踪文件的编译。

1
@echo About to make distribution files.

上面这个例子执行了echo指令,输出后面的字符,但是不会输出echo。

使用make的命令行参数“-n”或者“—just-print”,那么make执行时会显示要执行的命令,而未必真的去执行这些命令。使用这个选项可以使我们按照执行顺序打印出Makefile中所有需要执行的命令。

make参数“-s”或者“—slient”禁止所有执行命令的显示,好比在每个指令前都加了@。在Makefile中也可以使用“.SILENT”禁止命令的回显。

7.2 每行命令属于一个独立的shell进程

Makefile中每一行执行的shell命令属于一个独立的shell子进程。因而,命令行“cd”改变目录不会对后面的其他行的命令的执行产生影响。

如果需要保持目录的改变,需要将“cd”和其他命令写在同一行,中间用分号分隔开。使用反斜杠“\”可以对处于多行的命令进行连接,表明它们属于同一个完整的shell命令行。

makefile中可以使用“SHELL”指定的程序对规则命令进行解析,在GNU make中默认的程序是“/bin/sh”。

7.3 并行执行

一般地,make一次会执行一条指令,在执行下一条指令前等待本条执行结束。参数“-j”或者”—jobs“使得make可以并发地执行许多指令。可以在特定的makefile文件中使用”.NOTPARALLEL”禁止并发。

如果”-j“之后存在一个整数,表示make同一时刻可以执行的命令的数目,称为”job slots“。如果“-j”后面没有数字,表示对执行的命令数没有要求。

7.4 命令执行的错误

通常,命令在执行结束之后,make都会检测命令执行的返回状态。如果返回成功,则启动另外一个shell子进程执行下一条命令。规则中的所有命令都执行完成之后,这个规则就执行结束了。如果一个规则中的某一条命令出错,make就会放弃对当前规则后续命令的执行。

有时,规则中命令的执行失败并不意味着规则执行的错误。例如使用mkdir创建目录,如果目录已经存在,则mkdir指令就会执行失败。

可以在命令之前加上一个减号“-”(在[Tab]键之后),来告诉make忽略此命令的执行失败。很多makefile文件中,clean目标一般如下:

1
2
clean:
-rm *.o

如果使用命令行选项“-i”或者“—ignore-errors”,make将忽略所有规则中命令执行的错误。也可以使用“.IGNORE”,在makefile中具有同样的效果。

可以使用make的命令行选项“—keep-going”通知make,在出现错误时不立刻退出,而是继续执行命令,直到无法继续执行命令时才异常退出。

7.5 Defining Canned Recipes 定义命令包

书写makefile时,可能有多个规则会使用相同的一组命令。在make中,可以使用指示符define对命令进行封装,同时使用一个变量来代表这一组命令。这样的一组命令称为命令包,以define开始,以endef结束。

1
2
3
4
define run-yacc = 
yacc $(firstword $^)
mv y.tab.c $@
endef

“run-yacc”是这个命令包的名字。在“define”和“endef”之间的命令就是命令包的主体。需要说明的是:使用“define”定义的命令包中,命令体中变量和函数的引用不会展开。命令体中所有的内容包括“$”、“(”、“)”等都是变量“run-yacc”的定义。它和 c 语言中宏的使用方式一样。

同样,类似c语言中的宏,当在规则的命令行中使用命令包变量时,命令包所定义的命令体就会对它进行替代。

1
2
foo.c: foo.y
$(run-yacc)

8. Makefile的条件执行

条件语句只能用于控制make实际执行的makefile文件部分,不能够控制规则的shell命令执行过程。

e.g.:

对变量“CC”进行判断,其值如果是“gcc”,那么在程序连接时使用“libgnu.so”或者“libgnu.a”,否则不链接任何库。

1
2
3
4
5
6
7
8
9
10
11
......
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
......

更为简洁的表达为:

1
2
3
4
5
6
7
8
9
10
libs_for_gcc = -lgnu
normal_libs =
ifeq($(CC),gcc)
libs=$(libs_for_gcc)
else
libs=$(normal_libs)
endif

foo: $(objects)
$(CC) -o foo $(objects) $(libs)

8.1 关键字 ifeq 与 ifneq

ifeq关键字用于判断参数是否相等。

可以有5种表达形式,以下3种应该是正常人会用的:

1
2
3
ifeq (arg1, arg2)
ifeq 'arg1' , 'arg2'
ifeq "arg1" , "arg2"

ifneq关键字用于判断参数是否不相等。类似于ifeq的表达形式。

8.2 关键字 ifdef 和 ifndef

ifdef关键字用于判断一个变量是否已经定义,格式为:

1
ifdef VARIABLE_NAME

ifdef只是测试一个变量是否有值,不会对变量进行替换展开来判断变量的值是否为空。对于变量VARIABLE_NAME,除了“VARIABLE_NAME = ”这种情况之外,使用其他方式对其的定义都会使得ifdef返回值为真。也就是说,即便通过其他方式对变量赋予了空值,ifdef返回值仍然为真。

ifndef关键字实现的功能和ifdef相反。

9. make的内嵌函数

make的内嵌函数提供了处理文件名、变量、文本和命令的方法。函数在调用的地方被替换为它的处理结果。函数调用(引用)的展开和变量引用的展开方式相同。

9.1 函数调用语法

以$开始表示一个引用。语法格式如下:

1
2
3
$(FUNCTION ARGUMENTS)
#or
${FUNCTION ARGUMENTS}
  1. FUNCTION是需要调用的函数名,它应该是内嵌函数的名称。对于用户自定义的函数,需要通过make的call函数来间接调用。
  2. ARGUMENTS是参数,与函数名之间用空格或者[Tab]隔开。多个参数之间用逗号隔开。参数不能出现逗号或者空格,如果需要包含,应该首先将它们赋值给某个变量。

9.2 文件名处理函数

9.2.1 $(wildcard PATTERN) 获取匹配模式文件名函数

功能:列出当前目录下所有符合模式“PATTERN(使用shell可识别的通配符)”格式的文件名。

返回值:使用空格分隔的、存在当前目录下的所有符合模式PATTERN的文件名。

e.g.:

1
2
$(wildcard *.c)
#返回值是当前目录下所有.c源文件列表。

9.2.2 $(dir NAMES…) 取目录函数

功能:从文件名序列“NAMES…”中取出各个文件名的目录部分。

返回值:空格分隔的文件名序列中每一个文件的目录部分。

(如果文件名中没有斜线,认为此文件为当前目录(./)下的文件)。

e.g.:

1
2
$(dir src/foo.c hacks)
#返回值是“src/ ./”

9.2.3 $(notdir NAMES…) 取文件名函数

功能:从文件名序列NAMES…中取出非目录部分。删除所有文件名的目录部分,只保留非目录部分。

返回值:文件名序列“NAMES…”中每一个文件的非目录部分。

10. Makefile的约定

10.1 基本约定

  1. 所有的Makefile中均应该包含这样一行:

    1
    SHELL = /bin/sh

    目的在于避免变量SHELL在有些系统上可能继承同名的系统环境变量而导致错误。GNU make中变量SHELL的默认值是“/bin/sh”。

  2. 小心处理规则中的路径。当需要处理指定目录的文件时,应该明确给出路径。“./”代表当前目录,“$(srcdir)”代表源代码目录。可以通过configure脚本选项“—srcdir”指定源代码所在的目录。

  3. 使用GNU make的变量“VPATH”指定搜索目录。当规则只有一个依赖文件时,应该使用自动化变量“$<”和“$@”代替出现在命令的依赖文件和目标文件。

10.2 代表命令变量

在Makefile中应该将所有的命令、选项作为变量定义,方便后期对命令和选项的修改。那么,用户可以通过修改一个变量值来重新指定要执行命令,或者来控制命令执行的选项、参数等。

当使用变量来表示命令时,如果规则中需要使用此命令时,可通过引用代表此命令的变量来实现。例如:定义变量“CC = gcc”,规则中就可使用“$(CC)”来引用“gcc”。对于一些件管理器工具,如“ln”、“rm”、“mv”等,可以不为它们定义变量,而直接使用。

所有命令执行的参数选项也应该定义一个变量(称为命令的选项变量)。在命令变量后添加“FLAGS”来命名这个选项变量。例如,变量“CFLAGS”是c编译器(命令变量为“CC”)的命令行选项变量;变量YFLAGS是命令“yacc”(命令变量是“YACC”)的选项变量;变量“LDFLAGS”是命令“ld”(令变量为“LD”)的选项变量。在所有需要执行预处理的命令行应该使用变量“CCFLAGS”作为gcc的执行参数:同样任何需要执行链接的命令行中使用“LDFLAGS”作为命令的执行参数。

c 编译器的编译选项变量“CFLAGS”在 Makefile 中通常是为编译所有的源文件提供选项变量。为编译一个特定文件增加的选项,不应包含在变量“CFLAGS”中。编译特定文件(或者一类特定文件)时,如果需要使用特定的选项参数,可以将这些选项写在编译它所执行规则的命令行中(也可以使用目标指定变量或者模式指定变量)。

e.g.:

1
2
3
4
CFLAGS = -g
ALL_FLAGS = -I $(CFLAGS)
.c .o:
$(CC) -c $(CPPFLAGS) $(ALL_FLAGS) $<

例子中,变量“CFLAGS”指定编译选项为“-g”,在本例中它作为缺省的编译选项。对于所有源文件的编译都使用“-g”选项。双后缀规则的命令行中为编译生成“.o”文件指定了另外的选项“-I. -g”。

在所有编译命令行中,变量“CFLAGS”应该放在编译选项列表的最后。这样可以保证当命令行参数出现重复时,“CFLAGS”始终效的。另外,在任何调用 c 编译器的命令行中都应该使用选项变量“CFLAGS”,无论是进行编译还是连接。

如果需要在Makefile中实现文件安装的规则,那么就需要在Makefile中定义变量“INSTALL”。此变量代表安装命令(install)。同时在Makefile中也需要定义变量“INSTALL_PROGRAM”和“INSTALL_DATA”(“INSTALL_PROGRAM”的缺省值是“$(INSTALL)”;“INSTALL_DATA”的缺省值是“${INSTALL} –m 644”)。可以使用这些变量,来安装可执行程序或者非可执行程序到指定位置。

e.g.:

1
2
$(INSTALL_PROGRAM) foo $(bindir)/foo
$(INSTALL_DATA) libfoo.a $(libdir)/libfoo.a

可以使用变量“DESTDIR”来指定目标需要安装的目录。通常也可以不在Makefile中定义变量“DESTDIR”,可以通过make命令行参数的形式来指定。例如:make DESTDIR=exec/install

e.g.:

1
2
$(INSTALL_PROGRAM) foo $(DESTDIR)$(bindir)/foo
$(INSTALL_DATA) libfoo.a $(DESTDIR)$(libdir)/libfoo.a

10.3 安装目录变量

安装目录需要使用变量来指定。

  1. 安装文件根目录(其余安装目录都是它们的子目录)

    • prefix:通常作为实际文件安装目录的父目录,可以理解为其他实际文件安装目录的前缀。变量“prefix”缺省值是“/usr/local”,创建完整的GNU系统时,变量prefix的缺省值时空值。
    • exec_prefix:变量“exec_prefix”缺省值是“$(prefix)”。通常,“$(exec_prefix)”目录中的子目录下存放和机器相关的文件。“$(prefix)”目录的子目录存放通用的一般文件。
  2. 文件(可执行文件、说明文档……)的安装目录

    • bindir:用于安装一般用户可运行的可执行程序。通常值为:“/usr/local/bin”,使用时一般写为“$(exec_prefix)/bin”。
    • sbindir:安装可在shell中直接调用执行的程序。通常值为:“/usr/local/sbin”,要求在使用时写为“$(exec_prefix)/sbin”。
    • libexecdir:用于安装那些通常不是由用户直接使用,而是由其它程序调用的可执行程序。通常它的值为:“/usr/local/libexec”,要求在使用时应写为:“$(exec_prefix)/libexec”。
    • includedir:用于安装用户程序源代码使用“#include”包含的头文件。它的缺省值:“/usr/local/include”,使用时应写为:“$(prefix)/include”。
    • mandir:安装该软件包的帮助文档的顶层目录。它的缺省值为:“/usr/local/man”,要求在使用时应写为:“$(prefix)/man”。
    • srcdir:此变量指定的目录是需要编译的源文件所在的目录。该变量的值在使用“configure”脚本对软件包进行配置时产生的。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 安装的普通目录路径前缀
    # 注意:该目录在开始安装前必须存在
    prefix = /usr/local
    exec_prefix = $(prefix)
    # 放置gcc命令使用的可执行程序
    bindir = $(exec_prefix)/bin
    # 编译器需要的目录
    libexecdir = $(exec_prefix)/libexec
    # 软件包的Info文件所在目录
    infodir = $(prefix)/info

10.4 Makefile 标准目标

所有的GNU程序必须在Makefiles文件中包含如下目标:

  • all

    此目标的动作是编译整个软件包。all应该是Makefile的终极目标。默认地,对所有源程序的编译和连接应该使用选项“-g”,所以最终的可执行程序中包含调试信息。

  • install

    编译程序并将可执行文件、库文件等拷贝到安装目录。如果有一个简单的测试程序去验证程序是否正确安装,则应该运行这个测试。

  • install-html install-dvi install-pdf install-ps

    这些目标安装除了info之外其他形式的文件;如果需要这种格式的文件,需要安装人员显式调用。GNU偏爱信息文件,所以必须由安装目标安装这些文件。安装这些文件,需要将它们安装在合适的子目录下。

  • uninstall

    删除所有已安装文件——由 install 创建的文件拷贝。规则所定义的命令不能修改编译目录下的文件,仅仅是删除安装目录下的文件。

  • install-strip

    和目标 install 的动作类似,但是 install-strip 指定的命令在安装时对可执行文件进行 strip(去掉程序内部的调试信息)。一般不建议安装时对可执行文件进行 strip,因为去掉可执行文件的调试信息后,如果在程序中存在 bug,就不能通过 gdb 对程序进行调试。

  • clean

    清除当前目录下编译生成的所有文件,这些文件在 make 过程中产生。注意,clean动作不能删除软件包的配置文件,同时也不能删除 build 时创建的那些文件(诸如:目录、build生成的信息记录文件等)。因为这些文件都是发布版本的一部分。

  • distclean

    类似于目标 clean,但增加删除当前目录下的的配置文件、build 过程产生的文件。目标“distclean”指定的删除命令应该删除软件包中所有非发布文件。

  • mostlyclean

    类似于目标“clean”,但是可保留一些编译生成的文件,避免在下次编译时对这些文件重建。例如,对于 gcc 来说,此目标指定的命令不删除文件“libgcc.a”,因为在绝大多数情况下它都不需要重新编译。

  • maintainer-clean

    此目标所定义的命令几乎会删除所有当前目录下能够由 Makefile 重建的文件。典型的,包括目标“distclean”删除的文件、由 Bison 生成的.c 源文件、tags 记录文件、Ifon 文件等。但是有一个例外,就是执行“make maintainer-clean”不能删除“configure”这个配置脚本文件,即使“configure”可以由 Makefile 生成。

  • TAGS

    此目标所定义的命令完成对该程序的 tags 记录文件的更新。tags 文件通常可被编辑器作为符号记录文件,例如 vim、Emacs 等。

  • info

    产生任何需要的Info文件,最好按照如下方式书写规则。

    1
    2
    3
    info: foo.info
    foo.info: foo.texi chap1.texi chap2.texi
    $(MAKEINFO) $(srcdir)/foo.texi

    必须在 Makefile 中定义变量“MAKEINFO”,代表命令工具 makeinfo,该工具是发布软件 Texinfo 的一部分。通常,GNU 的发布程序会和 Info 文档会被一同创建,这意味着 Info 文档是在源文件的目录下。用户在创建发布软件时,一般情况下,make不更新 Info 文档,因为它们已经更新到最新了。

  • dvi html pdf ps

    生成给定格式的文档。这些目标总是存在的,但是如果给定输出格式不能够生成,任何或者所有的都可以是no-op(无操作)。这些目标不应该是所有目标的依赖项,用户必须手动调用它们。

  • dist

    此目标指定的命令创建发布程序的 tar 文件。创建的 tar 文件应该是这个软件包的目录(即创建的 tar 文件在解包之后是一个目录),文件名中也可以包含版本号。例如,发布的 gcc 1.40 版的 tar 文件解包的目录为“gcc-1.40”。

    通常的做法是创建一个空目录,使用 ln 或 cp 将所需要的文件加入到这个目录中,之后对这个目录使用 tar 进行打包。打包之后的 tar 文件使用 gzip 压缩。例如,实际的 gcc 1.40 版的发布文件叫“gcc-1.40.tar.gz”。

  • check

    此目标指定的命令完成所有的自检功能。在执行检查之前,应确保所有程序已经被创建,可以不安装。

  • installcheck

    执行安装检查。在执行安装检查之前,确保所有程序已经被创建并且被安装。需要注意的是:安装目录“$(bindir)”是否在搜索路径中。

  • installdirs

    使用目标“installdirs”创建安装目录以及它的子目录,在很多场合是非常有用的。脚本“mkinstalldirs”就是为了实现这个目的而编写的;发布的 Texinfo 软件包中就包含了这个脚本文件。

    e.g.:

    1
    2
    3
    4
    5
    6
    # Make sure all installation directories (e.g. $(bindir))
    # actually exist by making them if necessary
    installdirs: mkinstalldirs
    $(srcdir)/mkinstalldirs $(bindir) $(datadir) \
    $(libdir) $(infodir) \
    $(mandir)

    或者,可以使用DESTDIR(强烈推荐):

    1
    2
    3
    4
    5
    6
    7
    # Make sure all installation directories (e.g. $(bindir))
    # actually exist by making them if necessary.
    installdirs: mkinstalldirs
    $(srcdir)/mkinstalldirs \
    $(DESTDIR)$(bindir) $(DESTDIR)$(datadir) \
    $(DESTDIR)$(libdir) $(DESTDIR)$(infodir) \
    $(DESTDIR)$(mandir)

    该规则不能更改软件的编译目录,仅仅是创建程序的安装目录。

11. 其他

  • ARFLAGS

    存档维护程序的标志,默认是“rv”。

  • abspath函数

    用法:$(abspath _names)

    该函数将_names中的各路径转换成绝对路径,并将结果返回

  • realpath函数

    用法:$(realpath _names)

    用于获取_names所对应的真实路径。

  • $(shell XXX)

    在makefile中直接使用shell指令“XXX”。e.g.:

    1
    UNAME_S = $(shell uname -s)