Makefile基础教程 4

来源:互联网 发布:网络大电影播放量分红 编辑:程序博客网 时间:2024/06/07 13:03

一、实验介绍--Make 的处理阶段及条件执行

本实验重点介绍make的两个处理阶段和条件执行语句。

1.1 实验内容

  1. 验证make的两个处理阶段。
  2. 测试make目标指令的执行细节。
  3. 测试make的条件执行语句。

1.2 实验知识点

1.make分为两个处理阶段:1)读取所有makefile文件,内建变量、规则和依赖关系结构链表。2)执行更新和重建。

2.makefile中可以使用反斜线将语句和命令分成多行。

3.makefile中可以使用$$$$打印当前进程id

4.makefile中条件语句的基本格式。

5.makefile ifeqifneqifdefifndef的使用。

1.3 实验环境

Ubuntu系统, GNU gcc工具,GNU make工具

1.4 适合人群

本课程难度为简单,属于入门级别课程,适合有代码编写能力的用户,熟悉和掌握make的一般用法。

1.5 代码获取

可以通过以下命令获取代码:

$ git clone https://github.com/darmac/make_example.git

二、实验原理

依据makefile的基本规则设计相应的正反示例,验证规则。

三、开发准备

进入实验楼课程即可。

四、项目文件结构

makefile:make工程文件。

五、实验步骤

5.1 make 的两个执行阶段

5.1.1 抓取源代码

使用如下cmd获取GitHub源代码:

cd ~/Code/git clone https://github.com/darmac/make_example.gitcd make_example/chapter3

5.1.2 新增makefile并测试执行状况

源代码中已经存在makefile文件,查看文件内容:

# this is a makefile examplevari_a = "vari a from makefile"vari_b = "vari b from makefile".PHONY:allall:        @echo $(vari_a)        @echo $(vari_b)

此文件定义了两个变量vari_a,vari_b,并在执行all规则时打印它们的值。

执行make

make

终端打印如下:

vari a from makefilevari b from makefile

5.1.3 新增被包含文件并修改makefile

现在新增一个文件inc_a,并在其中将vari_b变量改为“vari b from inc_a”

源代码中已有此文件,内容如下:

# this is a include file for makevari_b = "vari b from inc_a"

修改makefile在最后一行包含inc_a档案。

include inc_a

5.1.4 测试include文件是否在第一阶段被包含

我们知道make是按照顺序一行行读入makefile

前面介绍make的第一阶段是读入所有makefileinclude文档,内建变量。

所以解析新修改的makefile时,inc_a应该在第一阶段被解析完毕,vari_b变量也被inc_a修改掉。

执行make进行验证:

make

终端打印:

vari a from makefilevari b from inc_a

说明vari_b已经被修改,与include指示符在makefile的位置无关。

实验过程如下图所示:

5.1

5.2make目标指令的执行细节

5.2.1makefile目标下指令的执行过程

到目前为止,我们看到makefile中的指令都是shell指令,那么make是怎样执行目标对应指令呢?

答案还是shell。make会调用shell去执行每一条指令。

但需要注意的是,即便在同一个目标下,每一条指令都是相互独立的。

也就是说make会分别调用shell去执行每一条指令,而非使用一个shell进程按顺序将所有指令都执行一遍。

5.2.2 新建makefile测试命令pwdcd的执行效果。

现在使用cd命令和pwd命令查看两条相邻的命令能否相互产生影响。

源文件代码中已经有cd_test.mk文件,内容如下:

# this is a makefile to test cd and pwd cmd.PHONY:allall:        @pwd        cd ..        @pwd

all 规则由三条命令构成,其中“@pwd”表示打印当前绝对路径,但不要显示“pwd”命令,“cd ..”表示回到上一层目录。

因此,若三条指令是在一个 shell 进程中顺序执行,应该会先打印当前目录的绝对路径,

再返回上一层目录并打印上一层目录的绝对路径。

执行make -f cd_test.mk 进行测试:

make -f cd_test.mk

终端打印:

/root/study/make_example/chapter3cd ../root/study/make_example/chapter3

