Managing Projects with GNU make 学习笔记

7 篇文章 0 订阅
1. 简介
makefile定义了一种语言来描述源代码、中间文件及可执行文件之间的关系。
如果命令行指定了目标,则更新指定的目标,如果没有,则取第一个目标,也即默认目标。

1.1 目标与依赖

makefile包含构造程序的一组规则,规则包含三个部分:目标、依赖及执行命令。

	target: prereq1 prereq2
		commands

target是需要构造的东西,依赖则是在创建target前必须存在的东西。

commands为将依赖生成为target的shell命令。

1.2 依赖检查
对于-l<NAME>的依赖,make有特殊的支持,它表示依赖库文件。
make会查找libNAME.so,如果找不到,则查找libNAME.a。

1.3 减少重编
文件修改时,只会影响相关的编译,整个项目一般不会从头构造
1.4 调用make
make target 更新指定的target。如果目标是最新的,则什么也不做。
如果指定的target在makefile里不存在,也没有隐含规则,则提示无规则。
make有很多命令行参数,最有用的是 --just-print (或 -n)
它使make显示编译目标使用的命令,但不实际执行。
命令行的变量可以覆盖makefile里的变量。

1.5 makefile基本语法
更完整的形式(不是最完整的):

	target1 target2 target3: prerequisite1 prerequisite2
		command1
		command2
		command3

target可以有多个,依赖可以没有。如果没有依赖,则只有不存在的target会更新。
command必须以tab开始。make另启动子shell来执行command。
长的行可以用反斜杠分隔,依赖也可以使用。

2. 规则

规则有不同种类

 * 显式规则,明确指定目标和依赖
 * 模式规则,使用通配符
 * 隐式规则,makefile内置的模式规则或者扩展名规则

2.1 显式规则

大多数规则是显式规则。规则可以有多个目标,表示每个目标的依赖是相同的。

	target1 target2: prereq

等价于

	target1: prereq
	target2: prereq

规则不需要一次写全,make每次搜索目标文件时,会将目标和依赖加到依赖关系图中。
如果目标在依赖关系图中已存在,则将依赖添加到图中。

	vpath.o: vpath.c make.h config.h getopt.h gettext.h dep.h
	vpath.o: filedef.h hash.h job.h commands.h variable.h vpath.h

2.1.1 通配

make的通配符与bash一样:~, * , ?, [...], [^...]

*.*表示所有包含.的文件,?表示任意一个字符,[...]表示一组字符,选择相反的字符集用[^...]

~用于表示当前用户的home目录,~user表示那个用户user的home目录。

目标或是依赖里的通配由make处理,而命令里的通配由shell处理。
而make在读取makefile时会立即展开通配符。

2.1.2 Phony目标
不代表文件的目标即为phony目标,比如all和clean。
一般情况下,phony总会执行,因为相关的命令不产生目标名。

但是make无法区分文件目标和phony目标,假如一个phony目标恰好对应了一个文件名,make会把假的目标加到依赖图里。为了避免该问题,GNU make引入了一个特殊的目标:.PHONY来告诉make这个目标不是一个真实文件,可以用它来修饰任何一个伪目标,形式如下:

    .PHONY: clean
    clean:
        command

这样,即使存在一个叫clean的文件,make也会执行clean目标的命令。

将phony目标作为真实文件的依赖没有多大意义,因为phony总是旧的,这会导致目标文件重新生成。常用的做法是将phony作为目标。

按习惯,有一些标准的phony目标:

 * all:执行所有任务构建程序
 * install:安装程序
 * clean:删除从源码生成的二进制文件
 * distclean:删除不在源码发布范围内的所有产生文件
 * TAGS:创建tag表给编辑器使用
 * info:从Texinfo源码创建GNU info文件
 * check:运行与程序相关的测试

TAGS不是一个phony目标,因为ctags和etags的输出就是一个TAGS文件。

2.1.3 空目标
空目标与phony目标类似,phony target总是过时的,导至其依赖被重新生成。有时有些命令没有输出文件,希望只在有些情况下去执行,并且不想更新依赖,此时可以创建一个空文件的规则(也称为cookie):
prog: size prog.o
	$(CC) $(LDFLAGS) -o $@ $^
size: prog.o
	size $^
	touch size
