工程中编写自己的makefile---6 makefile学习
来源:互联网 发布:软件协会网站 编辑:程序博客网 时间:2024/05/01 18:52
1 Makefile
1.1 Makefile运行顺序
1、读入所有的 Makefile。
2、读入被 include 的其它 Makefile。
3、初始化文件中的变量。
4、推导隐晦规则,并分析所有规则。
5、为所有的目标文件创建依赖关系链。
6、根据依赖关系,决定哪些目标要重新生成。
7、执行生成命令。
1-5步为第一个阶段,6-7 为第二个阶段。第一个阶段中,如果定义
的变量被使用了,那么,make 会把其展开在使用的位置。但 make
并不会完全马上展开,make 使用的是拖延战术,如果变量出现在依
赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其
内部展开。
1.2 Makefile显式规则
target ... : prerequisites ...
command
...
...
例如
target:目标文件1 目标文件2 目标文件3
<tab> gcc –o 欲建立的执行文件目标文件1 目标文件2 目标文件3
target: 是一个目标文件;可以是 Object File,也可以是执行文件;还可以是一个标签(Label);
prerequisites: 要生成那个target 所需要的文件或是目标
command: 也就是 make 需要执行的命令(任意的Shell 命令)
1.3 引用其他的makefile
使用 include 关键字可以把别的 Makefile 包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。
include 的语法是:
include <filename>
filename 可以是当前操作系统 Shell 的文件模式(可以保含路径和通配符)
注意!!!在 include 前面可以有一些空字符, 但是绝不能是[Tab]键开始。 include和<filename>可以用一个或多个空格隔开
1.4 一些注意的符号
注意!!!
1,- :命令前面加了一个小减号,标记为不管命令出不出错都认为是成功的
2,@ :用“@”字符在命令行前,这个命令将不被 make 显示出来
3,$< :自动化变量,表示所有的依赖目标集
4,$@ :自动化变量,表示目标集
5,$$$$ :意为一个随机编号
6,$(CFLAGS) 编译参数。如果没有定义就是空的。CFLAGS主要是指makefile中的隐式规则里会用到的常见预定义变量,是C编译器的选项
1.5 Make的参数
1,-n:只显示命令,但不执行命令,利于调试Makefile,看看命令执行起来是什么顺序
2,-s:全面禁止命令的显示
3,-f:运行指定的 Makefile,例如:make -f make.linux
4,-i:Makefile 中所有命令都会忽略错误
5,-k:如果某规则中的命令出错了,那么就终目该规则的执行,但继续执行其它规则
1.6 文件搜寻目录
在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当 make 需要去找寻文件的依赖关系时,你可以在文件前加上路径,但最好的方法是把一个路径告诉 make,让 make 在自动去找。
Makefile文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。
VPATH = src:../headers
上面的的定义指定两个目录,“src”和“../headers”,make会按照这个顺序进行搜索。目录由“冒号”分隔。(当然,当前目录永远是最高优先搜索的地方)
另一个设置文件搜索路径的方法是使用make的“vpath”关键字(注意,它是全小写的),这不是变量,这是一个make的关键字,它的使用方法有三种:
vpath <pattern> <directories>
为符合模式<pattern>的文件指定搜索目录<directories>
vpath <pattern>
清除符合模式<pattern>的文件的搜索目录。
vpath
清除所有已被设置好了的文件搜索目录。
vapth使用方法中的<pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”结尾的文件。<pattern>指定了要搜索的文件集,而<directories>则指定了<pattern>的文件集的搜索的目录。例如:
vpath %.h ../headers 该语句表示,要求make在“../headers”目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话)
vpath %.h ../headers:work 该语句表示,要求make在“../headers”和work两个目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话)
我们可以连续地使用 vpath 语句,以指定不同搜索策略。如果连续的vpath 语句中出现了相同的<pattern>,或是被重复了的<pattern>,那么,make 会按照 vpath 语句的先后顺序来执行搜索
1.7 伪目标
“伪目标”并不是一个文件,只是一个标签,由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它是否要执行;为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”
.PHONY:clean
clean :
rm edit $(OBJECTS)
伪目标作为依赖;目的:不同的指令清除不同的目标;可以输入“make cleanall”和
“make cleanobj”和“make cleandiff”命令来达到清除不同种类文件的目的
.PHONY:cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff
注意!!!
可以使用 依赖一个伪目标,这样每次编译时因为ALWAYSMAKE不存在,都会执行依赖后的命令
1.8 静态模式
需要重点看下!!!
静态模式可以更加容易地定义多目标的规则,可以让我们的规则变得更加的有弹性和灵活。
<targets ...>: <target-pattern>:<prereq-patterns ...>
<commands>
..
targets定义了一系列的目标文件,可以有通配符。是目标的一个集合(目标集)。
target-parrtern是指明了targets的模式,也就是的目标集模式(目标模式)。
prereq-parrterns是目标的依赖模式(依赖模式),它对target-parrtern形成的模式再进行一次依赖目标的定义。
上面的模式为生成一个新集合(依赖目标集或者依赖集)
还是举个例子来说明一下吧。如果我们的<target-parrtern>定义成“%.o”,意思是我们的<target>集合中都是以“.o”结尾的,而如果我们的<prereq-parrterns>定义成“%.c”,意思是对<target-parrtern>所形成的目标集进行二次定义,其计算方法是,取<target-parrtern>模式中的“%”(也就是去掉了[.o]这个结尾),并为其加上[.c]这个结尾,形成的新集合(依赖目标集)。
objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
等价于
foo.o:foo.c
$(CC) –c $(CFLAGS) foo.c –o foo.o
bar.o:bar.c
$(CC) –c $(CFLAGS) bar.c –o bar.o
files= foo.elc bar.o lose.o
$(filter %.o,$(files)): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc,$(files)): %.elc: %.el
emacs -f batch-byte-compile $<
$(filter %.o,$(files))表示调用Makefile的filter 函数,过滤“$filter”集,
上面的例子中,指明了我们的目标从$object中获取,“%.o”表明要所有以“.o”结尾的目标,也就是“foo.o bar.o”,也就是变量$object集合的模式,而依赖模式“%.c”则取模式“%.o”的“%”,也就是“foo bar”,并为其加下“.c”的后缀,于是,我们的依赖目标就是“foo.c bar.c”。而命令中的“$<”和“$@”则是自动化变量,“$<”表示所有的依赖目标集(也就是“foo.c bar.c”),“$@”表示目标集(也就是“foo.o bar.o”)。
1.8.1.1 嵌套执行 make
在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile,这有利于让我们的Makefile变得更加地简洁,而不至于把所有的东西全部写在一个Makefile中,这样会很难维护我们的Makefile,这个技术对于我们模块编译和分段编译有着非常大的好处。
我们有一个子目录叫subdir,这个目录下有个Makefile文件,来指明了这个目录下文件的编译规则。那么我们总控的Makefile可以这样书写:
subsystem:
cd subdir && $(MAKE)
其等价于:
subsystem:
$(MAKE) -C subdir
定义$(MAKE)宏变量的意思是,也许我们的make需要一些参数,所以定义成一个变量比较利于维护。这两个例子的意思都是先进入“subdir”目录,然后执行make命令。
如果你要传递变量到下级Makefile中,那么你可以使用这样的声明:
export <variable ...>
否则
unexport <variable ...>
export variable = value
其等价于:
variable = value
export variable
其等价于:
export variable := value
其等价于:
variable := value
export variable
如果你要传递所有的变量,那么,只要一个export就行了。后面什么也不用跟,表示传递所有的变量。
“-w” 输出当前的工作目录;
如果我们的下级make目录是“/home/hchen/gnu/make”,如果我们使用“make -w”来执行,那么当进入该目录时,我们会看到:
make: Entering directory `/home/hchen/gnu/make'.
而在完成下层make后离开目录时,我们会看到:
make: Leaving directory `/home/hchen/gnu/make'
当你使用“-C”参数来指定make下层Makefile时,“-w”会被自动打开的。如果参数中有“-s”(“--slient”)或是“--no-print-directory”,那么,“-w”总是失效的。
1.9 Makefile变量
1.9.1 定义变量
变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有“:”、“#”、“=”或是空字符(空格、回车等)。使用写法(MAKEFLAGS)。
变量使用:
$ MAKEFLAGS
$(MAKEFLAGS)同一使用此种模式,包括在shell中
${ MAKEFLAGS }
如果要使用真实的“$”字符,那么需要用“$$”来表示。
1.9.2 定义命令包
这种命令序列的语法以“define”开始,以“endef”结束
1.9.3 变量中的变量
1,”=”
MyName=$(Name)
Name=wzt
变量是可以使用后面的变量来定义的(递归调用容易出问题)
2,”:=”
Name:=wzt
MyName:=$(Name)
前面的变量不能使用后面的变量,只能使用前面已定义好了的变量
推荐使用此种方法
3,”?=”
如果 FOO 没有被定义过,那么变量 FOO 的值就是“bar”
如果 FOO 先前被定义过,那么这条语将什么也不做
4,”+=”
追加变量值
objects =main.o foo.o bar.o utils.o
objects +=another.o
于是,我们的$(objects)值变成:“main.o foo.o bar.o utils.o another.o”
(another.o被追加进去了)
如果变量之前没有定义过,那么,“+=”会自动变成“=”,如果前面有变量定义,那么“+=”会继承于前次操作的赋值符。如果前一次的是“:=”,那么“+=”会以“:=”作为其赋值符,
1.9.4 多行变量
define 指示符后面跟的是变量的名字,而重起一行定义变量的值,定义是以 endef关键字结束。其工作方式和“=”操作符一样。变量的值可以包含函数、命令、文字,或是其它变量。因为命令需要以[Tab]键开头,所以如果你用 define 定义的命令变量中没有以[Tab]键开头,那么 make就不会把其认为是命令
definetwo-lines
echo foo
echo $(bar)
endef
1.9.5 变量高级用法
1,变量值的替换(类似静态模式)
$(var:a=b)
把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符”
2,把变量的值再当成变量
x = y
y = z
a := $($(x))
有$(a)=z
1.9.6 目标变量
可以为某个目标设置局部变量,这种变量被称为“Target-specific Variable”,它可以和“全局变量”同名,因为它的作用范围只在这条规则以及连带规则中,所以其值也只在作用范围内有效。而不会影响规则链以外的全局变量的值。
其语法是:
<target...> : <variable-assignment>
<target...> : overide <variable-assignment>
<variable-assignment>可以是前面讲过的各种赋值表达式,如“=”、
“:=”、 “+=”或是“?=”。第二个语法是针对于 make 命令行带入的变量,
或是系统环境变量。
这个特性非常的有用,当我们设置了这样一个变量,这个变量会作用
到由这个目标所引发的所有的规则中去。如:
prog: CFLAGS = -g
prog: prog.o foo.o bar.o
$(CC)$(CFLAGS) prog.o foo.o bar.o
prog.o: prog.c
$(CC)$(CFLAGS) prog.c
foo.o: foo.c
$(CC)$(CFLAGS) foo.c
bar.o: bar.c
$(CC)$(CFLAGS) bar.c
在这个示例中,不管全局的$(CFLAGS)的值是什么,在 prog 目标,以及其所引发的所有规则中(prog.o foo.o bar.o 的规则) ,$(CFLAGS) 的值都是“-g”
1.9.7 模式变量
我们知道,make 的“模式”一般是至少含有一个“%”的,所以,我们可以以如下方式给所有以[.o]结尾的目标定义目标变量:
%.o: CFLAGS = -O
同样,模式变量的语法和“目标变量”一样:
<pattern...> : <variable-assignment>
<pattern...> : override <variable-assignment>
override同样是针对于系统环境传入的变量,或是make 命令行指定的变量
1.10 条件判断
<conditional-directive>
<text-if-true>
else
<text-if-false>
endif
ifeq(<arg1>, <arg2> )
ifeq'<arg1>' '<arg2>'
ifeq"<arg1>" "<arg2>"
ifeq"<arg1>" '<arg2>'
ifeq'<arg1>' "<arg2>"
比较参数“arg1”和“arg2”的值是否相同
<conditional-directive>表示条件关键字,如“ifeq”。这个关键字有四个。
1,ifeq “arg1”和“arg2”的值相同为真
2,ifneq “arg1”和“arg2”的值不同为真
3,ifdef
语法:
ifdef <variable-name>
如果变量<variable-name>的值非空,那到表达式为真。否则,表达式为假,
ifdef 只是测试一个变量是否有值,其并不会把变量扩展到当前位置。
4,ifndef
在<conditional-directive>这一行上,多余的空格是被允许的,但是不能以[Tab]键做为开始(不然就被认为是命令) 。而注释符“#”同样也是安全的。“else”和“endif”也一样,只要不是以[Tab]键开始就行了。
特别注意的是,make 是在读取 Makefile 时就计算条件表达式的值,并根据条件表达式的值来选择语句,所以,你最好不要把自动化变量(如“$@”等)放入条件表达式中,因为自动化变量是在运行时才有的。
1.11 常用函数
函数调用,很像变量的使用,也是以“$”来标识的,其语法为:$( )或${ }。
1.11.1 wildcard
原型:$(wildcard PATTERN)
功能:获取匹配模式的文件名
说明:这个函数的功能是查找当前目录下所有符合模式 PATTERN 的文件名,其返回值是以空格分割的,当前目录下的所有符合模式 PATTERN 的文件名列表。
例如:
如下模式返回当前目录下所有扩展名位 .c 的文件列表。
$(wildcard *.c)
1.11.2 patsubst
原型:$(patsubst pattern, replacement, text)
功能:模式替换函数
说明:函数功能是查找字符串 text 中按照空格分开的单词,将符合模式 pattern 的字符串替换成 replacement。 Pattern 中的模式可以使用通配符,当 pattern 和 replacement 中都有 % 时,符合条件的字符将被 replacement 中的替换。函数的返回值是替换后的新字符串。
例如:
需要将 C 文件替换为 .o 的目标文件可以使用如下模式:
$(patsubst%.c, %.o, add.c)
上面的模式将 add.c 字符串作为输入,当扩展名为 .c 时符合模式 %.c ,其中 % 在这里代表 add,替换为 add.o,并作为输出字符串。
$(patsubst%.c, %.o, $(wildcard *.c))
输出的字符串将当前扩展名为 .c 的文件替换成 .o 的文件列表。
1.11.3 foreach
原型:$(foreach VAR, LIST, TEXT)
功能:循环函数
说明: foreach 将 LIST 字符串中一个空格分割的单词,先传给临时变量 VAR ,然后执行 TEXT 表达式,TEXT 表达式处理结束后输出。其返回值是空格分割表达式 TEXT 的计算结果。
例如:
对于存在 add 和 sub 的两个目录,设置 DIRS 为 "add sub ./" 包含目录 add、sub 和当前目录。表达式$(wildcard $(dir)/*.c) ,可以取出目录 add 和 sub 及当前目录中的所有扩展名为 .c 的C语言源文件:
DIRS = subadd ./
FILES =$(foreach dir, $(DIRS), $(wildcard $(dir)/*.c))
1.11.4 filter
原型:$(filter PATTERN…,TEXT)
功能:过滤函数
说明:过滤掉字串“TEXT”中所有不符合模式“PATTERN”的单词,保留所有符合此模式的单词。可以使用多个模式。模式中一般需要包含模式字符“%”。存在多个模式时,模式表达式之间使用空格分割。一般用来去除一个变量中的某些字符串
返回值:空格分割的“TEXT”字串中所有符合模式“PATTERN”的字串。
例如:
sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
cc $(filter %.c %.s,$(sources)) -o foo
使用“$(filter %.c%.s,$(sources))”的返回值给 cc 来编译生成目标“foo”,函数返回
值为“foo.c bar.c baz.s”
1.11.5 subst
原型:$(subst FROM, TO, TEXT),
功能:替换函数
说明:即将字符串TEXT中的子串FROM变为TO
- 工程中编写自己的makefile---6 makefile学习
- 工程中编写自己的makefile---7 shell学习
- 工程中编写自己的makefile---8 编写Arm开发板的makefile
- 工程中编写自己的Makefile---1 一些基本概念
- 工程中编写自己的makefile---2 实例
- 工程中编写自己的makefile---3 交叉编译工具
- 工程中编写自己的makefile---4 库文件
- 编写自己的Makefile
- 工程中编写自己的makefile---5 在实例2中使用库文件
- 简单工程Makefile的编写
- 简单工程Makefile的编写
- 简单工程Makefile的编写
- 简单工程Makefile的编写
- 编写自己的makefile文件
- Eclipse中使用自己的makefile管理工程
- Eclipse中使用自己的makefile管理工程
- Eclipse中使用自己的makefile管理工程
- Eclipse中使用自己的makefile管理工程
- 矩阵快速幂 C++实现
- 学习kotlin第十天_对象、委托、委托属性
- 算菜价
- try..finally中使用return
- 分布式锁2 Java非常用技术方案探讨之ZooKeeper
- 工程中编写自己的makefile---6 makefile学习
- ocr识别
- nodejs的调试器node-inspector
- Android_Kotlin简介、认识及Studio初步配置
- C++----string
- let和const命令
- TensorFlow22: 手写汉字识别
- python-sklearn学习笔记(1)svm
- 工程中编写自己的makefile---7 shell学习