Makefile基础教程 3

一、实验介绍--Makefile 基础规则(2)

本实验在上一个实验的基础上,继续深入介绍makefile的基础规则。

1.1 实验内容

  1. 验证makefile的自动推导规则。
  2. 验证makefile include文件规则。
  3. 验证makefile环境变量MAKEFILES,MAKEFILE_LIST.VARIABLES的作用。
  4. 测试makefile的重载。

1.2 实验知识点

1.makefile文件不存在的情况下也可以利用make的自动推导规则实现代码编译。

2.include指示符可以让make读入其指定的文件。

3.include指定文件时可以支持通配符。

4.include的默认查找路径:/usr/gnu/include/usr/local/include/usr/include

5.include可以用-I选项指定查找路径。

  1. 变量MAKEFILES可以指定需要读入的makefile文件。
  2. 变量MAKEFILES的使用限制:不可作为终极目标。
  3. 变量MAKEFILE_LIST的作用:将"MAKEFILES",命令行指定,默认makefile文件及“include”指定的文件名都记录下来。9.makefile重载另一个makefile的限制条件:规则名称不得重名。10.makefile的“所有匹配模式”的使用。

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.1makefile的自动推导规则。

5.1.1 抓取源代码

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

cd ~/Code/
git clone https://github.com/darmac/make_example.git
cd make_example/chapter2
5.1.2makefile自动推导规则说明

makefile 有一套隐含的自动推导规则:

  1. 对于xxx.o目标会默认使用命令“cc -c xxx.c -o xxx.o”来进行编译。
  2. 对于xxx目标会默认使用命令“cc xxx.o -o xxx”

下面用两个小实验来验证makefile的自动推导规则。

5.1.3 编写main.c源文件

代码中已有main.c文件,内容如下:

#include <stdio.h>

int main(void)
{
        printf("Hello world!\n");
        return 0;
}
5.1.4 使用make main.o验证规则

确认当前目录下没有makefile类型的文件。

输入如下命令:

make main.o

终端打印:

cc    -c -o main.o main.c

说明make自动使用cc -c命令生成了main.o文件。

5.1.5 使用make main验证规则

接下来验证另一条规则,输入如下命令:

make main

终端打印:

cc   main.o   -o main

说明make自动使用cc命令生成了main文件。

由于main.o文件是上一个小实验生成的,现在我们删掉它和main文件:

rm main.o main

再次输入:

make main

终端打印:

cc     main.c   -o main

这说明当main.o不存在时,make 会尝试直接使用源文件编译来生成目标文件。

实验过程如下图所示:

5.1

5.2makefile include使用规则

makefile 中可以使用include指令来包含另一个文件。

make识别到include指令时,会暂停读入当前的makefile文件,并转而读入include指定的文件,之后再继续读取本文件的剩余内容。

5.2.1 编写makefile需要包含的文件

makefile_dir目录下有一份需要被包含的文件inc_a,文件内容如下:

#this is a include file for makefile

vari_c="vari_c from inc_a"
5.2.2 编写基本的makefile文件

拷贝makefile_dir目录下的makefile文件到当前目录:

cp makefile_dir/makefile ./

makefile内容如下:

# this is a basic makefile

.PHONY:all clean

vari_a="original vari a"
vari_b="original vari b"

include ./makefile_dir/inc_a

all:
        @echo $(vari_a)
        @echo $(vari_b)
        @echo $(vari_c)

clean:
5.2.3 测试 make 能否正常工作

执行指令:

make

终端打印:

original vari a
original vari b
vari_c from inc_a

从打印信息可以看出来makefile已经成功包含了inc_a文件,并且正确获取到了vari_c变量。

值得一提的是include指示符所指示的文件名可以是任何shell能够识别的文件名,这表明include还可以支持包含通配符的文件名。我们将在下面的实验中进行验证。

5.2.4 新建另一个被包含文件