在这例里,size规则用touch生成了一个文件名为size的空文件,这个空文件充当了一个时间戳的功能。只有在prog.o更新时,size规则才会执行。除非prog.o更新了,否则size规则不会导致prog被重编。也就是说这个规则在prog.o更新prog时执行,起到了特定条件下执行命令的目的。

空文件与自动变量$?结合时特别有用。$?表示比目标新的依赖集合。

    print: *.[hc]
        lpr $?
        touch $@

这个例子打印自上次执行print以来,变化过的文件。

通常,空文件是用来标记一个特定的时间。

2.2 变量
变量最简单的形式:

$(variable-name)


表示希望展开名为variable-name的变量。变量名需要用$()引用,但单字符变量可以省去括号。
makefile有自定义的一些变量。

2.2.1 自动变量
当规则匹配时,make会设置自动变量,它们提供了目标和依赖的清单,避免显式指定文件名,代码重复,且对通用的模式规则很重要。

有6个重要的自动变量:

$@
表示target的文件名

$%
the filename element of an archive member specification.
可能跟库有关系。

$<
第一个依赖的文件名

$?
比目标新的所有依赖

$^
所有依赖的文件名(会去掉重复项)

$+
与$^类似,但包含重复项

$*
目标文件名的根。根一般是不含后缀名的文件名。不建议在pattern rule之外使用。

自动变量只有在规则匹配时才会生成,因此只有在命令区才可以使用。

2.3 用VPATH和vpath查找文件

make只会在当前目录查找源文件

VPATH = src

告诉make,当前目录找不到文件时,到src里去找

VPATH变量包含了一个路径列表,make需要一个文件时就从这个列表里找。这个只对目标和依赖有效,命令区里的文件名是不用的。

VPATH有缺点:路径多的时候,效率会低。不同路径下有重名时,只会取第一个,vpath更好一些。

vpath pattern directory-list

前面的VPATH可以改写为:

vpath %.c src
vpath %.h include

vpath是否也存在重名的问题呢?

2.4 模式规则
make的内置规则都是模式规则,除了文件的根以%替代外,与普通规则无异(根是指后缀之前的部分)。可通过make --print-data-base查看make的内置规则。

可以修改内置规则的命令区。

2.4.1 模式
模式规则里,%基本上与unix shell的* 相同,代表任意数量的任意字符。%可以放在任意位置,但只能出现一次。
除%外的字符则用于匹配文件名。模式可以包含前缀、后缀和前后缀。
当make查找模式规则时,首先查找可以匹配的target。如果找到,在前缀和后缀之间的部分将作为名字的根。然后make查找对应的依赖,将根替换到依赖模式里。根至少包含一个字符。
模式可以只包含%,一般这种用法是生成一个可执行程序。

2.4.2 静态模式规则
静态模式规则是指只对特定目标生效的规则,如

$(OBJECTS): %.o: %.c
	$(CC) -c $(CFLAGS) $< -o $@

与普通的模式规则不同的只有前面的$(OBJECTS):限定,它指定该规则只对$(OBJECTS)变量列出的目标生效。

2.4.3 后缀规则
后缀规则是早期的(已经过时的)定义隐含规则的方式。其它版本的makefile不支持GNU make的模式规则,仍能在其它makefile里见到。

后缀规则包含一个或多个串接的后缀,用作目标:

.c.o:
	$(COMPILE.c) $(OUTPUT_OPTION) $<

这里的依赖在前,目标在后,它等价于:

%.o: %.c
	$(COMPILE.c) $(OUTPUT_OPTION) $<

将目标的后缀替换为依赖的后缀得到依赖,在有后缀名在已知清单里时,make才能识别。将后缀加入已知清单,通过特殊的目标.SUFFIXES实现,如:

.SUFFIXES: .out .a .ln .o .c .cc .C .cpp .p .f .F .r .y .l

要删除所有已知后缀,不指定依赖即可:

.SUFFIXES:


2.5 隐含规则数据库
GNU make 3.80有约90条内置隐含规则。隐含规则是模式规则或者后缀规则。

2.5.1 使用隐含规则
当一个目标没有明确规则更新时,make会使用隐含规则。
当隐含规则不是所需要的时候,可以删除它们,比如:

%.o: %.l
%.c : %.l

没有命令区的模式从make的数据库中删除该规则。

由链式规则生成的文件称为中间文件,make对其处理不同。由于中间文件不出现在目标中(出现在目标中的不称为中间文件),make不会简单地更新中间文件,make创建中间文件是更新目标的一种副效应,因此make在退出前会删除中间文件。