可见实际执行状况中cd命令并不会对下一条指令产生影响。

5.2.3 在每一条指令中打印进程id确认指令会被不同的进程执行。

还有一个更简单的方法是打印执行当前命令的进程 id。

源文件代码中已经有cmd_test.mk文件,内容如下:

#this is a command test makefile.PHONY:allall:        @echo "cmd1 process id is :" $$$$        @echo "cmd2 process id is :" $$$$

其中“$$$$”代表的是当前进程id

所以cmd_test.mk的命令执行过程就是分别打印all目标下两条命令的进程id

执行make -f cmd_test.mk进行测试:

make -f cmd_test.mk

终端打印:

cmd1 process id is : 298cmd2 process id is : 299

可以看出两条命令执行的进程id并不相同。

5.2.4 在同一行中使用多条命令

有些状况下,用户希望能够使用cd命令来控制命令执行时所在的路径,

比如cd到某个目录下,编译其中的源代码,要如何实现呢?

此时必须在一行中写入多条指令。

先修改cd_test.mk文件,将三条指令都放在一行,并用“;”隔开。

请注意第三条“@pwd”的指令中,“@”符号要删掉,此符号只用于每一行的开头。

修改后的cd_test.mk内容如下:

# this is a makefile to test cd and pwd cmd.PHONY:allall:        @pwd; cd .. ; pwd

再次执行 cmd_test.mk 文件:

make -f cd_test.mk

终端打印:

/root/study/make_example/chapter3/root/study/make_example

说明现在三条语句已经在同一个进程中被执行到了。

同样,我们也对cmd_test.mk文件进行修改,再确认进程号是否一致。

修改后的cmd_test.mk文件内容如下:

#this is a command test makefile.PHONY:allall:        @echo "cmd1 process id is :" $$$$;echo "cmd2 process id is :" $$$$

使用 make 执行此文件:

make -f cmd_test.mk

终端打印:

cmd1 process id is : 666cmd2 process id is : 666

由此可见同一行的指令的确会被同一个进程执行到。

5.2.5 使用反斜线分割命令

在同一行中书写多条指令是一件比较麻烦的事情,尤其是指令较长时,非常不方便阅读和修改。

makefile 中可以使用反斜线“\”来将一行的内容分割成多行。

源文件中有一个multi_test.mk脚本,用于测试反斜线的作用,内容如下:

#this is a command test makefile.PHONY:allall:        @echo "cmd1 process \        id is :" $$$$; \        echo "cmd2 process id is :" $$$$

此文件将一条指令分割成3行,其中第一行和第二行组成一条完整的指令,内容与第三行指令相似。

两条指令的作用也是打印当前执行进程的 id号。

使用make 执行此文件:

make -f multi_test.mk

终端打印如下:

cmd1 process id is : 675cmd2 process id is : 675

执行效果与修改后的 cmd_test.mk文件一致,说明反斜杠的确能起到连接多行指令的作用。

实验过程如下图所示:

5.2A

5.2C

5.3 条件执行语句

5.3.1 条件语句的基本格式

makefile 中不包含else 分支条件判断语句的语法格式为:

CONDITIONAL-DIRECTIVETEXT-IF-TRUEendif

TEXT-IF-TRUE可以为若干任何文本行,当条件为真时它被 make 作为需要执行的一部分。

包含 else 的格式为:

CONDITIONAL-DIRECTIVETEXT-IF-TRUEelseTEXT-IF-FALSEendif

make 在条件为真时执行TEXT-IF-TRUE,否则执行TEXT-IF-FALSE

5.3.2 ifeq 语句

ifeq 用于判断条件是否相等,可以支持以下几种格式:

ifeq (ARG1, ARG2)

ifeq 'ARG1' 'ARG2'

ifeq "ARG1" "ARG2"

ifeq "ARG1" 'ARG2'

ifeq 'ARG1' "ARG2"

请注意:ifeq/ifneq 等关键字后面一定要接一个空格,否则make会因为无法识别关键字而报错!

