makefile是为工程组织编译,为“自动化编译”,一旦写成,只须要一个make命令,整个工程彻底自动编译,极大提升了软件开发效率。make是一个命令工具,是一个解释makefile中指令的命令工具。通常来讲,大多数IDE都有这个命令,如Delphi的make,Visual C++的nmake,linux下GNU的make。可见,Makefile都成为一种在工程方面的编译方法。
一个Makefile告诉make命令如何编译和连接文件,规则是:
1) 若是这个工程没有编译过,那么咱们的全部C文件都要编译并被连接。
2) 若是这个工程的某几个C文件被修改,那么咱们只编译被修改的C文件,并连接目标程序。
3) 若是这个工程的头文件被改变了,那么咱们须要编译引用了这几个头文件的 C 文件,并连接目标程序。
只要咱们的 Makefile 写得够好,全部的这一切,咱们只用一个 make 命令就能够完成,make 命令会自动智能地根据当前的文件修改的状况来肯定哪些文件须要重编译,从而本身编译所须要的文件和连接目标程序。html
通常来讲,不管是C仍是C++,首先要把源文件编译成中间代码文件,在Windows下也就是 .obj 文件,UNIX下是 .o 文件,即 Object File,这个动做叫作编译(compile),通常来讲,每一个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。而后再把大量的Object File合成执行文件,这个动做叫做连接(link)。
编译时,编译器须要的是语法的正确,函数与变量的声明的正确。对于后者,一般是你须要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要全部的语法正确,编译器就能够编译出中间目标文件。
连接时,主要是连接函数和全局变量,因此,咱们可使用这些中间目标文件(O文件或是OBJ文件)来 连接咱们的应用程序。连接器并无论函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,因为源文件太多,编译生成的中间目标文件太多,而在连接时须要明显地指出中间目标文件名,这对于编译很不方便,因此,咱们要给 中间目标文件打个包,在Windows下这种包叫“库文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。linux
target …: prerequisites…
command
make的书写规则包含两部分,一个是依赖关系,另外一个是生成目标的方法。
prerequisites中若是有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则,也就是Makefile中最核心的内容。
makefile文件中只应该有一个最终目标,其余的目标都是被这个目标所连带出来的。通常来讲,定义在makefile文件中的目标会不少,但第一条规则中的目标将被肯定为最终目标。
每一个Makefile都应该写一个清空目标文件的规则(.o和执行文件),这不只便于重编译,也有利于保持文件的清洁。这是一个“修养”。
.PHONY: clean
clean:
-rm edit $(objects)shell
隐含规则1:倚赖关系中有FORCE编程
download: .config FORCEide
FORCE:
.PHONY: FORCE函数
FORCE总会被认为是最新的,这样当FORCE做为其余规则的倚赖时,倚赖总被认为更新过,规则中定义的命令总会被执行。工具
1) 读入全部makefile文件;
2) 读入被include包括的其余makefile文件;
3) 初始化文件中变量;
4) 推导隐式规则,并分析全部规则;
5) 为全部目标文件建立依赖关系链;
6) 根据依赖关系,决定哪些目标要从新生成;
7) 执行生成命令;
其中,1)-5)为第一阶段,6)-7)为第二阶段。ui
注:倚赖关系必须先存在,而后才能执行命令,即倚赖关系先执行,而后生产目标的命令才能执行。spa
规则中的命令同OS中Shell命令行是一致的。每条命令必须以Tab键开头,除非命令紧跟在依赖关系分号后。命令行之间的空格或空行会被忽略,但空格或空行以Tab键开头的,make会认为是一个空命令,除非指定一个其余的shell。
1. 显示命令
命令行前加“@”字符时,这个命令不被make显示出来。
1) 若是执行make时,带入make参数-n或--just-print,其只显示命令,而不执行命令。
2) make参数-s或--slient全面禁止命令显示。.net
2. 执行命令
若是要让一条命令的结果应用在下一条命令上,应使用分号分隔两条命令(两命令同行)。
3. 命令出错
忽略错误执行:
1) 命令行前加“-”;
2) make加参数-i或--ignore-errors;
3) make加参数-k或--keep-going(终止此规则执行,继续执行其余规则)。
4. 嵌套执行make
不一样模块在不一样目录中,每一个目录都书写一个该目录的makefile文件。
例:有一个子目录subdir,这个目录下有个makefile文件来指明这个目录下文件编译规则。总控makefile能够这样写:
subsystem:
cd subdir && $(MAKE)
<==>
subsystem:
$(MAKE) –C subdir
定义($(MAKE))宏变量是由于,也许make须要一些参数,因此定义成一个变量比较利于维护。
dirs :=multfile/src multfile/sub \ top/src all: $(foreach N, $(dirs),make -C $(N);) clean: $(foreach N, $(dirs),make clean -C $(N);)
若是你要传递变量到下级Makefile中,那么你可使用这样的声明:
export <variable ...>
若是你不想让某些变量传递到下级Makefile中,那么你能够这样声明:
unexport <variable ...>
如果你要传递全部的变量,那么只要一个export就好了。后面什么也不用跟,表示传递全部的变量。
要注意的是,有两个变量,一个是SHELL,一个是MAKEFLAGS,这两个变量无论你是否export,其老是要传递到下层Makefile中,特别是MAKEFILES变量,其中包含了make的参数信息,若是咱们执行“总控Makefile”时有make参数或是在上层Makefile中定义了这个变量, 那么MAKEFILES变量将会是这些参数, 并会传递到下层Makefile中, 这是一个系统级的环境变量。
可是make命令中的有几个参数并不往下传递,它们是“-C”,“-f”,“-h” “-o”和“-W”,若是你不想往下层传递参数,那么,你能够这样来:
subsystem:
cd subdir && $(MAKE) MAKEFLAGS=
若是你定义了环境变量MAKEFLAGS,那么你得确信其中的选项是你们都会用到的,若是其中有“-t”,“-n”,和“-q”参数,那么将会有让你意想不到的结果,或许会让你异常地恐慌。
5. 定义命令包
若是Makefile中出现一些相同命令序列,那么咱们能够为这些相同的命令序列定义一个变量。定义这种命令序列的语法以“define”开始,以“endef”结束。
define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef
这里,“ run-yacc”是这个命令包的名字,其不要和 Makefile 中的变量重名。在“define”和“endef”中的两行就是命令序列。
make支持3种通配符:“*”,“?”和“[…]”。 “\*”用转义字符“\”来显示“*”。
* 表示任意一个或多个字符
? 表示任意一个字符
[…] [abcd]表示a,b,c,d中任意一个字符,[^abcd]表示除a,b,c,d之外的字符,[0-9]表示0~9中任意一个数字
~ 表示用户的home目录
在Makefile规则中,通配符会被自动展开。但在变量的定义和函数引用时,通配符将失效。这种状况若是须要通配符有效,就须要使用函数wildcard。
变量声明时赋初值,使用时,须要在变量名前加上“$”符号,但最好用“()”或“{}”括起来。如使用真实的$要用“$$”来表示。
定义变量值时,能够用其余变量来构造变量的值,有两种方式:
1)= 左变量右侧值
右侧值可定义在赋值前也可后。
2):= 赋值时右侧变量必须先定义
要定义一变量,其值是一个空格,能够
nullstring :=
space := $(nullstring) #end
而dir: = /foo/bar #end表明四个空格
3)?=
如:FOO ?= bar
若FOO没有定义过,变量FOO值就是bar;
若FOO先前被定义,则这条语句什么也不作。
4)+= 追加变量值
若是变量以前没有定义过,“+=”会自动变成“=”,若定义过,“+=”会继承前一次操做的赋值。
5)变量的高级用法
这里介绍两种变量的高级使用方法,第一种是变量值的替换。咱们能够替换变量中的共有的部分,其格式是“$(var:a=b)”或是“${var:a=b}”,其意思是,把变量“var”中全部以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符”。另一种变量替换的技术是以“静态模式”定义的,如:
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
这依赖于被替换字串中的有相同的模式,模式中必须包含一个“%”字符,这个例子一样让$(bar)变量的值为“a.c b.c c.c”。
第二种高级用法是——“把变量的值再当成变量”。
x = y
y = z
a := $($(x))
在这个例子中,$(x)的值是“y”,因此$($(x))就是$(y),因而$(a)的值就是“z”。(注意,是“x=y”,而不是“x=$(y)”)
6) override
若是有变量是一般make的命令行参数设置的,那么Makefile中对这个变量的赋值会被忽略。若是你想在Makefile中设置这类参数的值,那么,你可使用“override”指示符。其语法是:
override <variable> = <value>
override <variable> := <value>
固然,你还能够追加:
override <variable> += <more text>
对于多行的变量定义, 咱们用define指示符, 在define指示符前, 也一样可使用ovveride 指示符,如:
override define foo
bar
endef
7) 目标变量
能够为某个目标设置局部变量,这种变量被称为“Target-specific Variable”,它的做用范围只在这条规则以及连带规则中,因此其值也只在做用范围内有效。而不会影响规则链之外的全局变量的值。
语法是:
<target ...> : <variable-assignment>
<target ...> : overide <variable-assignment>
<variable-assignment>能够是前面讲过的各类赋值表达式,如“=”、“:=”、“+=” 或是“?=”。第二个语法是针对于make命令行带入的变量,或是系统环境变量。这个特性很是的有用,当咱们设置了这样一个变量,这个变量会做用到由这个目标所引起的全部的规则中去。如:
prog : CFLAGS = -g
prog : prog.o foo.o bar.o
$(CC) $(CFLAGS) prog.o foo.o bar.o
8)模式变量
模式变量的好处就是,咱们能够给定一种“模式”,能够把变量定义在符合这种模式的全部目标上。
make的“模式”通常是至少含有一个“%”的,因此,能够以以下方式给全部以[.o]结尾的目标定义目标变量:
%.o : CFLAGS = -O
一样,模式变量的语法和“目标变量”同样:
<pattern ...> : <variable-assignment>
<pattern ...> : override <variable-assignment>
override一样是针对于系统环境传入的变量,或是make命令行指定的变量。
环境变量、自动变量和预约义变量。
1) 环境变量
make运行时的系统环境变量能够在make开始运行时被载入到Makefile文件中,可是若是 Makefile中已定义了这个变量,或是这个变量由make命令行带入,那么系统的环境变量的值将被覆盖。(若是make指定了“-e”参数,那么,系统环境变量将覆盖Makefile中定义的变量)
所以,若是咱们在环境变量中设置了“CFLAGS”环境变量,那么咱们就能够在全部的 Makefile中使用这个变量了。 这对于咱们使用统一的编译参数有比较大的好处。 若是Makefile 中定义了CFLAGS,那么则会使用Makefile中的这个变量,若是没有定义则使用系统环境变量的值,一个共性和个性的统一,很像“全局变量”和“局部变量”的特性。
当make嵌套调用时(参见前面的“嵌套调用”章节),上层Makefile中定义的变量会以系统环境变量的方式传递到下层的Makefile中。固然,默认状况下,只有经过命令行设置的变量会被传递,而定义在文件中的变量,若是要向下层Makefile传递,则须要使用exprot关键字来声明。
2) 自动变量
make管理项目所使用的自动变量所有以美圆符号$开头。
$@ Makefile文件中一条规则的目标文件名。
$< 依赖文件中的第一个
$^ Makefile文件中规则的目标所对应的全部依赖文件的列表,以空格分隔(不重复)
$? 依赖文件中新于目标的文件列表,以空格分隔
$(@D) 目标文件的目录部分,若是目标在子目录中。
$(@F) 目标文件的文件名部分,若是目标在子目录中。
$* 不包含扩展名的目标文件名称
$+ 全部依赖文件,空格隔开,前后为序,可重复
$% 若是目标是归档成员(函数库),则该变量表示目标的归档成员名称。
3)预约义变量
make管理项目支持的预约义变量主要用于定义程序名,以及传给这些程序的参数及标志位。
AR 归档维护程序,缺省值为ar。
AS 汇编程序,缺省值为as。
CC C语言编译程序,缺省值为CC。
CPP C语言预处理程序,缺省值为CPP。
RM 文件删除程序,缺省值为rm –f。
ARFLAGS 传给归档维护程序的标志,缺省值为rv。
ASFLAGS 传给汇编程序的标志,完好省值。
CFLAGS 传给C语言编译的标志,完好省值。
CPPFLAGS 传给预处理的标志,完好省值。
LDFLAGS 传给连接程序的标志,完好省值
CXX C++编译器名称,默认值为g++。
CXXFLAGS C++编译器选项。
使用条件判断,可让make根据运行时的不一样状况选择不一样的执行分支。条件表达式能够是比较变量的值,或是比较变量和常量的值。
语法:
<conditional-directive>
<text-if-true>
endif
或者
<conditional-directive>
<text-if-true>
else
<text-if-false>
endif
conditional-directive表示条件关键字,共4个:ifeq,ifneq,ifdef,ifndef。
ifeq (<arg1>,<arg2>)
ifneq (<arg1>,<arg2>)
ifdef <variable-name>
ifndef <variable-name>
特别注意的是, make是在读取Makefile时就计算条件表达式的值, 并根据条件表达式的值来选择语句,因此,你最好不要把自动化变量(如“$@”等)放入条件表达式中,由于自动化变量是在运行时才有的。 并且,为了不混乱,make不容许把整个条件语句分红两部分放在不一样的文件中。
创建makefile文件后,就可使用make命令生成和维护目标文件了。
make [options] [macrodef] [target]
options指定make的工做行为,macrodef指定执行makefile时的宏值,目标(target)是要更新的文件列表。
options:
-C dir make开始运行以后的工做目录为指定目录。若是有多个-C,后面的dir指定的目录是相对于前一个目录。
-d 打印除通常处理信息以外的调试信息。
-e 不容许在makefile中对环境的宏赋新值。
-f 使用指定的文件为makefile
-i 忽略运行makefile时命令行产生的错误,不退出make。
-I dir 指定搜索被include的makefile的目录。
若是命令行中有多个-I选项,按出现顺序依次搜索。与其余选项不一样,容许dir紧跟在-I以后(不加空格),这是为了与C预处理器兼容。
-k 执行命令出错时,放弃当前的目标文件,尽量地维护其余目标。
-n 按实际运行时执行顺序显示命令,包括@开头的命令,但不真正执行。
-o file 不维护指定文件
-p 显示makefile中全部宏定义和描述内部规则的规则(隐含规则),而后按通常状况执行。
如只想打印信息而不真正维护,可使用make –p –f /dev/null
-q “问题模式”。若是指令的目标目前没有过时,就返回0,不然返回一个非零值。
不运行任何命令或打印任何信息。
-r 忽略内部规则,同时清除缺省的后缀规则。
-s 执行但不显示执行的命令。
-S 执行makefile命令菜单时出错即退出make。这是make的默认工做方式,通常不指定。
-t 修改每一个目标文件的建立日期,不真正从新建立文件。
-v 打印make版本号,而后正常执行。如只想打印而不维护,使用
make –v –f /dev/null
make命令执行后有三个退出码:
0--表示成功执行
1--若是make运行时出现错误,返回1
2--若是使用make的“-q”选项,而且make使得一些目标不须要更新,返回2。
配置、编译、安装源码包软件
#./configure
#make
#make install
./configure比较重要的参数为--prefix,指定软件的安装目录。
在Makefile中引用其余Makefile文件的方法是,使用include filename.mk
参考:
1. 《跟我一块儿写Makefile》
5. gnu make