说说Makefile那些事儿

说说Makefile那些事儿面试

|扬说|透过现象看本质shell

  

  工做至今,一直对Makefile半知半解。忽然某天幡然醒悟,以为此举极为不妥,只得洗心革面从头学来,之前许多不明觉厉之处顿时茅塞顿开,想一想好记性不如烂笔头,便来讲说Makefile那些事儿。微信

 

  • Makefile究竟是个啥玩意儿

  Makefile就是一文本文件。函数

-----------------------------------------------工具

$ file Makefile性能

Makefile: ASCII make commands textui

-----------------------------------------------spa

  

  通常来讲,咱们平时所称的Makefile是指make命令以及Makefile文件,Makefile文件中记录着各类规则,make命令经过分析Makefile执行规则中的操做。命令行

   

  看看百度百科的定义:设计

make是一个命令工具,它解释Makefile 中的指令(应该说是规则)。在Makefile文件中描述了整个工程全部文件的编译顺序、编译规则。Makefile 有本身的书写格式、关键字、函数。像C 语言有本身的格式、关键字和函数同样。并且在Makefile 中可使用系统shell所提供的任何命令来完成想要的工做。Makefile(在其它的系统上多是另外的文件名)在绝大多数的IDE 开发环境中都在使用,已经成为一种工程的编译方法。

 

  再看看官方文档的定义:

You need a file called a makefile to tell make what to do. Most often, the makefile tells makehow to compile and link a program.

 

  • Makefile的由来

  任何一种技能或知识都是源之于某种社会需求,那为何要用Makefile呢?

  当项目源文件不多的时候,咱们也许还能够手动使用gcc命令来进行编译,可是当项目发展到一个庞大的规模时,再手动敲gcc命令去编译就变得不可能的事情。因此呢,在这样的历史背景下,就出现了某个大牛(斯图亚特·费尔德曼),在某年(1977年)在某地(贝尔实验室)制做了这样一个软件,它的名字就叫作make。

        

  用一句话来讲明为啥用Makefile:为了实现自动化(固然大多数场景都是用在自动化编译中)。

 

  另外在编译过程当中,为了节省时间,但愿仅编译修改过的文件,这也是Makefile在设计时一个重要的设计观点。

 

  • Makefile的组成部分

  Makefile包含五个东西:显示规则,隐式规则,变量定义,文件指示,注释。具体含义仍是直接引用网上的版本吧| | | 

一、显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。 

二、隐式规则。因为咱们的make有自动推导的功能,因此隐晦的规则可让咱们比较粗糙地简略地书写Makefile,这是由make所支持的。 

三、变量的定义。在Makefile中咱们要定义一系列的变量,变量通常都是字符串,这个有点你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。 

四、文件指示。其包括了三个部分,一个是在一个Makefile中引用另外一个Makefile,就像C语言中的include同样;另外一个是指根据某些状况指定Makefile中的有效部分,就像C语言中的预编译#if同样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。 

五、注释。Makefile中只有行注释,和UNIX的Shell脚本同样,其注释是用“#”字符,这个就像C/C++中的“//”同样。若是你要在你的Makefile中使用“#”字符,能够用反斜框进行转义,如:“/#”。 

最后,还值得一提的是,在Makefile中的命令,必需要以[Tab]键开始。 

        

  这里说说规则(Rules):

target ... : prerequisites ...
    command
    ...
    ...

  一条规则由三部分组成,目标(target)、先决条件(prerequisites)、命令(commands)。

 

  target也就是一个目标文件,能够是Object File,也能够是执行文件。还能够是一个标签(Label)。 

  prerequisites就是,要生成那个target所须要的文件或是目标。

  command也就是make须要执行的命令。(任意的Shell命令)

  

  这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。

  说白一点就是说,prerequisites中若是有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。

  这就是Makefile的规则。也就是Makefile中最核心的内容。

 

  • Makefile的核心思想

     四个字,依赖关系

 

  • Makefile之执行过程

  1. 依次读取变量“MAKEFILES”定义的makefile文件列表

  2. 读取工做目录下的makefile文件(根据命名的查找顺序“GNUmakefile”,“makefile”,“Makefile”,首先找到那个就读取那个)

  3. 依次读取工做目录makefile文件中使用指示符“include”包含的文件

  4. 查找重建全部已读取的makefile文件的规则(若是存在一个目标是当前读取的某一个makefile文件,则执行此规则重建此makefile文件,完成之后从第一步开始从新执行)

  5. 初始化变量值并展开那些须要当即展开的变量和函数并根据预设条件肯定执行分支

  6. 根据“终极目标”以及其余目标的依赖关系创建依赖关系链表

  7. 执行除“终极目标”之外的全部的目标的规则(规则中若是依赖文件中任一个文件的时间戳比目标文件新,则使用规则所定义的命令重建目标文件)

  8. 执行“终极目标”所在的规则

 

  • Makefile之模式规则

