Makefile知识总结
程序的编译和链接过程
- 编译:
- 编译概念:一般情况下,源文件(
.c或.cpp)会被编译成中间代码文件,在 Windows 下也就是.obj文件,UNIX 下是.o文件,即 Object File,这个动作叫做编译。 - 编译执行内容:检验语法、函数与变量声明是否正确。
- 编译结果:每个源文件都应对应一个中间目标文件(
.obj或.o),由于编译生成的中间目标文件太多,我们要给中间目标文件打个包,在 Windows 下这种包叫“库文件”(Library File),也就是.lib文件,在 UNIX 下,是 Archive File,也就是.a文件。
- 编译概念:一般情况下,源文件(
- 链接:
- 链接执行内容:主要链接函数与全局变量。此阶段,链接器会在所有的 Object File 中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error),在 VC 下,这种错误一般是:Link 2001 错误,意思说是说,链接器未能找到函数的实现。
makefile规则
-
makefile规则:
-
target ... : prerequisites ... recipe ... ... -
核心内容:target 这一个或多个的目标文件依赖于 prerequisites 中的文件,其生成规则定义在 command 中。prerequisites中如果有一个以上的文件比target文件要新的话,recipe所定义的命令就会被执行。
-
关键字解释:
- target:可以是一个 object file(目标文件),也可以是一个可执行文件,还可以是一个标签(label)。
- prerequisites(先决条件):生成该 target 所依赖的文件和
/或 target。 - recipe(内容):该 target 要执行的命令(任意的 shell 命令)。
-
makefile示例1
-
示例:
-
前提:假定一个工程有 3 个头文件和 8 个
C文件,makefile需要依照下列规则进行编译和链接。- 如果这个工程没有编译过,那么我们的所有
c文件都要编译并被链接 - 如果这个工程的某几个
c文件被修改,那么我们只编译被修改的c文件,并链接目标程序。 - 如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的
c文件,并链接目标程序。
- 如果这个工程没有编译过,那么我们的所有
-
makefile内容(反斜杠
\是换行符的意思):edit : main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o gcc -o edit main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o main.o : main.c defs.h gcc -c main.c kbd.o : kbd.c defs.h command.h cc -c kbd.c command.o : command.c defs.h command.h gcc -c command.c display.o : display.c defs.h buffer.h gcc -c display.c insert.o : insert.c defs.h buffer.h gcc -c insert.c search.o : search.c defs.h buffer.h gcc -c search.c files.o : files.c defs.h buffer.h command.h gcc -c files.c utils.o : utils.c defs.h gcc -c utils.c clean : rm edit main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o
-
make工作过程
- 输入make命令后:
- make 会在当前目录下找名字叫“Makefile”或“makefile”的文件。
- 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文件,并把这个文件作为最终的目标文件。
- 如果 edit 文件不存在,或是 edit 所依赖的后面的
.o文件的文件修改时间要比 edit 这个文件新, 那么,他就会执行后面所定义的命令来生成 edit 这个文件。 - 如果 edit 所依赖的
.o文件也不存在,那么 make 会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。 - 当然,你的 C 文件和头文件是存在的,于是 make 会生成
.o文件,然后再用.o文件生成 make 的终极任务,也就是可执行文件 edit 了。
$@, $^, $< , $?, $%, $+
$@:表示目标文件$^:表示所有的依赖文件$<:表示第一个依赖文件$?:表示比目标还要新的依赖文件列表$%:仅当目标是函数库文件时,表示规则中的目标成员名。例如,如果一个目标是“foo.a(bar.o)”,那么,“$%”就是“bar.o”,“$@”就是“foo.a”。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。$+:类似$^,也是所有依赖目标的集合。只是它不去除重复的依赖目标。
包含其他文件(mk/sh)
-
示例:
include foo.make *.mk $(bar) #等价于 include foo.make a.mk b.mk c.mk bish bash环境变量
.INCLUDE_DIRS包含当前 make 会寻找的目录列表。
make执行步骤
-
读入所有的 Makefile。
-
读入被 include 的其它 Makefile。
-
初始化文件中的变量。
-
推导隐式规则,并分析所有规则。
-
为所有的目标文件创建依赖关系链。
-
根据依赖关系,决定哪些目标要重新生成。
-
执行生成命令。
1-5 步为第一个阶段,6-7 为第二个阶段。第一个阶段中,如果定义的变量被使用了,那么,make 会 把其展开在使用的位置。
列出所有文件*
-
列出一确定文件夹中的所有
.c文件:objects := $(wildcard *.c)。 -
列出 (1) 中所有文件对应的
.o文件,在(3)中我们可以看到它是由 make 自动编译出的:$(patsubst %.c,%.o,$(wildcard *.c))。 -
由 (1)(2) 两步,可写出编译并链接所有
.c和.o文件:objects := $(patsubst %.c,%.o,$(wildcard *.c)) foo : $(objects) cc -o foo $(objects)
特殊变量
- VPATH:存储大项目中许多源文件的路径。
- 示例:
VPATH = src:../headers。定义指定两个目录,“src”和“../headers”,make 会按照这个顺序进行搜索。目录由“冒号” 分隔。(当前目录永远是最高优先搜索的地方)
- 示例:
- MAKE:make的一系列命令
cd subdir && $(MAKE):是先进入“subdir”目录,然后执行 make 命令。
- MAKELEVEL:如果make 有一个嵌套执行的动作,那么这个变量会记录了当前 Makefile 的调用层数。
伪目标
-
伪目标:“伪目标”并不是一个文件,只是一个标签,所以 make 无法生成它的依赖关系和决定它是否要执行。只有通过显式地指明这个“目标”才能让其生效。
“伪目标”的取名不能和文件名重名,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“
.PHONY”来显式地指明 一个目标是“伪目标”。
函数汇总及部分函数解析
- addsuffix:
- basename:
- call:
- dir:
- findstring:
- 语法格式:
$(findstring ,<find>,<in>)。 - 名称:查找字符串函数
- 功能:在字串
<in>中查找<find>字串。 - 返回:如果找到,那么返回
<find>,否则返回空字符串。 - 示例:
$(findstring a,a b c),$(findstring a,b c)。第一个函数返回 a 字符串,第二个返回空字符串
- 语法格式:
- filter:
- 语法格式:
$(filter <pattern...>,<text>)。 - 名称:过滤函数
- 功能:以
<pattern...>模式过滤<text>字符串中的单词,保留符合模式<pattern...>的单词。可以有多个模式。 - 返回:返回符合模式
<pattern...>的字串。 - 示例:
sources := foo.c bar.c baz.s ugh.h,$(filter %.c %.s,$(sources))返回的值是foo.c bar.c baz.s。
- 语法格式:
- filter-out:
- firstword:
- foreach:
- join:
- notdir:
- 语法格式:
$(notdir <name...>) - 名称:取文件函数
- 功能:从文件名序列
<names>中取出非目录部分,非目录部分是指最后一个反斜杠/之后的部分。 - 返回:返回文件名序列
<name>的非目录部分。 - 示例1:
$(notdir src/foo.c hacks)返回值是foo.c hacks。 - 示例2:
notdir $(SRCS):去除SRCS路径的绝对路径部分,只保留文件名。
- 语法格式:
- origin:
- patsubst:
- 语法格式:
$(patsubst ,<pattern>,<replacement>,<text>)。 - 名称:模式字符串替换函数。
- 功能:查找
<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式<pattern>,如果匹配的话,则以<replacement>替换。这里,<pattern>可以包括通配符%, 表示任意长度的字串。如果<replacement>中也包含%,那么,<replacement>中的这个%将是<pattern>中的那个%所代表的字串。(可以用\来转义,以\%来表示真实含义的%字符) - 返回:函数返回被替换过后的字符串。
- 示例1:
$(patsubst %.c,%.o,x.c.c bar.c)。把字串x.c.c bar.c符合模式%.c的单词替换成%.o,返回结果是x.c.o bar.o。 - 示例2:
patsubst %cpp, %o, text:把text字符串中符合%cpp形式的文本替换为%o。
- 语法格式:
- subst:
- 语法格式:
$(subst <from>,<to>,<text>)。 - 名称:字符串替换函数
- 功能:把字串
<text>中的<from>字符串替换成<to>。 - 返回:函数返回被替换过后的字符串。
- 示例:
$(subst ee,EE,feet on the street)。把feet on the street中的ee替换成EE,返回结果是fEEt on the strEEt。
- 语法格式:
- suffix:
- sort:
- strip:
- 语法格式:
$(strip <string>)。 - 名称:去空格函数。
- 功能:去掉
<string>字串中开头和结尾的空字符。 - 返回:返回被去掉空格的字符串值。
- 示例:
$(strip a b c ),把字串去掉开头和结尾的空格,结果是 a b c。
- 语法格式:
- shell :
- 功能:shell 函数把执行操作系统命令后的输出作为函数返回。
- word:
- wildcard:
- 作用:
- 可以使用
“$(wildcard *.c)”来获取工作目录下的所有的.c文件列表。 - 可以使用
“$(patsubst %.c,%.o,$(wildcard *.c))”,首先使用“wildcard”函数获取工作目录下的.c文件列表;之后将列表中所有文件名的后缀.c替换为.o。
- 可以使用
- 示例:
wildcard ./*.cpp。获取指定路径下的所有cpp文件。
- 作用:
- wordlist:
- words:
:=, =, ?=和+=的含义
-
:=:表示直接赋值,赋予当前位置的值。-
示例:VIR_B的值是A B,即根据当前位置进行赋值。
VIR_A := A VIR_B := $(VIR_A) B VIR_A := AA
-
-
=: 使用”=”进行赋值,变量的值是整个makefile中最后被指定的值。-
示例:VIR_B的值是AA B,而不是A B。
VIR_A = A VIR_B = $(VIR_A) B VIR_A = AA
-
-
?=:表示如果该变量没有被赋值,则赋予等号后的值。 -
+=:表示将等号后面的值添加到前面的变量上
makefile示例2
CC = g++
LIBS = -lpthread
INCLUDE = -I../webserver/head
SRCDIR = ../webserver/src
HEADDIR = ../webserver/head
SRCFILES = $(SRCDIR)/*.cpp
HEADFILES = $(HEADDIR)/*.h
TARGET = webserver
$(TARGET): $(SRCFILES) $(HEADFILES)
$(CC) $(INCLUDE) $(LIBS) $(SRCFILES) -o $(TARGET)
clean:
rm -f $(TARGET)
浙公网安备 33010602011771号