Makefile笔记

2 篇文章 0 订阅

规则格式

一个 Makefile 通常由一条或若干条规则组成,每条规则格式通常如下:

target … : prerequisites …
        recipe
        …
  1. target: 通常是文件名或要执行的动作名
    • 单个文件
    • 多个文件,使用空格分隔
    • 使用通配符,表示多个文件名
    • 压缩包中的文件: archive(member)
    • 要执行的动作名,参见:伪目标
  2. prerequisites: 通常是目录文件所依赖的文件
    • 可以为空,表示目录不依赖任何文件
    • 多个文件使用空格分隔,当一行太长时可以使用 \ 分割成多行写
    • 也可以使用通配符,表示多个文件
    • 也可是压缩包中的文件
  3. recipe: 通常是产生目标文件或执行动作名,所执行的具体命令

.PHONY target

.PHONY: target定 target 伪目标,伪目标不是文件的真正名称,它只时用来显示的执行一系列recipe的名称。
使用伪目标有两个原理: 避免与同名文件冲突,并提高性能。

避免与同名文件冲突

假如你在Makefile文件中定义了如下 1 条规则:

clean:
    rm *.o temp

因为该规则的recipe不会创建名为 clean 的文件,所以当你执行mingw32-make clean命令时,该规则的recipe(rm 命令)将总是被执行

但是,如果文件夹存在一个名称clean的文件,你再执行mingw32-make clean命令时,
又因 clean 依赖任何文件,所以不会更新。recipe里的 rm 命令不会被执行。
为了让每次执mingw32-make clean命令时,都执行recipe里的 rm 命令(即使当前目录下存在名为clean的文件),就需将 clean 定义成伪目标:

.PHONY: clean
clean:
    rm *.o temp
提高性能

假如当前文件目录如下:

.
├── Makefile
├── Makefile_phony
├── bar
│   └── Makefile
├── baz
│   └── Makefile
└── foo
    └── Makefile

根目录下的Makefile_phony是将subdirs目标定义为伪目标的规则文件,根目录下有 3 个文件夹,每个文件夹有自己的规则文件。
规则文件对应内容如下(注:因为以下 Makefile 中使用了 shell 的 for 和 sleep 等,所以建议在 bash 下执行):

$ cat ./Makefile
SUBDIRS = bar foo baz

subdirs:
        @for dir in $(SUBDIRS); do \
        $(MAKE) -C $$dir; \
        done

$ cat ./Makefile_phony
SUBDIRS = bar foo baz

.PHONY: subdirs $(SUBDIRS)

subdirs: $(SUBDIRS)

$(SUBDIRS):
        @$(MAKE) -C $@

$ cat ./bar/Makefile
bar:
        @sleep 1
        $(error some error in $@)
        @echo $@

$ cat ./baz/Makefile
baz:
        @sleep 2
        @echo $@

$ cat ./foo/Makefile
foo:
        @sleep 3
        @echo $@

我在./bar/Makefile中模拟了一个错误,分另用用单线程和多线程对根目录下的规则文件进行make

1. 单线程 make 对比
$ mingw32-make
mingw32-make[1]: Entering directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/bar'
Makefile:2: *** some error in bar.  Stop.
mingw32-make[1]: Leaving directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/bar'
mingw32-make[1]: Entering directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/foo'
foo
mingw32-make[1]: Leaving directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/foo'
mingw32-make[1]: Entering directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/baz'
baz
mingw32-make[1]: Leaving directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/baz'

可以看出即使在 make ./bar/Makefile 时出错了,后续的两个子项目仍然依次 make 了

$ mingw32-make -f ./Makefile_phony
mingw32-make[1]: Entering directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/bar'
Makefile:2: *** some error in bar.  Stop.
mingw32-make[1]: Leaving directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/bar'
Makefile_phony:8: recipe for target 'bar' failed
mingw32-make: *** [bar] Error 2

可以看出伪目标在子项目出错时,主项目就立即停止。且主 make 会显示哪个子项目出错了

2. 多线程 make 对比
$ mingw32-make -j4
mingw32-make[1]: Entering directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/bar'
Makefile:2: *** some error in bar.  Stop.
mingw32-make[1]: Leaving directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/bar'
mingw32-make[1]: Entering directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/foo'
foo
mingw32-make[1]: Leaving directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/foo'
mingw32-make[1]: Entering directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/baz'
baz
mingw32-make[1]: Leaving directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/baz'

可以看出这儿单线程和多线程 make 是无差别的,都是依次 make 每个子项目

$ mingw32-make -f ./Makefile_phony -j4
mingw32-make[1]: Entering directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/foo'
mingw32-make[1]: Entering directory 'C:/Usmingwe3r2s-/make[1]: Entering directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/bar'
ktoMp/aVskCodee_wfxWiidgletes/b:az'3
: *** some error in bar.  Stop.
mingw32-make[1]: Leaving directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/bar'
Makefile_phony:8: recipe for target 'bar' failed
mingw32-make: *** [bar] Error 2
mingw32-make: *** Waiting for unfinished jobs....
baz
mingw32-make[1]: Leaving directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/baz'
foo
mingw32-make[1]: Leaving directory 'C:/Users/TGL/Desktop/VsCode_wxWidgets/foo'

可以看出多线伪目标在多线程 make 时,同时启用了多个线程来 make 子项目,项目的总时间缩短,且子项目出错主项目就会停止 make

recipe 命令写法带来的差异

我们以下面这个 Makefile 文件为例来讲解一些特殊情况下的单行命令和和多行命令的区别

Makefile
.PHONY: test1 test2

test1:
	@echo "-------------两条命令写在两只一行-----------------"
	cd subdir; pwd

test2:
	@echo "-------------两条命令分别写在两行-----------------"
	cd subdir
	pwd

在该 Makefile 中创建了两个虚拟动作"test1"和"test2"两个动作都是执行相同的两条命令。
唯一的区别就是:一个是把两条命令写在同一行,而另一个是把两条命令写在了两行。

mingw32-make test1 执行结果
TGL233@TGL-ThinkPad MINGW64 ~/Desktop/Hello_wxWidgets
$ mingw32-make test1
-------------两条命令写在两只一行-----------------
cd subdir; pwd
/c/Users/TGL233/Desktop/Hello_wxWidgets/subdir
mingw32-make test2 执行结果
TGL233@TGL-ThinkPad MINGW64 ~/Desktop/Hello_wxWidgets
$ mingw32-make test2
-------------两条命令分别写在两行-----------------
cd subdir
pwd
/c/Users/TGL233/Desktop/Hello_wxWidgets

从上面示例的执行结果可以看出:
写在同一行时执行完cd subdir命令后 make 并没有返回当前目录,而是留在 subdir 目录下继续执行了pwd命令所以输出是 subdir 目录。
而分别写在两行时,在执行完cd subdir命令后 make 返回到当前目录了,所以再执行pwd命令时就输出是当前目录。

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值