模式规则其实也是普通规则,但它使用了如%这样的通配符。以下面的例子:

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

此规则描述了一个.o文件如何由对应的.c文件建立。规则的命令行中使用了自动化变量“$<”和“$@”,其中自动化变量“$<”表明规则的依赖,“$@”表明规则的目标。此规则在执行时,命令行中的自动化变量将根据实际的目标和依赖文件取对应值。

 

  • Makefile之隐式规则       

  若是发现某变量在shell和makefile中未找不到其定义,那么恭喜你,你极大可能遇到隐式规则了。固然隐式规则中的变量只是隐式规则的一部分。

 

  1.  隐式规则中的变量

  隐式规则中使用的变量分红两种:一种是命令相关的,如“CC”;一种是参数相关的,如“CFLAGS”。

  1)与命令相关的变量

 

变量

含义

AR

函数库打开包程序。默认命令是“ar”

AS

汇编语言编译程序。默认命令是“as”

CC

C语言编译程序。默认命令是“cc”

CXX

C++语言编译程序。默认命令是“g++”

CO

从RCS文件中扩展文件程序。默认命令是“co”

CPP

C程序的预处理器(输出是标准输出设备)。默认命令是“$(CC) -E”

FC

Fortran和Ratfor的编译器和预处理程序。默认命令是”f77”

GET

从SCCS文件扩展文件的程序。默认命令是“get”

LEX

Lex方法分析器程序(针对于C或Ratfor)。默认命令是”lex”

PC

Pascal语言编译程序。默认命令是”pc”

YACC

Yacc文法分析器(针对C程序)。默认命令是“yacc”

YACCR

Yacc文法分析器(针对Ratfor程序)。默认命令是“yacc -r”

MAKEINFO

转换Texinfo源文件(.texi)到info文件程序。默认命令是“makeinfo”

TEX

从TeX源文件建立TeX DVI文件的程序。默认命令是“tex”

WEAVE

转化Web到TeX的程序。默认命令是“weave”

TEXI2DVI

从Texinfo源文件建立TeX DVI文件的程序。默认命令是“texi2dvi”

CWEAVE

转化C Web到TeX的程序。默认命令是“cweave”

TANGLE

转换Web到Pascal语言的程序,默认命令是”tangle“

CTANGLE

转换C Web到C。默认命令是”ctangle“

RM

删除文件命令。默认命令是”rm -f“

 

  2)与参数相关的变量

变量

含义

ARFLAGS

函数库打包程序AR命令的参数。默认值是“rv”

ASFLAGS

汇编语言编译参数(当明显地调用”.s”或”.S”文件时)

CFLAGS

C语言编译器参数

CXXFLAGS

C++语言编译器参数

COFLAGS

RCS命令参数

CPPFLAGS

C预处理器参数(C和Fortran编译器也会用到)

FFLAGS

Fortran语言编译器参数

GFLAGS

SCCS ”get“程序参数

LDFLAGS

链接器参数(如“ld”)

LFLAGS

Lex文法分析器参数

PFLAGS

Pascal语法编译器参数

RFLAGS

Ratfor程序的Fortran编译器参数

YFLAGS

Yacc文法分析器参数

  2.  使用模式规则

  可使用模式规则定义一个隐式规则。和通常规则相似,只是在模式规则中,目标的定义须要有“%”字符。“%”定义对文件名的匹配,表示任意长度的非空字符串。在依赖目标中一样可使用“%”,只是依赖目标中“%”的取值,取决于其目标。