2.5.2 规则结构
下面是一条内置规则:

%.o: %.c
	$(COMPILE.c) $(OUTPUT_OPTION) $<

定制这条规则可完全由它使用的一组变量来控制。COMPILE.c实质是一组变量的集合:

COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
CC = gcc
OUTPUT_OPTION = -o $@

改变CC变量可以更换C编译器,其它变量设置编译选项。

2.5.3 用于源码控制的隐含规则
make支持两种源码控制系统:RCS和SCCS。

2.5.4 一个简单的Help命令
没什么好说的

2.6 特殊目标
特殊目标是内置的phony目标,用来改变make的默认行为。比如.PHONY这个特殊目标,表示它的依赖不指向实际文件,并且总是过时的。除.PHONY外,还有其它的特殊目标。
一共有十二个特殊目标。

.INTERMEDIATE

该特殊目标的依赖是中间文件。如果make在更新其它目标时创建了这个文件,在make退出时会自动删除它。如果make在更新时文件已存在,则不会删除。
在定制链式规则时比较有用。

.SECONDARY
与INTERMEDIATE相同,但不会自动删除。

.PRECIOUS
当make在执行过种中被中断时,它可能会删除正在更新的目标文件,因为make不希望保留部分构建的文件。将文件标记为precious,make中断时不会删除它。
注意,当规则的命令产生错误时,make不会自动删除文件,这个只在被信号中断时出现。

.DELETE_ON_ERROR
与precious相反,当命令执行出错时,make会删除这些文件。

其它的特殊目标在涉及到的地方再谈论。

2.7 自动依赖生成
gcc有一个选项可以读取源文件并生成makefile依赖

gcc -M source.c

这个把戏就是用gcc -M遍历所有源文件,将结果写到一个依赖文件里,然后用include把它包含进来。

gcc -M 生成的结果一般需要修改一下,形成.o .d: 头文件清单这样的形式。

2.8 管理库
归档库,是一种特殊类型的包含其它文件的文件。库用来将相关的object文件组合到一起。

库可以有几种方式链到可执行文件中,最直接的方式是在命令行中指定,比如:

cc count_words.o libcounter.a /lib/libfl.a -o count_words

cc识别libcounter.a和/lib/libfl.a为库文件。

另一种方式是通过-l选项:

cc count_words.o -lcounter -lfl -o count_words

这个选项可以省略库文件名的前缀和后缀。-l使命令行更简单,而且不需要关心库的位置。cc从系统的标准库目录查找-l列出的库。而且,支持动态链接库的系统,链接器先查找共享库,找不到了再找静态库。
编译器的查找路径可以通过-L选项添加。

2.8.1 创建及更新库
没什么特别的

2.8.2 使用库作为依赖
库作为依赖时,可以用文件名,也可以用-l的方式。

对-l格式的库文件名解析模式存放在.LIBPATTERNS中,可以定制支持其它库文件名格式。

如果目标是一个库文件,则不能使用-l格式的依赖。因此对makefile内生成的库,必须指定文件名。

库的依赖关系很重要,库是有顺序的。如果出现循环依赖,则需要重复指定。然而,自动变量一般会丢掉重复项,此时,就需要用$+而不是$^了。

2.8.2 双冒号规则
过时的特性。

3. 变量和宏
变量名区分大小写。

要取用一个变量的值,用$()的形式。单符号变量可以省略()。

变量也可以用${}的形式,主要是早期的makefile用。

按惯例,代表常量的变量用大写,仅在makefile内使用的变量用小写,单词间以下划线分隔。用户定义的函数用小写,以破折号分隔。

3.1 变量有什么用
用变量来表示外部程序是个好主意。

3.2 变量类型
make有两类变量:简单扩展变量和递归扩展变量。

简单扩展变量由:=定义:

MAKE_DEPEND := $(CC) -M

称为简单变量是因为make在读到这一行后,右侧会被立即展开,右侧的所有变量引用都会展开,其展开结果为变量的值。假如上面的CC没有设置,那么上面的赋值成为<space>-M

变量没有定义不是一个错误。

第二种变量为递归扩展变量,使用=赋值:

MAKE_DEPEND = $(CC) -M

称其为递归扩展是因为右侧不会被求值。只有当该变量被使用时,扩展才会发生。

