东软集团(大连)有限公司 SVVD事业部-于忠华 Makefile简析 东软集团(大连)有限公司 SVVD事业部-于忠华 Copyright 2010 By Neusoft Group. All rights reserved
目录 1. Makefile介绍 2. Makefile书写规则 3. Makefile中的语法 4. 如何让依赖关系的自动化 内容概要 1. Makefile介绍 2. Makefile书写规则 3. Makefile中的语法 4. 如何让依赖关系的自动化 5. Makefile的运行 6. 实践应用举例
Makefile 介绍 概要 一个完备的大型工程中,源文件不计其数,按类型、功能、模块分成了很多目录。需要一系列规则来实现自动化的编译、链接。 Makefile作为make命令的输入文件,按照一定的规则来完成源文件的编译、链接。只要Makefile写的好,通过一个make命令就能完成整个工程的编译 一个好的Makefile,在增、删、改文件的时候,不需要修改Makefile文件的情况下,能自动识别并完成变更后文件的编译,最大限度的节约编译时间。 目标: 1. 通过本次学习能读懂别人写的Makefile 2. 通过本次学习能完成一个普通工程的makefile
Makefile书写规则 规则—依赖关系 Makefile其实就一个核心规则: Def1.h Def2.h Def3.h Main.c F1.c F2.c Main.o F1.o F2.o Main Makefile其实就一个核心规则: target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label)。 prerequisites就是,要生成那个target所需要的文件或是目标。 command也就是make需要执行的命令。(任意的Shell命令) 就是说target这个目标文件依赖于prerequisites,他的生成规则定义在command中 Prerequisites中只要有一个以上比target新,command都会重新执行。 Target…:prerequisites… command …
Makefile书写规则 举例 举个例子说明makefile的书写规则和执行过程: Main.o :Main.c Typedef.h CC –o Main.o Main.c Draw.o : Draw.c CC –o Draw.o Draw.c Draw : Main.o Draw.o CC –o Draw Main.o Draw.o all: Draw clean : rm Draw Main.o Draw.o 这个makefile可以通过一句make命令生成Draw的可执行文件,也可以通过make clean清除所以make出来的文件 Makefile默认从all开始解释执行,all依赖于Draw,所以先执行Draw,Draw又依赖于Draw.o和Main.o,所以最先执行Draw.o和Main.o。
Makefile中的语法 Makefile中的基本元素 1. 目标文件,标签(Lable) 2. 源文件 3. 变量 在Makefile中的定义的变量,就像是C/C++语言中的宏一样,他代表了一个文本字串。变量可以使用在“目标”,“依赖目标”,“命令”或是Makefile的其它部分中。 如:Object = Main.o Draw.o 使用变量是要在变量前加$,最后用()或{}括起来,比如$(Object) Makefile中还有些奇怪的变量:$<(源文件), $@(目标文件),
Makefile中的语法 Makefile中的语法 1. 赋值语句 赋值表达式有”=”,”:=”,”+=”,”?=” [:=] 只使用定义好的变量,能避免递归 [+=] a+=b => a=a+b [?=] 如果变量没定义则赋值,否则不执行 A = $(B) B = $(A) # 存在递归问题 # 解决对策: A := $(B) B := $(A)
Makefile中的语法 Makefile中的语法 2. 条件判断 条件表达式有:ifeq,ifneq,ifdef,ifndef 使用方法: 注:make是在读取Makefile时就计算条件表达式的值,并根据条件表达式的值 来选择语句,所以,你最好不要把自动化变量(如“$@”等)放入条件表达式中 使用方法: ifXXX XXXX Else endif
Makefile中的语法 Makefile中的语法 3. 函数调用 $(<function>; <arguments>;) 参数间以逗号“,”分隔 例:$(subst <from>;,<to>;,<text>;)把字串<text>;中的<from>;字符串替换成<to>; Makefile中支持的函数不多,简单列举如下 # 将字符串”Main.cpp Draw.cpp”中的cpp换成o SRC_FILE = Main.cpp Draw.cpp OBJECT = $(subst cpp, o, $(SRC_FILE))
Makefile中的语法 Makefile中的函数 字符串处理函数 $(subst <from>;,<to>;,<text>;) 功能:把字串<text>;中的<from>;字符串替换成<to>;。 $(patsubst <pattern>;,<replacement>;,<text>;) 将<text>中和<pattern>模式匹配的换成<replacement>,可用通配符%表示任意长度字符串 $(strip <string>;) 去掉<string>;字串中开头和结尾的空字符。 $(findstring <find>;,<in>;) 在字串<in>;中查找<find>;字串。如果找到,那么返回<find>;,否则返回空字符串 $(filter <pattern...>;,<text>;) 以<pattern>;模式过滤<text>;字符串中的单词,保留符合模式<pattern>;的单词。 $(sort <list>;) 给字符串<list>;中的单词排序(升序)。 $(words <text>;) 统计<text>;中字符串中的单词个数。 $(wordlist <s>,<e>,<text>) 从字符串<text>;中取从<s>开始到<e>的单词串 $(firstword <text>;) 返回字符串<text>;的第一个单词。
Makefile中的语法 Makefile中的函数 文件名操作函数 $(dir <names...>;) $(notdir <names...>;) 从文件名序列<names>中取出非目录部分。非目录部分是指最后一个反斜杠(“/”)之后的部分。 $(suffix <names...>;) 从文件名序列<names>;中取出各个文件名的后缀 $(basename <names...>;) 从文件名序列<names>;中取出各个文件名的前缀部分 $(addsuffix <suffix>;,<names...>;) 把前缀<prefix>;加到<names>;中的每个单词后面。 $(join <list1>;,<list2>;) 把<list2>;中的单词对应地加到<list1>;的单词后面 循环处理函数 $(foreach <var>;,<list>;,<text>;) 把参数<list>;中的单词逐一取出放到参数<var>;所指定的变量中,然后再执行<text>; 所包含的表达式。
Makefile中的语法 Makefile中的函数 reverse = $(2) $(1) call 函数 $(call <expression>;,<parm1>;,<parm2>;,<parm3>;...) 你可以写一个非常复杂的表达式,这个表达式中可以定义许多参数,然后你可以用call函数来向这个表达式传递参数。<expression>;中用$(1),$(2),$(3)代替传入的参数 shell 函数 $(shell <Command>) shell函数也不像其它的函数。顾名思义,它的参数应该就是操作系统Shell的命令。 它和反引号“`”是相同的功能。 其他函数 $(wildcard PATTERN) 模式匹配函数,返回符合通配符PATTERN的字符串。 reverse = $(2) $(1) foo = $(call reverse,a,b) files := $(shell echo *.c) SRC_FILE = $(wildcard ./*.c)
如何让依赖关系的自动化 依赖关系的自动化 在Makefile中,依赖关系可能会需要包含一系列的头文件,在加入或删除头文件时,也需要小心地修改Makefile,这是一个很没有维护性的工作,下面介绍一种能自动生成依赖关系的方法 大多数的C/C++编译器都支持一个“-M”的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。 如果使用的是GNU编译器,最好使用-MM,否则-M会把标准库的头文件列出来。 cc -M main.c 其输出是: main.o : main.c defs.h
如何让依赖关系的自动化 依赖关系的自动化 编译器的这个功能如何与我们的Makefile联系在一起呢? GNU组织建议把编译器为每一个源文件的自动生成的依赖关系放到一个文件中,为每一个“name.c”的文件都生成一个“name.d”的Makefile文件,[.d]文件中就存放对应[.c]文件的依赖关系。 于是,我们可以写出[.c]文件和[.d]文件的依赖关系,并让make自动更新或自成[.d]文件,并把其包含在我们的主Makefile中。 删除现有.d文件 $(DEPS_FILE) : %.d: %.c @set -e; rm -f $@; \ $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ Sinclude $(DEPS_FILE) 通过CC –M生成自动依赖关系 将.d文件(依赖文件)放到目标侧
Makefile的运行 Make命令简介 Makefile的运行是由make命令来完成的,下面说明一下make命令。 make [-f makefile文件名][选项][宏定义][目标] Make的默认的文件名是makefile,Makefile 默认的目标是all “-C <dir>”:指定makefile的目录 “-f=<file>”:指定makefile文件名 ”-i“:在执行时忽略所有的错 “-n”:仅输出执行过程中的命令序列,但并不执行 “-p”:输出makefile中的所有数据,包括所有的规则和变量
实践应用举例 题目 当前目录下源文件的依赖关系如下图所示,写makefile将其编译成test 。 Def1.h Def2.h Def3.h Main.c F1.c F2.c Main.o F1.o F2.o Main
实践应用举例 makefile1 优点:结构清晰 缺点 文件数多时工作量很大;每增加一个文件,修改量大。 文件的依赖关系由开发人员判断,存在遗漏。 test : main.o f1.o f2.0 cc -o test main.o f1.o f2.0 main.o : main.c def1.h cc -c main.c f1.o : f1.c def1.h def2.h cc -c f1.c f2.o : f2.c def2.h def3.h cc -c f2.c
实践应用举例 Makefile2 使用变量 优点:与makefile1比较,增删文件时,只需要修改一处地方 缺点 文件的依赖关系只有.c文件的依赖,如果出现了头文件的修改可能需要clean后再编译 OBJECTS = main.o f1.o f2.0 test : $(OBJ_FILE) cc -o $@ $(OBJ_FILE) (OBJ_FILE) : %o : %c cc -c $<
实践应用举例 优点: 不管多少文件,makefile不需要扩展 增加删除文件时,不需要修改makefile 缺点 文件的依赖关系只有.c文件的依赖,如果出现了头文件的修改可能需要clean后再编译 SRC_FILE = $(wildcard ./*.c) OBJ_FILE = $(patsubst %.c, %.o, $(SRC_FILE)) test : $(OBJ_FILE) cc -o $@ $(OBJ_FILE) (OBJ_FILE) : %o : %c cc -c $<
实践应用举例 Makefile4 引入自动依赖 优点: 不管怎么修改、增删源文件都不需要修改makefile SRC_FILE = $(wildcard ./*.c) OBJ_FILE = $(patsubst %.c, %.o,$(SRC_FILE)) DEPS_FILE = $(patsubst %.c, %.d, $(SRC_FILE)) test : $(OBJECTS) cc -o $@ $(OBJ_FILE) (OBJ_FILE) : %o : %c cc -c $< $(DEPS_FILE) : %.d: %.c @set -e; rm -f $@; \ $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ Sinclude $(DEPS_FILE) 优点: 不管怎么修改、增删源文件都不需要修改makefile 实现了自动依赖,makefile能自动识别需要编译的文件
Copyright © 2018 版权所有 东软集团