模式规则中“%”的展开和变量与函数的展开是有区别的,“%”的展开发生在变量和函数的展开以后。变量和函数的展开发生在make载入Makefile时,而“%”的展开则发生在运行时。

  1)  模式规则举例

  模式规则中,至少在规则的目标中要包含“%”符号。

  %.o : %.c ; <command ......>

  其含义是,字指出了从全部的.c文件生成相应的.o文件的规则。若是要生成的目标是”a.o b.o”,那么

  %.c”就是”a.c b.c”。

  %.o : %.c

  $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

  表示把全部的.c文件都编译成.o文件。

  其中,“$@”表示全部目标的集合,”$<”表示全部依赖目标的集合(在模式定义规则的情形下)。

  2)  自动化变量

  自动化变量只应出如今规则的命令中。

变量

含义

$@

表示规则中的全部目标文件的集合。在模式规则中若是有多个目标,“$@”就是匹配于目标中模式定义的集合

$%

仅当目标是函数库文件时,表示规则中的目标成员名,若是目标不是函数库文件(UNIX下是

.a,Windows是.lib),其值为空。

$<

依赖目标中的第一个目标名字,若是依赖目标是以模式(即”%“)定义的,则”$<”是符合模式的一系列的文件集

$?

全部比目标新的依赖目标的集合,以空格分隔

$^

全部依赖目标的集合,以空格分隔。如若是在依赖目标中有多个重复的,则自动去除重复的依赖目标,只保留一份

$+

同”$^”,也是全部依赖目标的集合,只是它不去除重复的依赖目标。

$*

目标模式中“%”及其以前的部分

$(@D)

“$@”的目录部分(不以斜杠做为结尾),若是”$@”中没有包含斜杠,其值为“.”(当前目录)

$(@F)

“$@”的文件部分,至关于函数”$(notdir $@)”

$(*D)

同”$(@D)”,取文件的目录部分

$(*F)

同”$(@F)”,取文件部分,但不取后缀名

$(%D)

函数包文件成员的目录部分

$(%F)

函数包文件成员的文件名部分

$(<D)

依赖目标中的第一个目标的目录部分

$(<F)

依赖目标中的第一个目标的文件名部分

$(^D)

全部依赖目标文件中目录部分(无相同的)

$(^F)

全部依赖目标文件中文件名部分(无相同的)

$(+D)

全部依赖目标文件中的目录部分(能够有相同的)

$(+F)

全部依赖目标文件中的文件名部分(能够有相同的)

$(?D)

全部被更新文件的目录部分

$(?F)

全部被更新文件的文件名部分

 

 

  • Makefile那些稀奇古怪的符号

这些稀奇古怪的符号是前面隐式规则中出现过,单独拎出来是由于咱们会常常用到它们。         

这些符号也就是咱们常说的自动变量:

$@ :规则中的目标集

$^ :规则中的全部先决条件

$< :表示规则中的第一个先决条件

 

再来讲说$VAR和$$VAR的区别:

makefile文件中的规则绝大部分都是使用shell命令来实现的,这里就涉及到了变量的使用,包括makefile中的变量shell命令范畴内的变量。在makefile的规则命令行中使用$var就是在命令中引用makefile的变量,这里仅仅是读取makefile的变量而后扩展开,将其值做为参数传给了一个shell命令;而$$var是在访问一个shell命令内定义的变量,而非makefile的变量。若是某规则有n个shell命令行构成,而相互之间没有用';'和'\'链接起来的话,就是相互之间没有关联的shell命令,相互之间也不能变量共享。

 

  • Makefile之伪目标

使用其缘由一:避免和同名文件冲突

在现实中不免存在所定义的目标与所存在的目标是同名的,采用Makefile如何处理这种状况呢?Makefile中的假目标(phony target)能够解决这个问题。

假目标可使用.PHONY关键字进行声明,对于假目标,能够想象,由于不依赖于某文件,make该目标的时候,其所在规则的命令都会被执行。

        

若是编写一个规则,并不产生目标文件,则其命令在每次make 该目标时都执行。

例如:

clean:

rm *.o temp

由于"rm"命令并不产生"clean"文件,则每次执行"make clean"的时候,该命令都会执行。若是目录中出现了"clean"文件,则规则失效了:没有依赖文件,文件"clean"始终是最新的,命令永远不会执行;为避免这个问题,可以使用".PHONY"指明该目标。如:

.PHONY : clean

这样执行"make clean"会无视"clean"文件存在与否。

 

