Makefile使用教程及实践总结.docx
- 文档编号:9260795
- 上传时间:2023-02-03
- 格式:DOCX
- 页数:35
- 大小:112.99KB
Makefile使用教程及实践总结.docx
《Makefile使用教程及实践总结.docx》由会员分享,可在线阅读,更多相关《Makefile使用教程及实践总结.docx(35页珍藏版)》请在冰豆网上搜索。
Makefile使用教程及实践总结
Makefile简明使用教程及实践总结
杨小玉2011-02-16
目录
一、自动化编译器配置文件简介2
1.1什么是make命令2
1.2为什么要使用make命令2
1.3Makefile文件的结构与作用3
1.4Makefile应用举例:
4
二、MakeFile规则和几种目标6
2.1Makefile规则6
2.1.1显式规则6
2.1.2隐含规则6
2.1.3后缀规则(在高版本make命令中已由模式规则取代)6
2.1.4模式规则7
2.1.5清空目标文件的规则7
2.2Makefile文件的几种目标7
2.2.1指定目标——根据依赖生成指定目标文件。
7
2.2.2伪目标7
2.2.3多目标8
2.2.4标准目标8
2.2.5特殊目标9
三、MakeFile的变量9
3.1变量定义两种方式:
9
3.1.1递归展开变量:
9
3.1.2直接展开变量:
10
3.2MakeFile的变量高级用法10
3.2.1变量值的替换:
10
3.2.2变量嵌套10
3.2.3变量取值11
3.2.4系统环境变量11
3.2.5自动化变量12
3.2.6预定义变量12
四、MakeFile的执行13
4.1目录搜寻13
4.2make的嵌套执行14
4.3Makefile包含14
4.4条件执行14
4.5Makefile的命令行选项14
五、make内嵌函数15
5.1文本处理函数15
5.2文件名处理函数16
5.3foreach函数16
5.4if函数17
5.5origin函数17
5.6shell函数17
六、encode下Makefile执行过程分析17
6.1make执行过程20
6.2makeinstall执行过程21
一、自动化编译器配置文件简介
1.1什么是make命令
Make命令是对Makefile文件进行解释执行的命令。
他可以根据Mkefile文件完成对最终目标文件的生成和管理。
1.2为什么要使用make命令
使用编译语言创建一个二进制文件通常需要调用一系列的步骤来将所有的源码文件编译成为目标代码,然后调用链接器将这些目标代码模块链接成为一个要执行文件。
所有必须的步骤可以手动来完成,但是这很快就会成一件非常乏味的工作。
另外一个解决的办法就是可以写一个shell脚本每次来执行这些命令,这是一个好一些的解决办法。
例如,某shell文件内容:
gcc-oappexpsrc/main.csrc/app.csrc/bar.csrc/lib.c
这个命令会运行gcc程序,并且会调用预处理器、编译器、链接器来将这四源码文件转换成为一个可执行文件。
单一的命令对于类似于这样的简单工程来说还是可以接受的,但是对于大一些的工程来说就显得有一些不现实了。
下面的命令集合将编译过程显示步进的方式,从而我们可以看到更为详细和复杂的构建过程:
gcc-c-omain.osrc/main.c
gcc-c-oapp.osrc/app.c
gcc-c-obar.osrc/bar.c
gcc-c-olib.osrc/lib.c
gcc-oappexpmain.oapp.obar.olib.o
前面的四个命令将src目录中的四个c文件转换成为目标文件。
最后一条命令调用链接器将生成的四个目标文件组合成为一个可执行文件。
很显然,如果将上面的编译流程书写成shell脚本程序,则开发人员可以使用简单的shell命令来构建这个程序。
同时他也可以修正控制并与源代码一起进行分发,从而简化了构建一个程序的过程。
这个构建脚本的一个缺点就是每次调用时都会重新编译整个工程。
对于我们这里的简单的例子工程来说,这并不会在开发周期中着引起严重的时间增长,但是随着文件数量的增多,重新运行整个构建过程将会是一个严重的负担。
比较shell脚本解决方法,make命令一个强大的地方就是他具有理解一个工程依赖关系的能力。
对于依赖关系的理解允许make命令只重新构建整个工程中由于源文件的修改而需要更新的部分。
make实用程序使用开发者创建的输入文件来描述要构建工程。
GNUmake使用Makefile作为他的输入文件默认文件名。
所以,当输入make命令时,他会在当前目录下查找名为Makefile的文件,根据Makefile文件的安排构建工程。
下面展示了用来构建这个例子工程的基本的Makefile文件:
appexp:
main.oapp.obar.olib.o
gcc-oappexpmain.oapp.obar.olib.o
main.o:
src/main.csrc/lib.hsrc/app.h
gcc-c-omain.osrc/main.c
app.o:
src/bar.csrc/lib.h
gcc-c-obar.osrc/bar.c
lib.o:
src/lib.csrc/lib.h
gcc-c-olib.osrc/lib.c
1.3Makefile文件的结构与作用
Makefile关系到了整个工程的编译规则。
一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,Makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为Makefile就像一个shell脚本一样,其中也可以执行操作系统的命令。
Makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
make是一个命令工具,是一个解释Makefile中命令的工具。
make命令执行时,需要一个Makefile文件,以告诉make命令需要怎么样的去编译和链接程序。
Makefile的宗旨就是:
让编译器知道要编译一个文件需要依赖其他的哪些文件。
当那些依赖文件有了改变,编译器会自动的发现最终的生成文件已经过时,而重新编译相应的模块。
所有的Makefile文件结构都遵循下面的格式:
最终目标:
依赖(目标1目标2……)#规则0
命令
目标1:
依赖(目标11目标12……)#规则1
命令1
目标2:
依赖(目标21目标22……)#规则2
命令2
……
目标11:
依赖(……)
命令11
目标12:
依赖(……)
命令12
……
注意:
命令之前必须应有一个Tab。
Makefile文件描述了整个工程的编译、链接规则,其中包括:
工程中的哪些源文件需要编译以及如何编译,需要创建哪些库文件以及如何创建这些库文件,如何产生期望得到的最终可执行文件。
Makefile文件描述了工程中所有文件的编译顺序、编译规则,make根据这些规则来编译工程。
一个利用自动变量和默认变量构成Makefile文件的例子。
OBJS=control.oui.omain.o
CC=gcc
CFLAGS=-Wall
all:
program
program:
$(OBJS)
$(CC)$(OBJS)-o$@
control.o:
control.c
$(CC)$(CFLAGS)-c-o$@$^
ui.o:
ui.c
$(CC)$(CFLAGS)-c-o$@$^
main.o:
main.c
$(CC)$(CFLAGS)-c-o$@$^
clean:
$(RM)program$(OBJS)
#rm-fprogram*.o
1.4Makefile应用举例:
/*main.c*/
#include
#include"foo.h"
#include"bar.h"
intmain()
{
printf("hello\n");
foo();
bar();
return0;
}
/*foo.c*/
#include
voidfoo()
{
printf("youareinfoo\n");
}
/*foo.h*/
#ifndef__FOO_H__
#define__FOO_H__
voidfoo();
#endif
/*bar.c*/
#include
voidbar()
{
printf("youareinbar\n");
}
/*bar.h*/
#ifndef__BAR_H__
#define__BAR_H__
voidbar();
#endif
方式1:
命令行模式直接使用gcc命令编译程序,格式如下:
#gccmain.cfoo.cbar.c–ohello
方式2:
使用GNUmake工具编译程序,需要编写Makefile,内容如下:
hello:
main.ofoo.obar.o
gccmain.ofoo.obar.o-ohello
main.o:
main.cfoo.hbar.h
gcc-cmain.c-omain.o
foo.o:
foo.c
gcc-cfoo.c-ofoo.o
bar.o:
bar.c
gcc-cbar.c-obar.o
.PHONY:
clean
clean:
-rm-f*.o*.o~
在命令行运行make命令,同样可以编译出可执行程序hello。
如下图所示:
同时运用上Makefile的变量和规则,写出一个更通用便于修改的Makefile如下:
CC=gcc
CFLAGS=-DDEBUG-Wall-g-o
SRC=main.cfoo.cbar.c
OBJS:
=$(SRC:
.c=.o)
TARGET=hello
all:
$(OBJS)
$(CC)$(CFLAGS)$(TARGET)$(OBJS)
%.o:
%.c
$(CC)$(CFLAGS)$@-c$<
.PHONY:
clean
clean:
-rm-f$(OBJS)$(TARGET)
执行结果:
或
TARGET=hello
CC=gcc-c-g
LINK=gcc
C_SOURCE=$(wildcard*.c)
C_OBJS=$(C_SOURCE:
%.c=%.o)
$(TARGET):
$(C_OBJS)
@echoLinking$@from$^...
$(LINK)-o$@$^
@echo
$(C_OBJS):
%.o:
%.c
@echoCompling$@from$<...
$(CC)-o$@$<
@echo
.PHONY:
clean
clean:
-rm-rf$(C_OBJS)$(TARGET)
执行结果如图
GNUmake的执行过程分为:
在Linux命令行提示符输入make,它会在当前目录下按顺序寻找GNUmakefile、Makefile和makefile,如未找到则报错,找到则把Makefile文件中的第一个目标作为最终目标。
按照“堆栈”顺序,依次找到每一个目标文件,判断新旧关系,必要时生成新的目标文件,直到生成最终目标。
二、MakeFile规则和几种目标
2.1Makefile规则
2.1.1显式规则
foo:
foo.obar.o
cc–ofoofoo.obar.o$(CFLAGS)$(LDFLAGS)
foo.o:
foo.c
cc–cfoo.c$(CFLAGS)
bar.o:
bar.c
cc–cbar.c$(CFLAGS)
2.1.2隐含规则
foo:
foo.obar.o
cc–ofoofoo.obar.o$(CFLAGS)$(LDFLAGS)
所谓“隐含规则”也就是一种惯例,make会按照这种“惯例”心照不喧地来运行,那怕我们的Makefile中没有书写这样的规则。
例如,把[.c]文件编译成[.o]文件这一规则,你根本就不用写出来,make会自动推导出这种规则,并生成我们需要的[.o]文件。
2.1.3后缀规则(在高版本make命令中已由模式规则取代)
.SUFFIXES:
.c.o.cpp.cc.cxx.C#特殊目标“SUFFIXES”的所有依赖指出了一系列在后缀规则中需要检查的后缀名(就是当前make需要处理的后缀)。
并说明隐含规则
.cpp.o:
#双后缀规则,它的含义是目标为“.o”文件、依赖为“.cpp”文件。
$(CXX)-c$(CXXFLAGS)$(INCPATH)-o$@$<
.cc.o:
$(CXX)-c$(CXXFLAGS)$(INCPATH)-o$@$<
.cxx.o:
$(CXX)-c$(CXXFLAGS)$(INCPATH)-o$@$<
.C.o:
$(CXX)-c$(CXXFLAGS)$(INCPATH)-o$@$<
.c.o:
$(CC)-c$(CFLAGS)$(INCPATH)-o$@$<
2.1.4模式规则
%.o:
%.c
$(CC)-c$(CFLAGS)$(CPPFLAGS)$<-o$@
在模式规则中,目标名中需要包含有一个模式字符“%”,包含有模式字符的目标被用来匹配一个文件名。
一个模式规则的格式为:
%.o:
%.c:
指定了如何由文件“N.c”来创建文件“N.o”,文件“N.c”必须是已经存在或者可被创建的;
2.1.5清空目标文件的规则
每个makefile文件都应有一个“清空目标文件”的规则:
☐.PHONY表示clean是一个伪目标
☐减号“-”的作用是一旦出现问题,可以继续执行后面的操作
☐clean不能放在文件头,习惯上放在文件尾
2.2Makefile文件的几种目标
2.2.1指定目标——根据依赖生成指定目标文件。
edit:
main.okbd.ocommand.odisplay.oinsert.osearch.ofiles.outils.o
2.2.2伪目标
它不代表一个真正的文件名,在执行make时可以指定这个目标来执行其所在规则定义的命令,有时我们也可以将一个伪目标称为标签。
特点:
没有规则。
“伪目标”的取名不能和文件名重名。
clean:
rm-f*.o
.PHONY:
clean
clean:
rm-f*.o
all:
prog1prog2prog3#对伪目标也可以给定依赖
.PHONY:
all
prog1:
prog1.outils.o
cc-oprog1prog1.outils.o
prog2:
prog2.o
cc-oprog2prog2.o
prog3:
prog3.osort.outils.o
cc-oprog3prog3.osort.outils.o
2.2.3多目标
Makefile的规则中的目标可以不止一个,其支持多目标,有可能我们的多个目标同时依赖于一个文件,并且其生成的命令大体类似。
于是我们就能把其合并起来。
bigoutputlittleoutput:
text.g
generatetext.g-$(substoutput,,$@)>$@#剔除output字符串
#“$@”表示目标的集合,就像一个数组,“$@”依次取出目标,并执于命令。
上述规则等价于:
bigoutput:
text.g
generatetext.g-big>bigoutput
littleoutput:
text.g
generatetext.g-little>littleoutput
下面的例子中使用的是模式规则:
%.tab.c%.tab.h:
%.y
bison-d$<
这条规则告诉make把所有的[.y]文件都以"bison-d
(其中,
规则存在多个目标,并且不同的目标可以根据目标文件的名字来自动构造出依赖文件。
目标:
目标模式:
依赖
命令
objects=foo.obar.o
all:
$(objects)
$(objects):
%.o:
%.c
$(CC)-c$(CFLAGS)$<-o$@
等价于:
foo.o:
foo.c
$(CC)-c$(CFLAGS)foo.c-ofoo.o
bar.o:
bar.c
$(CC)-c$(CFLAGS)bar.c-obar.o
2.2.4标准目标
Makefile标准目标是一些约定俗成的目标,它们代表一个确定的含义,包含:
⏹all:
编译整个软件包,但不重建任何文档。
一般此目标作为默认的终极目标。
此目标一般对所有源程序的编译和连接使用"-g"选项,以使最终的可执行程序中包含调试信息。
⏹clean:
删除所有被make创建的文件;
⏹install:
完成程序的编译并将最终的可执行程序、库文件等拷贝到指定的目录。
此种安装一般不对可执行程序进行strip操作。
⏹print:
列出改变过的源文件;
⏹tar:
打包备份源程序;
⏹dist:
创建一个压缩文件;
⏹TAGS:
更新所有的目标;
2.2.5特殊目标
GNUmake所支持的特殊目标有:
⏹.PHONY:
它的所有依赖被视为伪目标。
伪目标所在规则定义的命令会被无条件执行。
⏹.PRECIOUS:
它的所有依赖文件在make过程中会被特殊处理。
⏹.DELETE_ON_ERROR:
规则的命令执行错误,它将删除已经被修改的目标文件。
⏹.SILENT:
指定命令行没有意义,这是旧版中的用法。
当前用法是使用make命令行参数“-s”或者“--silent”……
三、MakeFile的变量
Makefile中可以使用变量
变量类似于C语言的宏,但值可修改
变量名大小写敏感
变量名不应该包含:
#=或空格
变量使用时用$(var)形式
变量引用的展开过程是严格的文本替换过程,就是说变量值的字符串被精确的展开在此变量被引用的地方。
例如,有如下规则
foo =c
prog.o :
prog.$(foo)
g$(foo)$(foo)-$(foo)prog.$(foo)
被展开后就应是:
prog.o :
prog.c#prog.o :
prog.$(foo)
gcc-cprog.c#g$(foo)$(foo)-$(foo)prog.$(foo)
3.1变量定义两种方式:
3.1.1递归展开变量:
使用“=”表示,可先使用,后定义。
弊病:
有可能递归定义,导致无限展开。
假设变量TOPDIR和SUBDIR的定义如下:
TOPDIR=/home/xiaowp
SUBDIR=$(TOPDIR)/project
此时变量SUBDIR的值在解析时会被正确地展开为/home/xiaowp/project,但对于下面的定义:
TOPDIR=/home/xiaowp
SUBDIR=$(TOPDIR)/project
SUBDIR=$(SUBDIR)/src//修改变量
……
……$(SUBDIR)……//引用展开时会陷入死循环
很清楚,希望得到的结果是SUBDIR=/home/xiaowp/project/src,但实际并非如此。
SUBDIR在引用时会被递归展开,从而陷入一个无限循环当中,make能够检测到这个问题并报告如下错误:
***Recursivevariable'SUBDIR'referencesitself(eventually).Stop
3.1.2直接展开变量:
使用“:
=”表示,须先定义再使用。
例如:
x :
=$(foo)
y :
= $(x)$(bar)
x :
=$(later)
等价于:
y :
=$(foo)$(bar)//x :
=$(foo),即:
变量x与$(foo)字符串等价
x :
=$(later)
3.2MakeFile的变量高级用法
3.2.1变量值的替换:
☐可以替换变量的共有部分,格式为“$(var:
a=b)”,可以把变量var中所有以a字串结尾的a替换为b字串
x:
=a.ob.oc.o
y:
=$(x:
.o=.c)
all:
@echo$(y)
x:
=a.ob.oc.o
y:
=$(x:
%.o=%.c)
all:
@echo$(y)
☐静态模式替换,模式中必须有一个“%”
3.2.2变量嵌套
x=v1
v2:
=hello
y=$(subst1,2,$(x))
z=y
a:
=$($($(z)))
all:
@echo$(a)
函数subst:
把x中的所有1字串替换成2字串
3.2.3变量取值
变量的定义值在长度上没有限制;
当引用一个没有定义的变量时,make默认它的值为空;
一些特殊的变量在make中内嵌有固定的值,不过这些变量允许在Makefile中显式的重新赋值。
自动化变量不能在Makefile中显式修改;
如果希望实现这样一个操作,仅对一个之前没有定义过的变量赋值,可以使用“?
=”代替“=”或者“:
=”来实现。
可以使用+=给变量追加值
a=x.oy.o
a+=z.o
all:
@echo$(a)
●“?
=”操作符的使用——条件赋值(定义)
只有定义变量在当前操作之前没有赋值的情况下才会对这个变量进行赋值。
例如:
FOO ?
=bar
其等价于:
ifeq ($(origin$(FOO)),$(undefined))
FOO:
=bar
endif
其含义是:
如果变量“FOO”在没有定义过,就给它赋值“bar”。
否则不改变它的值。
三、变量值的追加
通常对于一个通用变量在定义之后某个地方,需要给它的值进行追加。
在Makefile中使用“+=”(追加方式)来实现对一个变量值的追加操作。
如:
objects +=another.o
这个操作把字符串“another.o”添加到变量“objects”原有值(字符串)的末尾,并使用空格将其分开。
因此我们可以看到:
objects :
=main.ofoo.obar.outils.o
objects +=another.o
上边的两个操作之后变量“objects”的值成为:
main.ofoo.oba
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Makefile 使用 教程 实践 总结