知识点
本章节将介绍make
对规矩指令的履行,指令履行进程中的过错处理以及指令包的运用。
-
make
对规矩指令的履行 -
make
的多线程履行 -
make
的过错疏忽选项 -
make
的反常完毕
本课程项目完成进程中将学习:
- $(SHELL) 履行规矩指令
- -j 选项进行多线程履行
- -、-i、-k 参数的效果
- make 反常完毕
- define
本章节的源代码位于 /home/shiyanlou/Code/make_example/chapter7/
目录下。
项目结构
项目文件结构:
├── cancel # make 的反常完毕处理
│ └── makefile
├── error # make 指令的过错处理
│ ├── iopt.mk
│ ├── kopt.mk
│ └── makefile
├── joption # make 的并行运用
│ └── makefile
├── pack # 指令包运用测验
│ └── makefile
└── shell_vari # 验证`$(SHELL)`环境变量的传递
└── shell.mk
1️⃣ make 对规矩指令的履行
SHELL
环境变量的传递
make 运用环境变量 SHELL
指定的 shell (指令解释器) 来处理规矩指令行,GNU make 中默许的 shell 是 /bin/sh
,与其它环境变量不同的是,SHELL
变量会由 GNU make 自行界说,而不会运用当时体系的同名变量。这样做的理由是:make 认为体系的 SHELL
变量适用于界说人机交互接口,make 没有交互进程,因而不适用。
shell_vari
目录下的 shell.mk
文件演示了体系环境变量 SHELL
和 make 运用的环境变量的差异, 文件内容如下:
#this is a makefile for $(SHELL) test
.PHONY:all
all:
@echo "\$$SHELL environment is $$SHELL"
@echo "\$$SHELL in makefile is " $(SHELL)
因为符号$
是变量引证的开始字符,因而要运用$
本身这个字符时需求运用$$
进行转义。 all
规矩的第一条指令是打印体系环境变量SHELL
,$$
代表$
字符,所以它的履行成果与下面这条指令的履行成果是相同的。
echo "\$SHELL environment is $SHELL"
makefile 中 @echo 与 echo 差异:echo 会在终端中先输出 echo 指令本身,再输出成果,而 @echo 是直接输出成果。详见 Difference between echo and @echo in unix shells
「\」符号是终端下的转义字符,「」符号在终端下同样是变量引证的开始字符,因而‘」符号在终端下同样是变量引证的开始字符,因而 `SHELL会被体系环境变量
SHELL的内容替代,而
SHELL‘会被打印为「SHELL` 会被打印为「SHELL」。all
规矩的第二条指令是打印当时 make 运用的 SHELL
变量。
输入下面的指令进入 shell_vari
目录,并检查当时体系 SHELL
变量。
cd shell_vari/;echo $SHELL
Terminal 的输出成果如下图:
履行下面的指令检查,make 在履行 makefile 规矩时所运用到的 shell。
make -f shell.mk
Terminal 的输出成果如下图:
从输出成果上看到 make 在履行 makefile 的时候所运用到的 shell 程序是不同于体系默许的 shell 程序。
SHELL
变量传参
接下来测验试验在履行 make 时,传入值为 abc 的 SHELL 变量。
make -f shell.mk SHELL=abc
Terminal 的输出成果如图所示:
可见make
尝试用咱们传入的abc
来履行规矩成果因为找不到abc
导致履行失利。
这阐明 ‘make‘本身的‘SHELL‘变量也是能够经过传参进行修正的。\color{red}{`make`本身的`SHELL`变量也是能够经过传参进行修正的。}
2️⃣ make 的多线程履行
make 也能够运用多线程进行并发履行,运用方法为履行 make 时参加指令行选项 -jN
,其中 N 为一个数字,表明要履行的线程数。而 make 的每个线程会履行一个规矩的重建,每条规矩只由一个线程履行。当不运用 -j
选项时 make 的履行为单线程编译。
经过 chapter7/joption
文件夹中的 makefile 文件可对 make 的多线程进行验证,makefile 文件的内容如下:
#this is a makefile for -j option
.PHONY:all
all:aim1 aim2 aim3 aim4
@echo "build final finish!"
aim%:
@echo "now build " $@
@sleep 2
@echo "build " $@ " finish!"
从内容上看最终方针 all
有 aim1
到 aim4
四个依靠项,每个依靠项的规矩一致,打印信息并睡觉两秒。进入 joption
目录,并履行 make 指令,完成编译需求 8 秒。
运转截图如下:
单线程履行
为了能更直观的看到时刻变化进程咱们能够修正 makefile 的内容,咱们能够为每一个输出进程增加一个时刻戳,修正后的内容如下:
#this is a makefile for -j option
.PHONY:all
all:aim1 aim2 aim3 aim4
@echo "build final finish!"
aim%:
@echo "now build $$(date +%T)" $@
@sleep 2
@echo "build " $@ " finish! $$(date +%T)"
先履行 make 指令检查在单线程下编译的状况,Terminal 的输出如下图:
从输出成果能够看出,从开端编译 aim1
到 aim4
编译完毕,一共耗时 8 秒。
多线程履行
接下来,经过增加 -j2
选项检查在多线程下的履行状况。
make -j2
Terminal 的输出成果如图:
能够看出 从开端编译 aim1
到 aim4
编译完成一共耗时 4 秒比单线程所消耗的时刻减少了一半。
大家能够自己再测验一下三线程和四线程的并行履行进程,并尝试在方针中参加依靠项来限制并行编译的次序。
参阅:# 在Makefile中强制依靠项次序
关于Makefile的一个留意事项:多线程编译犯错总结
然后总结出来的3线程次序:
#this is a makefile for -j option
.PHONY:all
all:all2 all4 all1 all3
@echo "build final finish!"
all2:aim1
all4:aim2
all1:aim3
all3:aim4
aim%:
@echo "now build $$(date +%T)" $@
@sleep 2
@echo "build " $@ " finish! $$(date +%T)"
总结出来的四线程次序: 想不出好办法了,用的依靠
#this is a makefile for -j option
.PHONY:all
all:aim1 aim2 aim3 aim4
@echo "build final finish!"
aim4:aim1 aim2 aim3
aim3:aim1 aim2
aim2:aim1
aim%:
@echo "now build $$(date +%T)" $@
@sleep 2
@echo "build " $@ " finish! $$(date +%T)"
3️⃣ make 的过错疏忽选项
make
履行进程犯错的简略测验
下面咱们来看一下 make 履行犯错的状况。 在 chapter7/error/
目录下的 makefile
文件对 rm
指令的不同履行状况进行了描述,其内容如下:
#this is a makefile for error handle test
.PHONY:all
all:pre_a pre_b pre_c
$(RM) pre_a
$(RM) pre_b
$(RM) pre_c
$(RM) d
-rm e
rm f
rm g
pre_%:
touch $@
从内容上看前面三条指令是删去生成的文件,后边四条指令则是删去不存在的文件,而在 shell 运用 rm
会直接运转失利。
现在进入到 error
文件夹下并履行此 makefile 并调查履行状况:
cd ../error/;make
Terminal 的输出成果如图:
从输出成果上进行分析 make 在运转规矩指令完毕后会检测指令履行的回来状况,回来成功则启动另外一个子 shell 来履行下一条指令。在 makefile 履行进程中,先生成 pre_a
、pre_b
、pre_c
三个文件,再运用 rm -f
或者 rm
指令来删去它们,这个进程是没有问题的。
但是第四条指令是删去不存在的文件 d
,因为运用了 -f
参数,因而 shell 也不会回来过错。
第五条指令是删去不存在的文件 e
,因为指令行开始处运用了符号「-」,所以 make 会疏忽此指令的履行过错,所以 shell 尽管回来并打印过错,但 make 持续往下履行。
第六条指令是删去不存在的文件 f
,因为只运用了 rm
指令,shell 回来过错,make 收到过错后不再往下履行,因而第七条指令现已没机会履行到。
在某些状况下,用户期望 make 遇上过错能够持续往下履行。在多人维护的巨大工程中,makefile 文件随时可能呈现过错,这时用户期望它能持续履行下去便利测验自己的模块,而不是被其他人的过错阻塞住。此刻能够运用 -i
选项,-i
选项会让 make 疏忽一切的过错。
经过提供的 iopt.mk
文件能够对 -i
选项的用法进行演示,iopt.mk
文件内容如下:
#this is a makefile for error handle test
.PHONY:all
all:
rm a
rm b
rm c
rm d
make 会直接调用 rm
指令删去四个不存在的文件,因而每一条指令都会回来过错。
现在履行 make 指令先并调查不运用 -i
选项时文件履行的成果。
make -f iopt.mk
Terminal 的输出成果如下图:
接下来履行加上 -i
选项再履行 iopt.mk
文件。
make -f iopt.mk -i
Terminal 的输出成果如下图:
能够看到尽管前一条指令履行时发生了过错,但是后边的指令仍然得到了履行,并没有就此停止履行。
留意:当make遇上依靠项不存在时,‘−i‘选项就不管用了,它不归于指令行过错。\color{red}{留意:当 make 遇上依靠项不存在时,`-i` 选项就不管用了,它不归于指令行过错。}
提供的 kopt.mk
文件能够用来对依靠文件过错的状况进行验证,其内容如下:
#this is a makefile for error handle test
.PHONY:all
all: h i j
@echo "exe OK!"
履行 make -f kopt.mk -i
时 Terminal 的输出成果如下图:
履行 make -f kopt.mk
时 Terminal 的输出成果如下图:
比照两种状况的输出成果,阐明在依靠项过错中 -i
选项没有任何效果。
关于这种状况能够运用 -k
选项让其疏忽依靠项过错并持续履行,现履行 make 指令。
make -f kopt.mk -k
Terminal 的输出成果如图所示:
-k
选项能够让make
持续检查其它依靠项,但并不会履行终极方针的指令。 若有多个依靠项被修正过后,能够运用此选项测验哪些依靠项的修正有问题。
请谨慎运用-i
和-k
选项,避免发生预期外的过错。
4️⃣ make 的反常完毕
make 若收到致命信号被停止时,它会删去此进程中现已重建的方针文件,避免方针文件呈现预期外的过错。例如某个方针规矩需求对方针文件进行屡次处理,处理到一半时 make 被停止,导致方针文件处于反常状况, 此刻 make 会删去此文件避免发生难以察觉的问题。
chapter7/cancel/
目录下的 makefile 文件用于对 make 反常完毕的状况的验证,内容如下:
#this is a makefile for cancel handle
.PHONY:all clean
all:clean pre_a pre_b pre_c
sleep 1
@echo "exe target all!"
clean:
$(RM) pre_*
pre_%:
@echo "\n"
touch $@
@echo "generate " $@
@ls -l $@
@echo "sleep 5s before finish..."
sleep 5
最终方针 all
依靠于 pre_a
、pre_b
、pre_c
文件,这三个文件在树立进程中会 sleep 五秒钟,便利用户完毕 make 指令。
现在进入到 cancel
文件夹中履行 make 指令不作其他任何操作并调查输出成果。
cd ../cancel;make
Terminal 的输出成果如下图:
此刻经过 ls
指令检查当时目录下新文件的发生状况。
可见 pre_a
、pre_b
、pre_c
三个文件被被成功的生成。
手动模仿 make 履行进程中反常发生
接下来手动模仿 make 履行进程中反常的发生,在 pre_b
文件被生成的进程中,手动键入 ctrl + c
来停止程序的持续履行。
现在经过 ls
指令检查当目录中文件的状况。
从输出成果能够看出,pre_b
现已被删去,从上一张 make 的履行进程的图片中也能够看出在反常中断的发生进程中 pre_b
文件就被删去了。
5️⃣ 指令包的运用(c函数)
书写makefile
时,可能有多个规矩会运用一组相同的指令,就像c
言语要调用函数一样, 咱们能够运用define
来完成这项功能。
define
以“define”
最初,以“endef”
完毕,效果与c
言语的宏界说类似。
chapter7/pack/
目录下演示了指令包的运用方法,其内容如下:
#this is a makefile for define test
.PHONY:all
define echo-target
@echo "now rebuilding target : " $@
touch $@
endef
all:pre_a pre_b pre_c
@echo "final target finish!"
pre_%:
$(echo-target)
从文件内容上看最终方针 all
的依靠项都会调用同一组指令包。进入 pack
目录并测验履行效果:
cd ../pack;make
Terminal 的输出成果如图所示:
能够看出在 all
的每个依靠的指令部分都履行了 define
界说的指令包。
本章节测验了 make 对规矩指令的履行,指令履行进程中的过错处理以及指令包的运用方式。