Makefile基础教程 9

一、实验介绍--Make 递归执行

本次实验将介绍 make 的递归执行及其过程中变量、命令行参数的传递规则。

1.1 实验内容

1.make的递归执行示例

  1. 递归执行过程中变量的传递
  2. 测试MAKELEVEL环境变量
  3. 命令行参数和变量的传递

1.2 实验知识点

1.make-w选项可以打印make进入和离开目录的信息。

2.makefile中通常使用$(MAKE)递归执行下层makefile,以确保上下层的make程序一致。

  1. 递归执行过程中,顶层文件的变量默认不会传给下层文件。
  2. 可以使用export进行变量传递,也可以使用unexport取消传递。
  3. 当上层变量和下层变量定义有冲突时,下层优先级更高,也可以使用-e选项取消下层的定义。6.$(SHELL)变量和$(MAKEFLAGS)变量默认会传给下层文件。7.$(MAKELEVEL)可以显示make当前的文件层级。8.$(MAKEFLAGS)变量会记录make的命令行选项和参数。

1.3 实验环境

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

1.4 适合人群

本课程难度为中等,适合已经初步了解 makefile 规则的学员进行学习。

1.5 代码获取

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

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

二、实验原理

依据 makefile 的基本规则进行正反向实验,学习和理解规则的使用方式。

三、开发准备

进入实验楼课程即可。

四、项目文件结构

.
├── flags:用于测试 MAKEFLAGS 变量
│   ├── dir_a
│   │   └── makefile
│   └── makefile
├── level:用于测试 MAKELEVEL 变量
│   ├── dir_a
│   │   ├── dir_b
│   │   │   └── makefile
│   │   └── makefile
│   └── makefile
├── Readme.md
├── recur:用于测试 make 的递归调用
│   ├── dir_a
│   │   └── makefile
│   ├── dir_b
│   │   └── makefile
│   └── makefile
└── vari:用于测试 make 递归调用的变量传递
    ├── dir_a
    │   └── makefile
    ├── dir_b
    │   └── makefile
    ├── dir_c
    │   └── makefile
    ├── export.mk
    ├── makefile
    └── spec.mk

五、实验步骤

5.1make的递归执行示例

5.1.1 抓取源代码

使用如下 cmd 获取 GitHub 源代码并进入相应章节:

cd ~/Code/
git clone https://github.com/darmac/make_example.git
cd make_example/chapter8
5.1.2 测试make的递归调用

make 的递归过程是指:在makefile中使用make作为命令来执行本身或其它makefile文件。

在实际项目中,我们经常需要用到make的递归调用,这样可以简化每个模块的makefile设计与调试,便于项目管理。

chapter8/recur/目录演示了一个简单的递归调用过程,主目录makefile内容如下:

#this is a makefile for recursion test

subdir := dir_a dir_b

.PHONY:clean all $(subdir)

all:$(subdir)
        @echo "final target finish!"

$(subdir):
        cd $@;$(MAKE)

终极目标all依赖于两个依赖项,依赖项的规则命令是进入子目录并调用底层的makefile

这里有三个地方需要说明:

1)subdir变量使用了:=进行赋值,这是直接展开赋值,即make读到此行时就直接将subidr文本固定为后面赋值的内容。我们会在后续章节继续讲解:==的差别。对简单的makefile而言,推荐大家尽量使用:=的赋值方式。

2).PHONY中也引用了变量subdir,这样做没有任何问题。

3)观察依赖项的规则命令,进入子目录后使用了$(MAKE)而非make,这样做的好处是保证执行上下层makefilemake都是同一个程序。

两个子目录下的makefile文件内容相同,都是:

.PHONY:show

show:
        @echo "target " $@ "@" $(PWD)

所以子目录的执行过程是更新目录时间戳,并打印当前执行路径。

现在进入recur目录并执行make

cd recur/;make

终端打印:

cd dir_a;make
make[1]: Entering directory `/home/shiyanlou/Code/make_example/chapter8/recur/dir_a'
target  show @ /home/shiyanlou/Code/make_example/chapter8/recur/dir_a
make[1]: Leaving directory `/home/shiyanlou/Code/make_example/chapter8/recur/dir_a'
cd dir_b;make
make[1]: Entering directory `/home/shiyanlou/Code/make_example/chapter8/recur/dir_b'
target  show @ /home/shiyanlou/Code/make_example/chapter8/recur/dir_b
make[1]: Leaving directory `/home/shiyanlou/Code/make_example/chapter8/recur/dir_b'
final target finish!

分析打印内容可知,make 先后进入dir_adir_b目录执行目录下的makefile,并完成终极目标的重建。

除了预期打印之外,终端上还多出了几行make进出目录的打印信息,这其实是由-w选项来控制的。

make在进行递归调用时会自动传递-w选项给下层make

我们在顶层make也加上-w确认一下效果:

make -w

终端打印:

make: Entering directory `/home/shiyanlou/Code/make_example/chapter8/recur'
cd dir_a;make
make[1]: Entering directory `/home/shiyanlou/Code/make_example/chapter8/recur/dir_a'
target  show @ /home/shiyanlou/Code/make_example/chapter8/recur/dir_a
make[1]: Leaving directory `/home/shiyanlou/Code/make_example/chapter8/recur/dir_a'
cd dir_b;make
make[1]: Entering directory `/home/shiyanlou/Code/make_example/chapter8/recur/dir_b'
target  show @ /home/shiyanlou/Code/make_example/chapter8/recur/dir_b
make[1]: Leaving directory `/home/shiyanlou/Code/make_example/chapter8/recur/dir_b'
final target finish!
make: Leaving directory `/home/shiyanlou/Code/make_example/chapter8/recur'

可知make在执行顶层makefile和执行完毕之后都会打印进出当前目录的信息。

实验过程如下图所示:

5.1

5.2 递归执行过程中变量的传递

5.2.1 变量传递

make的递归执行过程中,上层的变量默认状态下是不会传递给下层makefile的。

chapter8/vari/目录下的内容演示了变量传递的过程,先看看chapter8/vari/makefile文件:

#this is a makefile for recursion test

subdir := dir_a dir_b

.PHONY:clean all $(subdir)

all:$(subdir)
        @echo "final target finish!"

$(subdir):
        cd $@;$(MAKE)

与上一个实验中的内容一致,进入两个子目录并调用$(MAKE)之后打印完成消息。

dir_a/makefile的内容如下:

.PHONY:show

show:
    @echo "target " $@ "@" $(PWD)
    @echo "vari subdir is " $(subdir)

打印当前目录,并打印subdir变量的内容。

dir_b/makefile内容相似,但多出了一个subdir的定义:

.PHONY:show

subdir := none

show:
        @echo "target " $@ "@" $(PWD)
        @echo "vari subdir is " $(subdir)

执行打印,看看子目录的subdir内容:

cd ../vari/;make

终端打印:

cd dir_a;make
make[1]: Entering directory `/home/shiyanlou/Code/make_example/chapter8/vari/dir_a'
target  show @ /home/shiyanlou/Code/make_example/chapter8/vari/dir_a
vari subdir is 
make[1]: Leaving directory `/home/shiyanlou/Code/make_example/chapter8/vari/dir_a'
cd dir_b;make
make[1]: Entering directory `/home/shiyanlou/Code/make_example/chapter8/vari/dir_b'
target  show @ /home/shiyanlou/Code/make_example/chapter8/vari/dir_b
vari subdir is  none
make[1]: Leaving directory `/home/shiyanlou/Code/make_example/chapter8/vari/dir_b'
final target finish!

可见上层的subdir变量没有传递到下层makefile中。

5.2.2 使用export传递变量

如果想要将上层变量往下层传递,需要用到export关键字,格式为:

export VARIABLE ...

export.mk文件演示了export的用法,它的内容与makefile一致,只是在定义完subdir之后多了一行:

export subdir

执行 export.mk 文件:

make -f export.mk

终端打印:

