赞
踩
makefile规则?
target...:prerequisites...
command #每个command签名必须要有一个tab
...
...
含义:生成target文件或者标签需要prerequisites所要求的文件或者标签,其生成规则在command中
换句话说:
实例?
target:可以是标签比如clean,可以是中间目标文件比如:utils.0,可以是执行程序比如:edit
把上面的内容保存在**"makefile"或”Makefile“**中;在该目录下 ”make“可以生成edit可执行文件(make不会执行clean后面的命令,clean不是一个文件名字只是一个动作名字);”make clean“ 会执行clean:后面的命令
总结:prerequisites表明了target文件是由哪些依赖文件按照哪些规则command生成更新的,当prerequisites文件的时间大于target说明target需要重新更新了。
make是如何工作的?
几个重要的点:
make 时会找到makefile或者Makefile文件中第一个target,并作为最终目标文件。
在生成一个target时会先检查依赖prerequisites是不是存在或者比target旧(即prerequisites对应target中command生成的文件是不是更改了)
如果command部分没有生成任何文件,则检查到该依赖时都会运行该command部分如下:
some_file:other_file
touch some_file
other_file:
echo "nothing"
无论运行多少次make时这个两个target都会运行,因为other_file中没有创建任何文件,如果other_file更改为
other_file:
echo "this will run first"
touch other_file
第一make会运行这两个target,第二次运行make时如果other_file没有更改时则不会重新创建some_file
以上注意command创建的文件名必须与target一致这样才能追踪到特定的文件,检查文件的修改情况
变量?
变量只能是字符串,可以用**:=或者=赋值** ;可以用** 或 者 {}或者 或者()引用**;可以理解为C语言的宏
多target:
其中$@表示是当前rule的target名;
其他小规则?
Makefile中只有行注释#,如果想在Makefile中使用“#”字符,可以用反斜框进行转义,如:“#”
引用其他Makefile?
在Makefile使用include关键字可以把别的Makefile包含进来. 会把包含的Makefile文件中内容安置在当前的位置。
以上包含的文件会在当前目录下首先寻找,如果当前目录下没有找到,那么,make还会在下面的几个目录中找。
书写规则分为两个部分:一个是依赖关系,另一个是生成目标的方法
1. 生成的最终目标:
在一个Makefile文件中有很多规则,但是最终目标只有一个。一般是Makefile文件中的第一个规则的第一个目标作为最终目标
**注释:**上面这句话有两个第一个,第一个规则是指Makefile文件从上到下第一个规则,第一个目标是指由于一个规则中可能有多个目标,在多个目标中从左到右数第一个目标
规则举例:
foo.o:foo.c defs.h # foo模块
cc -c -g foo.c
解释:
当依赖关系成立时会运行“cc -c -g foo.c”
依赖关系成立的条件:foo.o文件不存在 或者 foo.c defs.h文件比foo.o新当依赖关系成立后就会执行command部分
2. 书写基本格式:
targets : prerequisites
command
...
targets是文件名,以空格分开,可以使用通配符。如果一行写不下可以用反斜杠""来作为换行符 一个规则告诉Makefile文件之间的依赖关系、如何生成目标文件的命令
3. 在规则文件名中使用通配符:
*:表示匹配零个或多个任意字符
?:表示匹配一个任意字符
[…] : 表示匹配[]中的一个特定字符
如果文件名中存在*,?等字符,可以是用反斜杠\来转义。
举例说明:
```makefile
clean:
rm -f *.o
## make clean 后会删除所有.o为后缀的所有文件
print: *.c
lpr -p $?
touch print
## 依赖关系是后缀为.c的所有文件
objects = *.o
##objects变量就是*.o没有进行展开,可以认为Makefile中的变量是C中的宏
```
4.文件搜寻:
Makefile文件中特殊变量"VPATH"可以指明路径去寻找依赖文件和目标文件,否则只会在当前目录中去找寻依赖文件和目标文件
VPATH=src:../headers
## 上面定义了两个文件搜寻目录其中一个是./src和./../headers两个,搜寻优先级:1 ./ 2 ./src 3 ./../headers 目录之间用冒号分隔开
vpath关键字,比特殊变量VPATH更为灵活,它可以指定不同的文件在不同的搜索目录中
例子:
vpath %.h ../headers
# 要求make时以.h为后缀的所有文件在目录../headers中搜寻(如果某文件在当前目录没有找到的话)
vpath %.c foo
vpath % blish
vpath %.c bar
#make会按照vpath语句的先后顺序来执行搜索
5.伪目标:
"伪目标"并不是一个文件,只是一个标签,由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它是否要执行。当然,伪目标的取名不能和文件名重名,不然其就失去了“伪目标”的意义,伪目标的本质是在文件搜索目录中找不到该名字的文件
伪目标地特性:1. 总是被执行 2. 可以通过"make 伪目标名"执行伪目标 3. 伪目标可以有依赖关系,且依赖关系中可以是目标也可以是伪目标.
“.PHONY”来显示地指明一个目标时“伪目标”,想make说明,不管是否有这个文件,这个目标就是“伪目标”
.PHNOY : clean
clean:
rm *.0 temp
伪目标中也可以有依赖关系(依赖关系中可以是目标也可以是伪目标),伪目标也可以作为最终目标(但是必须放在makefile文件中的第一个规则)
all : prog1 prog2 prog3
.PHONY : all
prog1 : prog1.o utils.o
cc -o prog1 prog1.o utils.o
prog2 : prog2.o
cc -o prog2 prog2.o
prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o
##make all
可以通过伪目标的方式,不同程度的编译或者说不同程度的执行
make cleanall=>会执行"cleanobj"伪目标和"cleandiff"伪目标以及"rm program"指令
make cleanobj=>会执行“rm *.o”指令
make cleandiff=>会执行"rm *.diff"指令
从而可以根据伪目标名实现不同程度的清理工作。
6.多目标:
Makefile的规则中的目标可以不止一个,其支持多目标,有可能我们的多个目标同时依赖于一个文件,并且其生成的命令大体类似。
make一般是使用环境变量SHELL中所定义的系统shell来执行命令。
寻找命令解释器的过程: 1. 首先会在SHELL所指定的路径中找寻命令解释器 2.当前目录下找寻命令解释器 3.会在PATH环境变量中所定义的所有路径中寻找。
1. 显示命令:
通常make会把要执行的命令行在命令执行前输出到屏幕上,当我们用“@”字符在命令行前,那么,这个命令将不会被make显示出来
clean:
@echo "正在编译xxx模块"
##make clean其结果为
## 正在编译xxx模块
由于一条指令在执行时会有两个步骤(1.命令行打印 2.命令执行信息)其中 make的参数有
2. 命令执行
当依赖条件中的文件新于目标文件或者目标文件不存在时,命令command部分会被执行.
伪目标则命令总是会执行,由于伪目标在搜索目录中总是找不到与伪目标名相同的文件。
如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令.
exec:
cd /home/hchen
pwd
如上显示的是当前makefile文件存在的目录
exec:
cd /home/hchen;pwd
如上显示的是/home/hchen
3.命令出错
每当命令运行完后,make会检测每个命令的返回码,如果命令返回成功,那么make会执行下一条命令,当规则中所有的命令成功返回后,这个规则就算是成功完成了。只要当一条命令返回码非零时即命令出错,则会中止该规则的命令的继续运行,以及其他规则的继续运行。
为了忽略命令的出错,可以在Makefile的命令行签名加一个减号"-"(在tab键之后)
exec:
@cd /home/panHY;pwd
-@rm *.o
4.嵌套执行make
在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile.
例子:我们有一个子目录叫subdir,这个目录下有个Makefile文件,来指明了这个目录下文件的编译规则。
总控Makefile可以这样书写:
subsystem:
cd subdir && ${MAKE}
#进入当前目录的subdir子目录,然后执行make执行
总控Makefile的变量可以传递到下一级Makefile中,但是不会覆盖下层的Makefile中所定义的变量
当export后面不加任何变量时表示所有的变量都可以传递给下一层.
5. 定义命令包:
如果Makefile中出现一些相同命令序列时,那么我们可以为这些相同的命令序列定义一个变量。
define xxx
yyy
yyyyy
endef
#引用就是${xxx},引用的方式和引用变量的方式是一样的,所以不要和makefile中的变量c
在Makefile中定义的变量,就像是c/c++语言中的宏一样,他代表了一个文本子串
在makefile中执行的时候其会原模原样地展开在所使用的地方.
1.变量使用地方
变量可以使用在“目标”、“依赖目标”、“命令”或是makefile的其他部分中,变量的命名字可以包含字符、数字、下划线(可以是数字开头),但不应该含有“:”、“#”
2.变量基础
变量在声明时需要给予初值,而在使用时,需要给在变量名前加上**“$“符号,但最好用小括号”()“或是大括号”{}“把变量**给包括起来。
3.变量中的变量:
其中:=和=的区别
方法1:就是简单的使用”=“号,在”=“左侧是变量,右侧是变量的值。右侧变量的值可以定义在文件的任何一处,也就是说,右侧中的变量不一定非要是已定义好的值。
foo = ${bar}
bar =${ugh}
ugh = Hub?
方法2:使用”:=“号,则前面的变量不能使用后面的变量,只能使用前面已定义好了的变量。
如何定义一个空格变量?
empty:=
space:=${empty} #end of the line
以上用empty变量表明变量的值开始,而后面采用"#"注释符来表明变量定义的终止。由于在${empty}和"#"之间有一个空格,所以space表示一个空格。
所以
Dir:=/foo/bar #directory 此时Dir表示的是“/foo/bar ”
3.变量高级用法
变量值的替换:${var: a=b }其意思是把变量var中所有以a字串“结尾”的“a”替换成“b”字串。这里结尾的意思是“空格”和“结束符”
foo := a.o b.o c.o
bar := ${foo:.o=.c}
把变量的值再当成变量
x = y
y = z
a := ${${x}} #引用变量x的值为y,则变量y的值为z所以a的值为z
可以有多层
x = y
y = z
z = u
a:=${${${x}}}
4.追加变量值:
使用"+="操作符给变量追加值
objects = main.o foo.o bar.o utils.o
objects += another.o
等价于
objects = main.o foo.o bar.o utils.o
objects := ${objects} another.o
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wJynsbni-1654830456047)((4)]使用变量.assets/image-20220516213402462-16527080440111.png)
5.自动变量
$@: 表示一个规则中的目标文件
$(@D):表示$@的目录
$^:表示一个规则中的所有依赖文件(如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份)
$<:表示一个规则中的第一个依赖文件
$?:表示一个规则中比目标文件要新的依赖文件列表
例子:如果 @ = “ d i r / f o o . o ” , 那 么 @=“dir/foo.o”,那么 @=“dir/foo.o”,那么(@D)=“dir”,$(@F)=“foo.o”
可以根据运行时的不同情况选择不同的执行分支。条件表达式可以是比较变量的值、或是变量或常量的值
实例:
foo:${Objects}
ifeq (${CC},gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
目标"foo"可以根据变量“$(cc)”值来选取不同的函数库来编译程序
我们可以从上面的示例中看到三个关键字:ifeq、else和endif
语法:
<conditional-directive>
<text-if-true>
endif
以及
<conditional-directive>
<text-if-true>
else
<text-if-false>
endif
其中中有四个关键字ifeq、ifneq、ifdef以及ifndef
ifeq ( , )表示两个值是否相等
ifneq( , )表示两个值是否不相等
ifdef :如果变量的值非空,那到表达式为真。否则,表达式为假。
但是一般常用ifeq来判断变量是否为空由于如下例子中虽然foo的值为空但是由于将bar赋值给foo所以输出yes即判断foo不为空
ifndef :与ifdef相反
bar =
foo = $(bar)
ifdef foo
frobozz = yes
else
frobozz = no
endif
(6)使用函数
在Makefile中可以使用函数来处理变量,函数调用后,函数的返回值可以当作变量来使用
1.函数调用语法:
( < f u n c t i o n > < a r g u m e n t s > ) 或 者 (<function> <arguments>) 或者 (<function><arguments>)或者{ }
<function>是函数名,<arguments>是函数的参数,之间用“,”分隔,函数名和参数之间用“空格”分隔。
2.字符串处理函数:
1.
$(subst <from>,<to>,<text>)
名称:字符串替换函数-subst
功能:把字串<text>中的<from>字符串替换成<to>
返回:函数返回被替换后的字符串
$(subst ee,EE,feet on the street)
返回结果:fEEt on the strEEt
2.
$(patsubst <pattern>,<replacement>,<text>)
名称:模式字符串替换函数--patsubst
功能:查找<text>中的单词是否符合模式<pattern>,如果匹配的话,则以<replacement>替换。
单词:一个字符串中以空格、Tab、换行、回车分隔的子串。
<pattern>:中可以使用通配符,<pattern>可以包括通配符“%”,表示任意长度的字串。如果<replacement>中也包含“%”,那么,<replacement>中的这 个“%”将是<pattern>中的那个“%”所代表的字串。(可以用“\”来转义,以“\%”来表示真实含义的“%”字符)
$(patsubst %.c,%.o,x.c.c bar.c)
把“x.c.c bar.c”符合模式[%.c]的单词替换成[%.o],所以返回结果为"x.c.o bar.o"
3.
$(strip <string>)
名称:去空格函数--strip
功能:去掉<string>字符串中开头和结尾的空字符
返回:返回被去掉空格的字符串值
$(strip a b c )返回为"a b c"
4.
$(sort <list>)
名称:排序函数--sort
功能:给字符串<list>中的单词排序(升序)
返回:返回排序后的字符串(会去掉<list>中相同的单词)
$(sort foo bar lose)返回“bar foo lose”
$(wordlist ,,
3. origin函数
origin函数不像其他的函数,他并不操作变量的值。他只是告诉你你的这个变量是哪里来的
$(origin ) 其中是变量的名字,不应该是引用。
4.shell函数
shell函数也不像其他的函数。顾名思义,它的参数应该就是操作系统shell的命令。它和反引号“`”是相同的功能。这就是说,shell函数把执行操作系统命令后的输出作为函数返回。
一般来说,最简单的就是直接再命令行下输入make命令,make命令会找当前目录makefile来执行。
但也有时你也许只想让 make 重编译某些文件,而不是整个工程,而又有的时候你有几套编译规则
1.make的退出码:
2.指定Makefile:
默认情况下GNU make是在当前目录下依次找三个文件–“GNUmakefile”,“makefile”,“Makefile”。
当然我们也可以给makefile文件指定特殊的名字。要达到这个功能,我们要使用make的“-f”或者“–file”参数
3.指定makefile目标:
一般来说,make的最终目标是makefile中的第一个目标(makefile文件中从上到下第一个规则,规则中的从左到右的第一个目标),而其他目标一般是由这个目标连带出来的。
可以根据make命令后直接跟目标的名字,就**可以完成你所指定的目标。**make的环境变量叫"MAKECMDGOALS",这个变量中会存放你所指定的终极目标的列表。
4.参照规则来写makefile中的目标:
"all:"这个伪目标是所有目标的目标,其功能一般是编译所有的目标。
“clean:”这个伪目标功能是删除所有被make创建的文件。
本博客根据《跟我一起学makefile》而做的学习笔记,只限于学习之用
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。