已知phony 目标并不是是由其它文件生成的实际文件,make 会跳过隐含规则搜索。这就是声明phony 目标会改善性能的缘由,即便你并不担忧实际文件存在与否。

完整的例子以下:

.PHONY : clean

clean :

rm *.o temp

 

使用其缘由二:提升执行make的效率

当一个目标被声明为伪目标后,make在执行此规则时不会试图去查找隐含规则来建立这个目标。这样也提升了make的执行效率,同时咱们也不用担忧因为目标和文件名重名而使咱们的指望失败。    

 

  • Makefile那些赋值的事儿

面试中常常被问到的问题: [=]和[:=]符号的区别。

=

  能够先使用后定义,这就致使makefile在所有展开后才能决定变量的值。

  有可能出现循环递归,没法暂开的问题。

:=

  必须先定义而后再使用,在当前的位置就能够决定变量的值。

再补充两种符号?= 、+=,若是熟悉C语言那对这两种符号理解会很容易。

?=

  至关于选择疑问句,若是前面的变量没被赋值,那就作赋值操做

+=

  至关于递加操做

看一个例子就明白了。新建一个Makefile,内容以下:

ifdef DEFINE_VRE VRE = “Hello World!else endif ifeq ($(OPT),define) VRE ?= “Hello World! First!” endif ifeq ($(OPT),add) VRE += “Kelly!” endif ifeq ($(OPT),recover) VRE := “Hello World! Again!” endif all: @echo $(VRE)

 

敲入如下make命令:

make DEFINE_VRE=true OPT=define 输出:Hello World!

make DEFINE_VRE=true OPT=add 输出:Hello World! Kelly!

make DEFINE_VRE=true OPT=recover  输出:Hello World! Again!

make DEFINE_VRE= OPT=define 输出:Hello World! First!

make DEFINE_VRE= OPT=add 输出:Kelly!

make DEFINE_VRE= OPT=recover 输出:Hello World! Again!

 

  • 杂谈

 

  如下,摘止采铜大大的《开放的智力》,献给那些不甘平庸的人。

  在今天这个时代,不少人疯狂地追求着不少东西,却没有反思过本身为何要追求这些。咱们争先恐后地买房,买车,咱们笑谈着「卖肾」买iPhone,咱们为各类名牌神魂颠倒,咱们在微博、微信这些社交媒体上八面玲珑,咱们在知乎上写答案而后等着赞同票一点点刷上去。这些都构成了米兰·昆德拉笔下的「媚俗」的生活。

  在一个钟鼓齐鸣的地方,你会失去驻足倾听的能力;在一个霓虹闪烁的地方,你就没法发现事物自己的光泽;在一我的流如梭的闹市,你会忘记本来行进的方向。以旁人的眼光做为本身人生的参考线,你会为追逐一个心里并不想要的东西,而气喘吁吁地奔跑;更糟糕的是,你的人生会被割裂开来,变成不少碎片,每一块碎片都用来讨好特定的一群人。也许,以一个较小的概率,你最终得到了世俗意义上的成功,但这种成功也极可能是平庸的。若是你作不成本身,再大的成功又有何意义?

  趋同的益处简单而直接,你能够很容易的融入一个集体,与别人有更多共同的兴趣,聊天时有更多的谈资,得到长辈更多的称赞,甚至,你会成为一个小群体中的明星。而它的代价是缓慢而隐蔽的,它会让你不去努力发现本身的天赋和才能,让你失去变得不同凡响的全部可能性。曾经有一个日本小说家,从小学开始,成绩就很很差,是老师同窗眼中的大笨蛋,固然他本身也这么以为。不过他很早就有一个爱好,就是喜欢看小说。到了高一时,他的成绩在整年级四百多人中都排到了倒数几名,可他对此倒是不急不躁,不只不迎头遇上,而是变本加厉,开始作一件同窗老师家长玩玩没想到的事:写小说。天天晚上,他用小伙伴们作做业的时间伏案写做,一上来就是长篇,一下就是几十万字,这样写了多年,遭遇了几回挫折以后,终于成名,他是东野圭吾。

  因此,心里坚决的人,历来不忌惮作一些不寻常之事。那些在别人看来疯狂的举动,对本身来讲却多是最好最安宁的选择。逃脱献媚于他人的牢笼,才能真正得到自由。