因为展开的滞后,赋值可以是乱序的,比如先定义MAKE_DEPEND,再定义CC。

递归变量每次被使用时,右侧都会重新求值。比如右侧包含date命令时,重新计算会保证每次使用都代表着执行时的值。

3.2.1 其它类型的赋值
除:=和=外,还有两种赋值类型。

?=是条件赋值,它只有在变量没有值的时候才会赋值。

OUTPUT_DIR ?= $(PROJECT_DIR)/out

这个功能与环境变量一起使用非常方便。

+=: 将文本添加到一个变量

3.3 宏
宏只是另一种方式的变量定义,它可以包含换行。

define 变量名
...
endef

命令行前面的@表示不回显命令,@用在宏前面,则对整个宏生效。

3.4 变量何时扩展

make运行有两个阶段。第一个阶段,make读入mkakefile及包含的makefile,此时,变量和规则加载到make的内部数据库,并生成依赖图。第二阶段,make分析依赖图来确定需要更新的目标,然后执行相应的命令来更新。

当make遇到一个递归变量或者define标志符时,它们的内容都原封不动地存下来。

当展开一个宏时,make会扫描展开的文本,并展开存在的变量引用和宏。如果宏是在action区间展开,则宏内的每一行前面会插入一个TAB。

总结如下:
 * 对于变量赋值,当make在第一阶段读取到这一行时,赋值左侧总是立即扩展(是指左侧的变量)。
 * =和?=的右侧在第二阶段使用时才会扩展。
 * :=的右侧立即展开。
 * 如果+=的左侧是一个简单变量,则右侧立即展开。否则推迟求值。
 * 对于宏定义,宏名立即展开,但宏体推迟到使用时展开。
 * 对于规则,目标和依赖总是立即展开,命令则推迟展开。
一个通用的规则是,在使用变量和宏之前定义他们。

3.5 目标/模式特定的变量

make提供了目标特定变量,也就是与一个目标绑定的变量定义,只有在处理这个目标及它的依赖时才有效。例如

gui.o: CPPFLAGS += -DUSE_NEW_MALLOC=1

只有在编译gui.o这个目标时,CPPFLAGS才包含-DUSE_NEW_MALLOC=1,在这个目标完成后,CPPFLAGS恢复到原始值。
目标特定变量的一般格式是:

target...: variable = value
target...: variable := value
target...: variable += value
target...: variable ?= value

变量在赋值前可以是不存在的。

3.6 变量从何处来
到目前为止,大多数变量是直接在makefile里定义的。但变量可以有下面一些来源:

文件: 变量定义在makefile或者文件,然后由makefile用include引入。

命令行: 变量可以在命令行定义。命令行的变量覆盖环境变量及makefile里的赋值。可以用override关键字来覆盖命令行的赋值。

环境变量: 当make启动时,环境变量的所有变量会自动定义为make变量,这些变量优先级很低,如果makefile或命令行参数指定了环境变量,会被覆盖。可以用--environment-overrides参数来产生相反的结果。
当递归执行make时,父make的一些变量会传给子make,默认情况下,只有环境变量会导出到子进程的环境,但可以用export将变量导出到环境。不带变量的export会导出所有变量。
可以用unexport阻止环境变量导出到子进程。

自动变量: 不用说了

传统上,环境变量用于区分不同的机器。

3.7 条件处理和包含处理
基本的条件处理形式如下:

    if-condition
        text if the condition is true
    endif

或者

    if-condition
        text if the condition is true
    else
        text if the condition is false
    endif

if-condition可以是下面的形式:

ifdef variable-name
ifndef variable-name
ifeq test
ifneq test

ifdef/ifndef测试中,variable-name不能用$()的形式。

test的形式可以为:"a" "b"    或者  (a,b)

单引双引均可。

条件处理可以用在宏定义里,命令脚本区域里。

ifeq/ifneq测试入参是否相等,白字符需要注意,当使用()形式的测试时,只有逗号后面的白字符是忽略的,但其它白字符都是有效的。

    ifeq (a, a)
     # these are equal
    endif
    ifeq ( b, b )
     # these are not equal  ' b' != 'b '
    endif

有时,变量扩展后会出现不想要的白字符,影响条件判断,可以用strip函数处理。

    ifeq "$(strip $(OPTIONS)) "-d"
      COMPILATION_FLAGS += -DDEBUG
    endif

