Makefile学习.docx
- 文档编号:23003629
- 上传时间:2023-04-30
- 格式:DOCX
- 页数:12
- 大小:22.88KB
Makefile学习.docx
《Makefile学习.docx》由会员分享,可在线阅读,更多相关《Makefile学习.docx(12页珍藏版)》请在冰豆网上搜索。
Makefile学习
Makefile
1Makefile基础
Makfile的原则
1).如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
2).如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。
3).如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。
target...:
prerequisites...
command
...
...
target也就是一个目标文件,可以是ObjectFile,也可以是执行文件。
还可以是一个标签(Label),对于标签这种特性,在后续的“伪目标”章节中会有叙述。
prerequisites就是,要生成那个target所需要的文件或是目标。
command也就是make需要执行的命令。
(任意的Shell命令)
这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。
说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。
这就是Makefile的规则。
也就是Makefile中最核心的内容。
在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个Tab键作为开头。
记住,make并不管命令是怎么工作的,他只管执行所定义的命令。
make会比较targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的话,那么,make就会执行后续定义的命令。
make自动推导
GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在每一个[.o]文件后都写上类似的命令,因为,我们的make会自动识别,并自己推导命令。
只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果make找到一个whatever.o,那么whatever.c,就会是whatever.o的依赖文件。
并且cc-cwhatever.c也会被推导出来
清空目标文件的规则
每个Makefile中都应该写一个清空目标文件(.o和执行文件)的规则,这不仅便于重编译,也很利于保持文件的清洁。
一般的风格都是:
clean:
rmobj$(objects)
更为稳健的做法是:
.PHONY:
clean
clean:
-rmobj$(objects)
.PHONY意思表示clean是一个“伪目标”。
而在rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。
当然,clean的规则不要放在文件的开头,不然,这就会变成make的默认目标。
不成文的规矩是——“clean从来都是放在文件的最后”。
2Makefile 总述
Makefile里主要包含了五个东西:
显式规则、隐晦规则、变量定义、文件指示和注释。
1.显式规则。
显式规则说明了,如何生成一个或多的的目标文件。
这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
2.隐晦规则。
由于make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。
3.变量的定义。
在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点像C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
4.文件指示。
其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。
5. 注释。
Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。
如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:
“\#”。
在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。
include的语法是:
自动生成依赖性
在Makefile中,我们的依赖关系可能会需要包含一系列的头文件,比如,如果我们的main.c中有一句“#include"defs.h"”,那么我们的依赖关系应该是:
main.o:
main.cdefs.h
但是,如果是一个比较大型的工程,你必需清楚哪些C文件包含了哪些头文件,并且,你在加入或删除头文件时,也需要小心地修改Makefile,这是一个很没有维护性的工作。
为了避免这种繁重而又容易出错的事情,我们可以使用C/C++编译的一个功能。
大多数的C/C++编译器都支持一个“-M”的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。
例如,如果我们执行下面的命令:
cc-Mmain.c
其输出是:
main.o:
main.cdefs.h
条件关键字
1“ifdef”。
语法是:
ifdef
如果变量
否则,表达式为假。
当然,
注意,ifdef只是测试一个变量是否有值,其并不会把变量扩展到当前位置。
还是来看两个例子:
示例一:
bar=
foo=$(bar)
ifdeffoo#foo在这里没有进行扩展,所以此时值为true
endif
示例二:
foo=
ifdeffoo#false
endif为了避免混乱,make不允许把整个条件语句分成两部分放在不同的文件中。
2“ifeq”,语法是
ifeq(arg1,arg2)/ifeq“arg1””arg2”/if‘arg1’‘arg2’
else/也可以没有else
endif
关于变量
1多行变量
还有一种设置变量值的方法是使用define关键字。
使用define关键字设置变量的值可以有换行,这有利于定义一系列的命令(前面我们讲过“命令包”的技术就是利用这个关键字)。
define指示符后面跟的是变量的名字,而重起一行定义变量的值,定义是以endef关键字结束。
其工作方式和“=”操作符一样。
变量的值可以包含函数、命令、文字,或是其它变量。
因为命令需要以[Tab]键开头,所以如果你用define定义的命令变量中没有以[Tab]键开头,那么make就不会把其认为是命令。
下面的这个示例展示了define的用法:
definetwo-lines
echofoo
echo$(bar)
endef
2变量的高级用法
我们可以替换变量中的共有的部分,其格式是“$(var:
a=b)”或是“${var:
a=b}”,其意思是,把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。
这里的“结尾”意思是“空格”或是“结束符”。
如:
foo:
=a.ob.oc.o
bar:
=$(foo:
.o=.c)
这个示例中,我们先定义了一个“$(foo)”变量,而第二行的意思是把“$(foo)”中所有以“.o”字串“结尾”全部替换成“.c”,所以我们的“$(bar)”的值就是“a.cb.cc.c”
3当make嵌套调用时(参见前面的“嵌套调用”章节),上层Makefile中定义的变量会以系统环境变量的方式传递到下层的Makefile中。
当然,默认情况下,只有通过命令行设置的变量会被传递。
而定义在文件中的变量,如果要向下层Makefile传递,则需要使用exprot关键字来声明。
Makefile中include的用法
include类似于c语言中的#include,是将指定的makefile文件包含进来,格式如下:
includefilename–》在当前路径下找要包含的mk文件。
或者(includefilepath/filename指定路径),或者命令行选项“-I”或者“--include-dir”如果找到指定的文件,则使用这个文件;否则依此搜索以下几个目录(如果存在) “/usr/gnu/include”“/usr/local/include”和“/usr/include”。
当在这些目录下都没有找到“include”指定的文件时,make将会提示一个包含文件未找到的告警提示, 但是不会立刻退出此时,如果在指定的路径下没有要包含的mk文件。
如果在指定或者当前目录下没有找到指定的mk文件,当前总控makefile会继续处理其他的内容,当读取整个makefile后,再次试图使用规则来创建通过指示符“include”指定的但未找到的文件,当不能创建它时(没有创建这个文件的规则),make将提示致命错误并退出。
Makefile中可使用“-include”来代替“include”,来忽略由于包含文件不存在或者无法创建时的错误提示。
在进行make时不会出现错误,更不会停止。
include的意义:
(自己学习理解)先看如下code:
当前目录下包含a、b两个目录,a、b中分别包含a.cb.c两个文件,
1test:
main.oa.ob.o
2cc-otestmain.oa.ob.o
3-include/home/jaychen/test/make/b/Makefile(或直接为b/Makefile)
4-include/home/jaychen/test/make/a/Makefile
clean:
rm*.otest–f
当我在写这个的时候,先看了网上的资料,解释说3和4两行需要放在1行前面,然后我试了,发现只会运行b的mk,后面的停止了。
然后我又将code调为现在的样子,发现就好了。
于是,我就反思,makefile中第一行中如果有类似目标:
依赖格式的将作为这个mk的最终目标,当3、4两行在前面,也就是3行在第一行,那么就作为最终目标,然后就运行cc-cb.c(bmk中的内容),因为没有其他的依赖性,就不会进行make自动推导,所以就会停止,当code如上时,test会依赖a.o、b.o,就会进行自动推导,就会在include包含进来的mk中查找。
。
。
然后就行了。
a的makefile为:
a.o:
a/a.c
cc-ca/a.c
其中需要注意的是,这里在引用a.c或b.c时需要指明路径,因为a、b两个的mk是被包含在了当前目录中的mk中,相当于把两个mk的内容写到了当前目录的mk中,并在这个mk中执行。
嵌套执行makefile
假定一个目录为other_path下面有makefile文件,
other_path=*/*/*(目录的最后一个*为makefile的所在目录)
可以有两种方式
1$(MAKE)–Cother_path
2cdother_path&&$(MAKE) $(MAKE)就是make
如果想要在make时显示信息到屏幕中,可以这样:
all:
dis_begintestdis_end(我经过实践,makefile中,各个伪目标执行的顺序是从左到右)
.PHONY:
all
dis_begin:
@echo-------beginbulid---------
dis_end:
@echo--------endbulid----------
如果要嵌套执行makeclean或其他的命令,格式如下
1$(MAKE)clean–Cother_path(或者将clean置于句末)
2cdother_path&&$(MAKE)clean
我们把这个Makefile叫做“总控Makefile”,总控Makefile的变量可以传递到下级的Makefile中(如果你显示的声明),但是不会覆盖下层的Makefile中所定义的变量,除非指定了“-e”参数。
如果你要传递变量到下级Makefile中,那么你可以使用这样的声明:
export
如果你不想让某些变量传递到下级Makefile中,那么你可以这样声明:
unexport
如:
exportvariable=value
其等价于:
variable=value
exportvariable
Makefile动态库和静态库
1动态库
Linux下的动态库以.so为后缀,在Linux下使用动态库。
动态库在程序运行时被链接。
第一步:
编写Linux程序库
动态库实现文件test.c
第二步:
编译生成动态库
gcc test.c -shared -o–fPIC -o libtest.so
由以上命令生成动态库libtest.so,为了不需要动态加载动态库,在命令时需以lib开头以.so为后缀。
–fPIC:
表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
–shared:
指明编译成动态库。
第三步:
使用动态库
文件main.c.动态库使用文件test.c。
使用动态库libtest.so,
编译命令:
gcc test.c –L . –ltest –o test
–L:
指明动态库所在的目录
-l:
指明动态库的名称,该名称是处在头lib和后缀.so中的名称,如上动态库libtest.so的l参数为-l test。
测试:
ldd test
ldd 测试可执行文件所使用的动态库
编译时指定动态库加载路径命令:
gcc test.c -L . -ltest -o test -Wl,-rpath=./
gcc编译链接动态库时,很有可能编译通过,但是执行时,找不到动态链接库,那是因为-L选项指定的路径只在编译时有效,编译出来的可执行文件不知道-L选项后面的值,当然找不到。
可以用ldd
2静态库
链接静态库其实从某种意义上来说也是一种粘贴复制,只不过它操作的对象是目标代码而不是源码。
创建静态库:
第一步:
生成目标文件并归档。
gcc-cpathname/test.c(在makefile中这一步可由make自动推导)
arrlibtest.apathname/test.o(pathname/test2.o)
libtest.a就是生成的静态库。
第二步:
链接静态库
gccmain.c-L.-ltest-static-omain
比链接动态库时多了一个static。
函数以及调用
1函数的调用语法
函数调用,很像变量的使用,也是以“$”来标识的,其语法如下:
$(
或是
${
2字符串处理函数
$(subst
名称:
字符串替换函数——subst。
功能:
用
返回:
函数返回被替换过后的字符串。
$(patsubst
名称:
模式字符串替换函数——patsubst。
功能:
查找
这里,
如果
(可以用“\”来转义,以“\%”来表示真实含义的“%”字符)返回:
函数返回被替换过后的字符串。
$(strip
名称:
去空格函数——strip。
功能:
去掉
返回:
返回被去掉空格的字符串值。
$(findstring
名称:
查找字符串函数——findstring。
功能:
在字串
返回:
如果找到,那么返回
$(filter
名称:
过滤函数——filter。
功能:
以
可以有多个模式。
返回:
返回符合模式
$(wordlist,
名称:
取单词串函数——wordlist。
功能:
从字符串开始到
和
返回:
返回字符串到
如果比
如果开始,到
$(foreach,,
这个函数的意思是,把参数中的单词逐一取出放到参数所指定的变量中,然后再执行
每一次
$(if
或是
$(if
call函数
call函数是唯一一个可以用来创建新的参数化的函数。
你可以写一个非常复杂的表达式,这个表达式中,你可以定义许多参数,然后你可以用call函数来向这个表达式传递参数。
其语法是
$(call
当make执行这个函数时,
(1),$
(2),$(3)等,会被参数
而
例如:
reverse=$
(1)$
(2)
foo=$(callreverse,a,b)
那么,foo的值就是“ab”。
当然,参数的次序是可以自定义的,不一定是顺序的,如:
reverse=$
(2)$
(1)
foo=$(callreverse,a,b)
此时的foo的值就是“ba”。
origin函数
不像其它的函数,他并不操作变量的值,他只是告诉你你的这个变量是哪里来的?
其语法是:
$(origin
shell函数
它的参数应该就是操作系统Shell的命令。
shell函数把执行操作系统命令后的输出作为函数。
contents:
=$(shellcatfoo)
files:
=$(shellecho*.c)
PWD:
=$(shellpwd)
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Makefile 学习