源文件代码中已有eq.mk文件,内容如下:

#this is a makefile to test ifeq.PHONY:allb="ifeq default"ifeq ($(a),1)b="ifeq a 1"endififeq '$(a)' '2'b="ifeq a 2"endififeq "$(a)" "3"b="ifeq a 3"endififeq "$(a)" '4'b="ifeq a 4"endififeq '$(a)' "5"b="ifeq a 5"endifall:        @echo $(b)

使用make重建目标all时,将会根据a的值重新定义b的值并将其打印出来。

使用make命令执行此文件,指令及打印内容如下:

shiyanlou:chapter3/ (master*) $ make -f eq.mkifeq defaultshiyanlou:chapter3/ (master*) $ make -f eq.mk a=1ifeq a 1shiyanlou:chapter3/ (master*) $ make -f eq.mk a="1"ifeq a 1shiyanlou:chapter3/ (master*) $ make -f eq.mk a=2  ifeq a 2shiyanlou:chapter3/ (master*) $ make -f eq.mk a=3ifeq a 3shiyanlou:chapter3/ (master*) $ make -f eq.mk a=4ifeq a 4shiyanlou:chapter3/ (master*) $ make -f eq.mk a=5ifeq a 5shiyanlou:chapter3/ (master*) $ make -f eq.mk a=6ifeq default

5.3.3ifneq语句

ifneq支持的格式与ifeq相同,

源文件代码中已经有neq.mk,内容如下:

#this is a mekfile to test ifneq.PHONY:allifneq ($(a),)b=$(a)elseb="null"endifall:        @echo "value b is:" $(b)

neq.mk中使用了ifneq ... else ... endif结构。

a不为空时,b的值与a相同,否则b为默认值“null”

执行make时会打印b的值,指令及打印内容如下:

shiyanlou:chapter3/ (master*) $ make -f neq.mkvalue b is: nullshiyanlou:chapter3/ (master*) $ make -f neq.mk a=1value b is: 1shiyanlou:chapter3/ (master*) $ make -f neq.mk a=2value b is: 2shiyanlou:chapter3/ (master*) $ make -f neq.mk a="hello"value b is: hello

5.3.4ifdef语句

ifdef语句的语法格式如下:

ifdef VARIABLE-NAME

ifdef 只会判断变量是否有值,而不关心其值是否为空。

现在我们测试ifdef的用法,以及要怎样理解变量值为空和变量未定义的差别。

源文件代码中已经存在def.mk文件,内容如下:

#this is a makefile to test ifdef.PHONY:alla=b=$(a)ifdef ac="a is defined"elsec="a is not defined"endififdef bd="b is defined"elsed="b is not defined"endifall:        @echo "vari a is:" $(a)        @echo "vari b is:" $(b)        @echo "vari c is:" $(c)        @echo "vari d is:" $(d)

def.mk文件中先声明了一个变量a,但并未给其赋值,这相当于是未定义变量。

变量a又被赋给了变量b,由于a是未定义变量,因此b为空值。

make执行此文件时分别打印变量a``b``c``d的值。

执行make

make -f def.mk

终端打印:

vari a is:vari b is:vari c is: a is not definedvari d is: b is defined

可见对make 来说,它认为 a 属于未定义变量,b 则属于已定义变量。

5.3.5ifndef语句

ifneq格式与ifeq相同,逻辑上与ifneq相反。

源文件代码中已有ndef.mk文件,内容与def.mk相似:

#this is a makefile to test ifndef.PHONY:alla=b=$(a)ifndef ac="a is not defined"elsec="a is defined"endififndef bd="b is not defined"elsed="b is defined"endifall:        @echo "vari a is:" $(a)        @echo "vari b is:" $(b)        @echo "vari c is:" $(c)        @echo "vari d is:" $(d)

执行 make:

make -f ndef.mk

终端打印:

vari a is:vari b is:vari c is: a is not definedvari d is: b is defined

实验过程如下图所示:

5.3

六、实验总结

本实验测试了make执行的两个阶段,目标指令的执行细节和条件执行语句的编写。