3.7.1 include关键字
一个makefile可以包含其它文件,用include关键字,入参可以是任意数量的文件,通配符,也支持make变量。

3.7.2 include及依赖
当make遇到include时,展开通配符及变量引用,然后读入包含的文件,如果文件存在,则正常继续,如果不存在,make会报告,并继续读取剩下的makefile。
在所有文件读完后,make查找规则数据库,看是否有规则来更新包含文件(前面缺失的文件)。如果找到更新规则,则执行与普通的目标更新一样的过程。

如果include的文件是有规则来更新的,make会清空内部的数据库,并重读整个makefile(使用include的这个makefile)。如果在读取,更新,再读取之后,仍有include找不到文件,make报错并结束。

make把makefile也当作一个可能的目标,在整个makefile读入后,make会查找是否有规则来更新当前执行的makefile,如果有的话则处理这个规则,并检查makefile是否被更新,如果更新了,则会清空内部状态,并重新读取这个makefile。

make从哪里寻找include文件呢,绝对路径和相对路径。如果找不到,则查找--include-dir命令行参数指定的路径,然后查找编译内置的一些路径。
如果想让make忽略找不到的include文件,可以在include前面加减号(sinclude是-include的一个兼容方法)。

3.8 标准make变量

MAKE_VERSION     make版本号
CURDIR           当前工作目录
MAKEFILE_LIST    make已经读取的的makefile的清单
MAKECMDGOALS     当前make进程命令行指定的所有目标
.VARIABLES       当前已在makefile里定义的变量名的列表(除了目标特定变量),该变量只读。

4. 函数
函数引用就像变量引用一样,但包括一个或多个由逗号分隔的参数。

4.1 用户定义的函数
函数其实就是带参数的宏。宏体内使用$1, $2等来引用参数。

展开参数或宏的语法为:

$(call macro-name[, param1 …])

call是一个内置的make函数,将参数替换成$1, $2等。macro-name是宏或者变量的名字(宏只是允许换行的变量)

如果宏内引用了$n,而入参不够,则为空,如果入参多于引用,则多余部分不在宏内扩展。

4.2 内置函数
内置函数分为几类:字符串处理,文件名处理,流程控制,用户定义函数及其它一些函数。

函数的形式如下:

$(function-name arg1[,argn])

第一个入参的白字符会去掉,但后续参数前的白字符会保留(所以要注意空格问题)
有许多函数也支持模式作为入参。

4.2.1 字符串函数

$(filter pattern …,text)
filter返回text中与pattern匹配的内容。text是一个以空白符分隔的单词序列。

$(filter-out pattern …,text)
与filter作用相反,选择text中不匹配pattern的内容。

$(findstring string,text)
在text中查找string,如果找到,则返回string(是string,不是包含string的字符串),否则返回空。string不接受通配符。

$(subst search-string,replace-string,text)
简单的非通配的查找与替换。通常是用它来将某种后缀替换成另一种。比如:

sources := count_words.c counter.c lexer.c
objects := $(subst .c, .o, $(sources))

subst并不知道文件名及后缀,只是简单的字符串替换,因此如果文件名里有.c的话,也会被替换掉。

$(patsubst search-pattern,replace-pattern,text)
通配版本的查找与替换。pattern可以包含一个%。
另一种可移植的替换形式是:

$(variable:search=replace)

这种形式,原变量会被修改吗?会

$(words text)
返回text中的单词个数

$(word n,text)
返回text的第n个单词,从1开始编号。如果n大于单词数,则结果为空。例,取得列表的最后一项:

current := $(word $(words $(MAKEFILE_LIST)), $(MAKEFILE_LIST))

$(firstword text)
返回text的第一个单词,等价于$(word 1,text)

$(wordlist start,end,text)
返回text从start到end的单词(含边界)

4.2.2 重要的辅助函数

$(sort list)
排序,并去掉重复项。词典序。

$(shell command)
将命令传给子shell进程执行,将命令的标准输出读回并作为函数的返回值。不返回标准错误输出,也不返回程序退出状态。

4.2.3 文件名函数

($wildcard pattern …)
wildcard接受一组模式并展开,如果模式找不到匹配文件,则返回空字符串。
模式里可以用shell的通配符:~, * , ?, […], [^...]

$(dir list …)
返回list每个单词的目录部分。

