每条规则中的命令和操作系统Shell的命令行是一致的。make会按照顺序一条条的执行命令,每条命令的开头必须以tab
键开头的,那么make会认为是一条空命令
我们在UNIX下可能会使用不同的shell,但是make的命令默认是被/bin/sh
–UNIX的标准shell解释执行的。除非你特别指定一个其他的shell。
显示命令
通常make会把需要执行的明林个在命令执行前输出到屏幕上,当我们用@
字符在命令行前,那么这个命令不被make显示出来,最具代表性的是如下
@echo 正在编译。。。
当make命令执行时,会输出正在编译
字体到屏幕上,但是不会输出命令,如果没有@
字符,则make会输出如下的内容
echo 正在编译。。。
正在编译。。。
如果make执行时,带入make参数-n
或者--just-print
,那么指示显示命令,并不会执行,这个功能很有利于我们调试我们的Makefile
而make参数-s
或者--slient
则是全面禁止命令的显示
命令执行
当依赖目标新于目标target时,make回一条条的执行其后的命令,如果我们想要让上一条命令的结果应用在下一条命令,我们应该使用分号分离这两条命令。例如一条命令是cd,另一条命令如果希望是在cd之后再去执行,我们就要把命令写成两行或者写成一行,但是用分号隔开
#第一种情况
exec:
cd /paul/tmp
pwd
#第二种情况
exec:
cd /paul/tmp;pwd
当我们执行make exec
时,第一个例子的cd没有作用,pwd会打印当前的Makefile目录,而第二个例子中,cd就起作用了,pwd会打印出/paul/tmp
。
make一般使用环境变量shell中所定义的系统shell来执行命令,默认情况下使用UNIX的标准shell–/bin/sh
来执行命令。
命令出错
当命令运行完之后,make会检测每个命令的返回码,如果命令返回成功,那么make会执行下一条命令,当规则中所有的命令成功返回后,这个规则就算是成功完成了。如果一个规则中的某个命令出错了,那么make就会终止执行当前规则,这有可能终止所有规则的执行。
为了做到这一点,忽略命令的出错,我们可以在Makefile的命令行前加一个减号-
,标记为不管命令处不出错都认为是成功的。
clean:
-rm -f *.o
还有一个全局的方法,给make加上-i
或者--ignore-errors
参数,那么Makefile重所有命令都会忽略错误。而如果这个规则是以.INGNORE
作为目标的,那么这个规则中的所有命令都将忽略错误。
嵌套命令
在做一些大工程中,我们会把我们不同模块或者不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile文件,这有利于我们让我们的Makefile变得更加简洁,同时也可以节省我们编译的时间,因为可以省去编译其他没有改变的模块的时间
例如:我们有一个子目录叫做subdir,这个目录下有一个Makefile文件,来指明这个目录的编译规则,那么我们总的Makefile文件可以这样写
subsystem:
cd subdir && $(MAKE)
等价于
subsystem:
$(MAKE) -c subdir
定义$(MAKE)宏变量的意思是,也许我们的make需要一些参数,所以定义成一个变量比较利于维护,这两个例子的意思都是先进入subdir目录,然后执行make命令
我们可以把这个makefile叫做总控Makefile,总控Makefile中的变量可以传递到下一级的Makefile文件中,但是不会覆盖下层Makefile中所定义的变量,除非指定了-e
参数
如果我们要传递变量到下一级的Makefile,那么你可以使用这样的声明
export <variable...>
相反,如果不想让某些变量传递到下一级的Makefile中,那么我们可以这样申明
unexport <variable...>
例如
exprot variable = value
#等价于
variable = value
export variable
#等价于
exprot variable := value
#等价于
variable:=value
exprot variable
如果我们需要将所有的变量都传递过去,直接写一个exprot即可。
注意SHELL和MAKEFILES,这两个变量,不管你是否export,总是要传递到下一层的Makefile中,特别是MAKEFILES变量,其中包含了make的参数信息,如果我们执行总控Makefile是有make参数或者是上层的Makefile中定义了这个变量,那么MAKEFILES变量将会是这些参数,并且传递到下层Makefile中,这是一个系统级的环境变量。
定义命令包
如果Makefile中出现一些相同命令序列,我们可以为这些相同命令序列定义一个变量,定义这种命令序列的语法以define
开始,以endef
结束
define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef
这里的run-yacc
是这个命令包的名字,不能喝Makefile中的变量重名。在define
和endef
中的两行就是命令序列。这个命令包中的第一个命令是yacc
,编译。因为yacc
总是为生成一个y.tab.c
的文件,所以第二行的命令就是把这个文件改名字,如果我们想要使用,可以直接像调用变量一样的使用,如下
main.c:main.y
$(run-yacc)