cd dir_a;make
make[1]: Entering directory `/home/shiyanlou/Code/make_example/chapter8/vari/dir_a'
target  show @ /home/shiyanlou/Code/make_example/chapter8/vari/dir_a
vari subdir is  dir_a dir_b
make[1]: Leaving directory `/home/shiyanlou/Code/make_example/chapter8/vari/dir_a'
cd dir_b;make
make[1]: Entering directory `/home/shiyanlou/Code/make_example/chapter8/vari/dir_b'
target  show @ /home/shiyanlou/Code/make_example/chapter8/vari/dir_b
vari subdir is  none
make[1]: Leaving directory `/home/shiyanlou/Code/make_example/chapter8/vari/dir_b'
final target finish!

这次dir_a目录下的makefile可以成功继承上层传递的subdir变量,

dir_b目录下的makefile打印出来依然为“none”,这是因为低层的makefile对变量定义具有更高的优先级。

可以使用-e选项来取消低层的高优先级:

make -f export.mk -e

终端打印:

cd dir_a;make
make[1]: Entering directory `/home/shiyanlou/Code/make_example/chapter8/vari/dir_a'
target  show @ /home/shiyanlou/Code/make_example/chapter8/vari/dir_a
vari subdir is  dir_a dir_b
make[1]: Leaving directory `/home/shiyanlou/Code/make_example/chapter8/vari/dir_a'
cd dir_b;make
make[1]: Entering directory `/home/shiyanlou/Code/make_example/chapter8/vari/dir_b'
target  show @ /home/shiyanlou/Code/make_example/chapter8/vari/dir_b
vari subdir is  dir_a dir_b
make[1]: Leaving directory `/home/shiyanlou/Code/make_example/chapter8/vari/dir_b'
final target finish!

可以看出,现在subdir可以顺利传递给每个子目录的makefile

如果不希望变量再往下传递,可以使用unexport关键字,格式与export一致。

unexportexport作用于同一个变量时,以最后声明的unexport/export为准,请大家课后自行测试unexport的用法。

5.2.3 两个默认传递的环境变量

需要注意的是有两个变量默认会传递给下层的makefile,它们是$(SHELL)$(MAKEFLAGS)变量。

之前的实验已经说明了$(SHELL)的作用:指明要使用何种shell程序执行规则命令。

$(MAKEFLAGS)则是用来传递make的命令行选项和参数,实验5.4会对这个变量做进一步说明。

spec.mk文件演示了这两个变量的传递,内容如下:

#this is a makefile for special vari in recursion test

subdir := dir_c

.PHONY:all $(subdir)

all:$(subdir)
        @echo "finished!"

$(subdir):
        cd $@;$(MAKE)

spec.mk会进入dir_c为子目录并执行$(MAKE)

子目录makefile内容如下:

.PHONY:show

show:
        @echo "SHELL is " $(SHELL)
        @echo "MAKEFLAGS is " $(MAKEFLAGS)
        @echo "subdir is " $(subdir)

因此子目录的执行内容就是打印三个变量的值。

执行spec.mk 进行测试:

make -f spec.mk

终端打印:

cd dir_c;make
make[1]: Entering directory `/home/shiyanlou/Code/make_example/chapter8/vari/dir_c'
SHELL is  /bin/sh
MAKEFLAGS is  w
subdir is 
make[1]: Leaving directory `/home/shiyanlou/Code/make_example/chapter8/vari/dir_c'
finished!

可见SHELLMAKEFLAGS变量默认是有值的,如何证明他们是从上层传递下来的而不是make的默认值呢?

只需要在顶层调用make时修改他们的值即可:

make -f spec.mk SHELL=bash -i -k

终端打印:

cd dir_c;make
make[1]: Entering directory `/home/shiyanlou/Code/make_example/chapter8/vari/dir_c'
SHELL is  bash
MAKEFLAGS is  wki -- SHELL=bash
subdir is 
make[1]: Leaving directory `/home/shiyanlou/Code/make_example/chapter8/vari/dir_c'
finished!

上个章节的实验已经测试过SHELL变量和-i``-k选项的作用,大家可以自行复习一下。

此时两个变量都得到了更新。

实验过程如下图所示:

5.2A