$(notdir name …)
返回文件路径的文件名部分。

$(suffix name …)
返回参数中每个单词的后缀。常见用法是与findstring结合做条件处理。

$(basename name …)
返回文件名的文件名部分,仅去掉后缀,路径部分不变。

$(addsuffix suffix,name …)
给name里的每个单词附加后缀。

$(addprefix prefix,name …)
给name里的每个单词附加前缀。

$(join prefix-list,suffix-list)
是dir和notdir的补运算。join接受两个list,并将prefix-list的元素与suffix-list的元素串接,一一对应,可以用来重组被dir与notdir分开的部分。

4.2.4 流程控制

$(if condition,then-part,else-part)
根据condition的情况,展开第一个或第二个宏。
condition只要包含字符(包括空格),就为真,此时执行then-part,否则执行else-part。

$(error text)
打印严重错误信息,make终止。

$(foreach variable,list,body)
遍历处理

4.2.5 次常用函数

$(strip text)
移除text的前后白字符,并将内部所有白字符替换为一个空格。

$(origin variable)
返回一个描述变量的来源的字符串。有下面一些来源:

undefined  变量没有定义过
default  来自make的内置数据库,如果内置变量被覆盖,则返回最近一次的定义
environment  来自环境,且--environment-overrides未打开
environment override 来自环境,且--environment-overrides已打开
file  来自makefile
command line  来自命令行
override  来自override关键字
automatic  自动变量


$(warning text)
类似error,但make不会退出。可以用在任意地方,不需要放在规则里,有效的调试方法。

4.3 高级用户定义函数

call一个函数时,其入参转化为$1, $2等,而函数名则可通过$0得到。
可以利用它来打印函数调用情况。例子见书。

5. 命令

5.1 命令解析

命令以TAB起始,下面的错误表示make在target上下文之外遇到了命令:

makefile:20: *** commands commence before first target.  Stop.

当make parser在合法的上下文中看到一个命令时,切换到命令解析模式,每次构造一行脚本,命令脚本里可以出现的有:
 * 以TAB开头的命令,开辟子shell执行。包括ifdef, 注释, include等,在命令解析模式下都当作命令。
 * 忽略空行,不会传给子shell
 * 忽略以#开头的行(包括空格,但不是TAB)
 * 条件处理关键字,如ifdef, ifeq可以识别,并在脚本区执行。

5.1.1 长命令接续

用 \ 接续长命令。shell上需要加分号的要加分号分隔。

5.1.2 命令修辞

@
命令不回显

QUIET = @
hairy_script:
	$(QUIET) complex script

这种方式比较适合调试。

- (减号)
忽略出错命令

+
执行命令(不管--just-print是否指定)
用在递归makefile中。

5.1.3 错误和中断

make执行的每条命令都返回一个状态码,0表示命令成功,非0表示某种错误。一般程序返回非0值时,make停止,如果想让make忽略继续,可以用--keep-going (-k) 选项。减号也可以达到这个目的,但不建议使用,因为它会使自动错误处理更复杂。

5.1.3.1 删除和保留目标文件

因为历史原因,make在未能更新目标时,这个目标仍会保留,因为它的时间戳已经变了,是后续命令未能形成完整的数据。如果希望不完整的目标文件,可以将其作为.DELETE_ON_ERROR的依赖。如果.DELETE_ON_ERROR没有依赖,则所有目标文件的都会在出错时删除。

另一个问题就是在Ctrl+C时,make会中断,如果当前的目标文件被修改,make会将其删除。有时需要保留,用.PRECIOUS。

5.2 使用哪个SHELL

make需要传递命令给子shell执行时,使用/bin/sh。可以通过make变量SHELL改变。

5.3 空命令
空命令什么也不做:

header.h: ;


5.4 命令环境

make执行的命令继续了make自己的环境,包括当前工作目录,文件描述符及make传递的环境变量。创建subshell时,make还向环境添加了几个变量:

MAKEFLAGS
包含了传给make的命令行配置。

MFLAGS
与MAKEFLAGS一样,历史原因保留。

MAKELEVEL
make的嵌套层数。

5.5 命令求值

命令直到执行的时候求值,但ifdef前导符则在使用点立即求值。
当要执行一个命令脚本时,make先扫描脚本查找需要展开和求值的make部件。