makefile_dir 目录下有一份需要被包含的文件inc_b`,文件内容如下:

#this is a include file for makefile

vari_d="vari_d from inc_b"
5.2.5 使用通配符让makefile包含匹配的文件

修改makefile,使用通配符同时包含inc_ainc_b文件。

修改后的makefile内容如下:

# this is a basic makefile

.PHONY:all clean

vari_a="original vari a"
vari_b="original vari b"

include ./makefile_dir/inc_*

all:
        @echo $(vari_a)
        @echo $(vari_b)
        @echo $(vari_c)
        @echo $(vari_d)

clean:

执行:

make

终端打印出:

original vari a
original vari b
vari_c from inc_a
vari_d from inc_b

说明文件inc_ainc_b被同时包含到makefile中。

5.2.6makefile include文件的查找路径

include指示符包含的文件不包含绝对路径,且在当前路径下也无法寻找到时,make 会按以下优先级寻找文件:

1.-I指定的目录

2./usr/gnu/include

3./usr/local/include

4./usr/include

5.2.7 指定makefileinclude路径

修改makefile,不再指定inc_ainc_b的相对路径:

再执行一次make

make

终端打印:

makefile:8: inc_*: No such file or directory
make: *** No rule to make target 'inc_*'.  Stop.

可以看到makefile无法找到inc_ainc_b文件。

使用“-I”命令来指定搜寻路径:

make -I ./makefile_dir/

终端依然打印:

makefile:8: inc_*: No such file or directory
make: *** No rule to make target 'inc_*'.  Stop.

看起来make在搜寻“inc_*”档案。

修改makefile,将“inc_*”改为"inc_a"``"inc_b"

include inc_a inc_b

执行:

make -I ./makefile_dir/

终端打印:

original vari a
original vari b
vari_c from inc_a
vari_d from inc_b

可见不使用通配符的情况下include配合-I选项才能得到预期效果。

5.2.8makefile include的处理细节

下面再研究一下makeinclude指示符的处理细节。

前面提到make读入makefile时遇见include指示符会暂停读入当前文件,转而读入include指定的文件,之后才继续读入当前文件。

拷贝文件makefile_dir/makefile_b到当前目录并命名为makefile

cp makefile_dir/makefile_b ./makefile

查看makefile的内容:

#this makefile is test for include process

.PHONY:all clean

vari_a="vari_a @ 1st"

include ./makefile_dir/c_inc

vari_a += " @2nd ..."

all:
        @echo $(vari_a)

clean:

可以看出makefile是先设定vari_a变量,再包含c_inc文件,之后再修改vari_a变量。

查看c_inc文件内容:

#this is a include file for include process

vari_a="vari_a from c_inc"

可以看出c_inc文件中也设定了vari_a变量。

执行make看最终vari_a变量定义为什么:

make

终端打印:

vari_a from c_inc  @2nd ...

这说明vari_ainclude过程中被修改掉,并在其后添加了字串" @2nd ...",结果与预期中make处理include指示符的行为一致。

实验过程如下图所示:

5.2A

5.2B

5.2C

5.2D

5.2E

5.3makefile的几个通用变量测试

5.3.1 测试MAKEFILES变量指定的文件是否能正确被包含

MAKEFILES 环境变量有定义时,它起到类似于include的作用。

该变量在被展开时以空格作为文件名的分隔符。

删掉当前makefile文件:

rm makefile

新建makefile内容如下:

#this makefile is test for include process

.PHONY:all clean

vari_a += " 2nd vari..."

all:
        @echo $(vari_a)

clean:

执行 make

make

终端打印:

 2nd vari...

增加环境变量MAKEFILES

export MAKEFILES=./makefile_dir/c_inc

再次执行make

make

终端打印:

vari_a from c_inc  2nd vari...

可见make按照MAKEFILES的文件列表载入了makefile_dir/c_inc文件。

5.3.2 测试MAKEFILES变量的使用限制

需要注意:

1.MAKEFILES指定文件的目标不能作为make的终极目标。

2.MAKEFILES是环境变量,它对所有的makefile都会产生影响,因此尽量不要使用该变量。

新建一个文件aim_b_file,内容如下:

#this is aim_b file

.PHONY:aim_b

aim_b:
        @echo "now we exe aim_b"

此文件定一个aim_b规则,执行此规则时打印“now we exe aim_b”

修改MAKEFILES变量:

export MAKEFILES=./aim_b_file

执行make

make

终端打印:

 2nd vari...

可见make虽然先包含aim_b_file文件,但依然以makefile中的all作为最终目标。

我们再来验证aim_b规则是否已经被正常解析到,修改makefile,为all增加一条依赖:

all: aim_b

这样,执行all规则之前必须先执行aim_b规则。

执行make

make

终端打印:

now we exe aim_b
 2nd vari...

再执行:

make aim_b

终端打印:

now we exe aim_b

“make”“makeaim_b”的打印都说明aim_b已经能够被正确执行,但它的确不会作为默认的目标规则,只有明确指定此规则时才会执行其对应的命令。

5.3.3 打印MAKEFILE_LIST

所有MAKEFILES指定的文件名,命令行指定的文件名,默认makefile文件以及include指定的文件名都记录下来。

当前路径下总共有./aim_b_file./makefile./makefile_dir/inc_a./makefile_dir/inc_b./makefile_dir/c_inc这5个文件。

现在我们使用不同的方式将它们包含进来。

./aim_b_file已经被包含在MAKEFILES变量中。

./makefile会在执行make时被自动调用。

修改makefileinclude指示符包含文件./makefile_dir/inc_a和./makefile_dir/inc_b

并在all目标中打印MAKEFILE_LIST变量,修改后的makefile`内容如下:

#this makefile is test for include process

.PHONY:all clean

include ./makefile_dir/inc_a ./makefile_dir/inc_b

vari_a += " 2nd vari..."

all:
        @echo $(vari_a)
        @echo $(MAKEFILE_LIST)
clean:

执行 make

make

终端打印:

now we exe aim_b
 2nd vari...
./aim_b_file makefile makefile_dir/inc_a makefile_dir/inc_b

第二行打印内容说明MAKEFILE_LIST已经包含了./aim_b_file makefile makefile_dir/inc_a makefile_dir/inc_b

实验过程如下图所示:

5.3A

5.3B

5.3C

5.3D

5.4 重载另一个makefile

5.4.1 使用make -f 重载另一个makefile

现在拷贝makefile文件为inc_test

cp makefile inc_test

再使用make -f命令指定需要读取的makefile文件为inc_test

make -f inc_test

终端打印:

now we exe aim_b
 2nd vari...
./aim_b_file inc_test makefile_dir/inc_a makefile_dir/inc_b

可见原来默认执行的makefile文件被替换成了inc_test文件,且被MAKEFILE_LIST正确记录。

5.4.2 测试重载makefile的限制条件

makefile重载另一个makefile的时,不允许有规则名重名。

若是有规则发生重名会发生什么状况呢?

修改aim_b_file增加all规则:

all:
        @echo "all in aim_b"

执行:

make

终端打印:

makefile:10: warning: overriding commands for target `all'
./aim_b_file:9: warning: ignoring old commands for target `all'
now we exe aim_b
 2nd vari...
./aim_b_file makefile makefile_dir/inc_a makefile_dir/inc_b

从打印日志中可以看出makefile重写了aim_b_file文件中的all规则。

5.4.3 用“所有匹配模式”重载另一个makefile

从上面的实验中可以看出,对于两个文件中同名的规则,make后读入的规则会重写先读入的规则。

现在假如有两个makefile文件,AMakeBMake,它们都定义了一条intro规则,但行为不同。

用户希望执行在生成目标AAimBAim的时候分别调用AMakeBMakeintro规则,要怎样来做呢?

我们无法用include指示符来包含这两个makefile,否则会产生重写规则的行为。

此时需要用到重载另一个makefile的技巧。

具体方法就是在对应的规则中重新调用make并传入需要重载的makefile文件名及目标名。

chapter2/makefile_dir/目录底下的makefile_c`` AMake`` BMake 这三个文件可以演示我们所需的功能。

先拷贝三个文件到当前目录下:

cp makefile_dir/makefile_c makefile
cp makefile_dir/AMake ./
cp makefile_dir/BMake ./

查看makefile文件,内容如下:

#this is a makefile reload example main part

.PHONY:AAim BAim

AAim:
        make -f AMake intro

BAim:
        make -f BMake intro

当目标为AAim时,会执行“make -f AMake intro”

也就是会重载AMake作为makefile文件并执行intro规则。

BAim的处理方式也类似。

现在测试一下执行效果,执行:

make AAim

终端打印:

make -f AMake intro
make[1]: Entering directory '/root/study/make_example/chapter2'
Hello, this is AMake
make[1]: Leaving directory '/root/study/make_example/chapter2'

可见AMake下的intro规则的确被执行到了。

再执行BAim规则:

make BAim

终端打印:

make -f BMake intro
make[1]: Entering directory '/root/study/make_example/chapter2'
Hello, this is BMake
make[1]: Leaving directory '/root/study/make_example/chapter2'

BMake的规则也被顺利执行。

上述部分是基本的重载方式。

现在我们在多一条需求,希望其它未定义规则都要执行另一条intro规则,此规则定义在CMake文件中。

为了匹配其它所有的未定义规则,我们需要用到通配符“%”。

修改makefile在文件最后加入“所有匹配模式”规则:

%:
        make -f CMake intro

并将makefile_dir/CMake文件拷贝到当前目录下:

cp makefile_dir/CMake ./

随便执行一条规则AAA

make AAA

终端打印:

make -f CMake intro
make[1]: Entering directory '/root/study/make_example/chapter2'
Hello, this is CMake
make[1]: Leaving directory '/root/study/make_example/chapter2'

说明这条未定义的规则最后会重载CMake并执行其intro规则。

实验效果如图所示:

5.4A

5.4B

5.4C

六、实验总结

本实验验证了makefile的自动推导规则,一些环境变量的使用,include指示符的使用和限制,以及makefile重载的技巧。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值