5.2B

5.3 测试MAKELEVEL环境变量

变量MAKELEVEL表明当前的调用深度,每一级的递归调用中,MAKELEVEL的值都会发生变化,

最上层值为0,每往下一层加1

chapter8/level/makefile演示了MAKELEVEL的变化,level/目录下还有两层子目录dir_adir_b

每一层的makefile都会打印当前MAKELEVEL的值。

进入level目录并执行makefile,确认MAKELEVEL的变化:

cd ../level/;make

终端打印:

cd dir_a;make
make[1]: Entering directory `/home/shiyanlou/Code/make_example/chapter8/level/dir_a'
cd dir_b;make
make[2]: Entering directory `/home/shiyanlou/Code/make_example/chapter8/level/dir_a/dir_b'
this is level: 2
dir_b finished!
make[2]: Leaving directory `/home/shiyanlou/Code/make_example/chapter8/level/dir_a/dir_b'
this is level: 1
dir_a finished!
make[1]: Leaving directory `/home/shiyanlou/Code/make_example/chapter8/level/dir_a'
this is level: 0
root dir finished!

由打印可以看出dir_b目录下MAKELEVEL值为2dir_a值为1level目录值为0

有些项目中需要利用MAKELEVEL变量的特性来进行条件编译,大家可以自己设计实验使用MAKELEVEL

实验过程如下图所示:

5.3

5.4 命令行参数和变量的传递

make的递归执行过程中,最上层make的命令行选项会被自动通过环境变量MAKEFLAGS传递给子make进程。

如前面的实验,通过命令行指定了-i-k选项,MAKEFLAGS的值会被自动设定为“ki”

需要注意的是:

1)-w选项默认会被传递给子make

2)有几个特殊的命令行选项不会被记录进变量,它们是-C -f -o-W

顶层makefile内容如下:

#this is a makefile for MAKEFLAGS test

subdir := dir_a

.PHONY:all $(subdir)

all:$(subdir)
        @echo "root dir finished!"

$(subdir):
        @echo "MAKEFLAGS before subdir is : " $(MAKEFLAGS)
        cd $@;$(MAKE)

它会在进入subdir之前打印MAKEFLAGS变量,底层makefile也同样会打印MAKEFLAGS变量。

执行make

cd ../flags/;make

终端打印:

MAKEFLAGS before subdir is : 
cd dir_a;make
make[1]: Entering directory `/home/shiyanlou/Code/make_example/chapter8/flags/dir_a'
MAKEFLAGS in dir_a is : w
dir_a finished!
make[1]: Leaving directory `/home/shiyanlou/Code/make_example/chapter8/flags/dir_a'
root dir finished!

可见顶层MAKEFLAGS变量为空,底层自动添加了-w选项。

现在可以利用这些打印测试传入不同参数。

先将makefile拷贝为test.mk,再用-f选项指定执行test.mk,并添加其它参数:

cp makefile test.mk;make -f test.mk -i -k SHELL=bash

终端打印如下:

MAKEFLAGS before subdir is :  ki -- SHELL=bash
cd dir_a;make
make[1]: Entering directory `/home/shiyanlou/Code/make_example/chapter8/flags/dir_a'
MAKEFLAGS in dir_a is : wki -- SHELL=bash
dir_a finished!
make[1]: Leaving directory `/home/shiyanlou/Code/make_example/chapter8/flags/dir_a'
root dir finished!

可见顶层makefile中,MAKEFLAGS变量为“ik -- SHELL=bash”-f选项没有添加进来。

底层makefile中,MAKEFLAGS变量为“ikw -- SHELL=bash”,除了多出默认需要传递的-w选项外,其它部分与顶层传参一致。

实验过程如下图所示:

5.4

六、实验总结

本次实验测试了make的递归执行及其过程中变量、命令行参数的传递规则。

七、课后习题

1.请测试exportunexport作用于同一个变量时,make的处理方式。

2.请使用MAKELEVEL变量进行编译流程控制。

3.请查找资料并测试$(MAKE)make在命令行参数的传递处理上有何区别并设计实验进行测试。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值