5.6 命令行限制
不同操作系统下,对命令行长度的限制不同。
解决办法就是分解。



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
make是unix和其他操作系统上最持久的工具之一。自1970年问世以来,make至今仍旧是大多数程序开发项目的核心工具,它甚至被用来编译linux内核。阅读本书,读者将可以了解,尽管出现了许多新兴的竞争者为何make仍旧是开发项目中编译软件的首选工具。. 简单就是make欲达成的目标:在你变更源代码文件之后,想要重编译你的程序或其他输出文件之际,make会检查时间戳,找出被变更的文件并进行必要的重编译动作,因此不会浪费时间去重编译其他文件。为了达到这个目标,make提供了许多选项让你能够操作多个目录、为不同的平台编译不同版本的程序以及自定义编译方法。.. 本书第三版的重点是gnu make,这个版本的make已经成为行业标准。本书将会探索gnu make所提供的强大扩充功能。gnu make之所以广受欢迎是因为它是一个自由软件,并且几乎可以在包括微软windows(作为cygwin项目的一部分)的每个平台上使用 *************************************************************** 请注意: 下载完,评论的同时,请点击评论框上方的五角星(共5个五角星),这样你的被扣的积分就可以返还了。 如果只评论,不点击小五角星,积分不会返还。 一定要先下载完,再评论。如果先评论后下载,或者在下载的过程中评论,积分同样不会返还。 *************************************************************** 更多linux、ARM和C语言资源请参考: http://blog.csdn.net/arkofnoach/archive/2010/10/23/5960560.aspx
The utility simply known as make is one of the most enduring features of both Unix and other operating systems. First invented in the 1970s, make still turns up to this day as the central engine in most programming projects; it even builds the Linux kernel. In the third edition of the classic Managing Projects with GNU make, readers will learn why this utility continues to hold its top position in project build software, despite many younger competitors.The premise behind make is simple: after you change source files and want to rebuild your program or other output files, make checks timestamps to see what has changed and rebuilds just what you need, without wasting time rebuilding other files. But on top of this simple principle, make layers a rich collection of options that lets you manipulate multiple directories, build different versions of programs for different platforms, and customize your builds in other ways.This edition focuses on the GNU version of make, which has deservedly become the industry standard. GNU make contains powerful extensions that are explored in this book. It is also popular because it is free software and provides a version for almost every platform, including a version for Microsoft Windows as part of the free Cygwin project. Managing Projects with GNU make, 3rd Edition provides guidelines on meeting the needs of large, modern projects. Also added are a number of interesting advanced topics such as portability, parallelism, and use with Java.Robert Mecklenburg, author of the third edition, has used make for decades with a variety of platforms and languages. In this book he zealously lays forth how to get your builds to be as efficient as possible, reduce maintenance, avoid errors, and thoroughly understand what make is doing. Chapters on C++ and Java provide makefile entries optimized for projects in those languages. The author even includes a discussion of the makefile used to build the book.
GNU Make 是一个广泛使用的项目管理工具,它可以帮助我们自动化构建和管理项目。它的核心思想是基于规则来定义项目的构建过程。 首先,我们需要在项目的根目录中创建一个名为“Makefile”的文件,用于定义项目的规则和依赖关系。Makefile 是用来告诉 GNU Make 如何构建和更新项目的文件,它由一系列规则组成。 每个规则由一个目标(target)和相应的依赖列表组成。目标是指我们希望生成的文件或执行的操作,而依赖列表则表示生成目标所需要的文件或操作。当某个目标的依赖发生变化时,GNU Make 将会自动检测并更新相应的目标。 在 Makefile 中,我们可以使用一些预定义的变量来简化配置,如 CC 表示编译器,CFLAGS 表示编译选项等。我们还可以定义自己的变量,以便在规则中使用。 通过定义规则和依赖关系,我们可以利用 GNU Make 来自动构建项目。当我们运行 make 命令时,GNU Make 将会读取 Makefile,并根据规则和依赖关系来判断哪些目标需要重新构建,然后执行相应的命令。 GNU Make 还支持一些高级特性,如条件判断、循环、递归等,这使得我们可以根据不同的情况来定义不同的规则和行为。 总之,GNU Make 是一个强大而灵活的项目管理工具,它允许我们根据项目的需求来定义规则和依赖关系,并自动化构建过程,提高项目的开发效率和可维护性。无论是小型项目还是大型项目,GNU Make 都是一个极为有用的工具。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值