完整的例子:
简化:
make是一个命令工具,它解释makefile中的指令(或者说规则),在Linux环境下使用GNU的make 工具能够比较容易地构建一个工程,整个工程的编译只需要一个命令就可以完成编译、链接以至于最后的执行。
make在执行时,需要一个命名为Makefile的文件。
make -c //make目标文件所在目录
-f //目标文件
make会自动根据修改情况(通过比较对应文件(规则的目标和依赖)的最后修改时间,来决定哪些文件要更新,哪些文件不需要更新),完成源文件对应.o文件的更新、库文件的更新、最终可执行程序的更新。
当使用make工具进行编译时,工程中以下几种文件在执行make时将会被编译:
1.如果是第一次编译,则所有源文件都会被进行编译。
2.每一个在上次执行make之后修改过的源代码文件在本次执行make时将会重新编译。
3.头文件在上一次执行make之后被修改,则所有包含此头文件的C源文件在本次执行make时将会被重新编译。
make的执行过程如下:
1. 依次读取变量“MAKEFILES”定义的makefile文件列表
2. 读取工作目录下的makefile文件(根据命名的查找顺序“GNUmakefile”,“makefile”,“Makefile”,首先找到那个就读取那个)
3. 依次读取工作目录makefile文件中使用指示符“include”包含的文件
4. 查找重建所有已读取的makefile文件的规则(如果存在一个目标是当前读取的某一个makefile文件,则执行此规则重建此makefile文件,完成以后从第一步开始重新执行)
5. 初始化变量值并展开那些需要立即展开的变量和函数并根据预设条件确定执行分支
6. 根据“终极目标”以及其他目标的依赖关系建立依赖关系链表
7. 执行除“终极目标”以外的所有的目标的规则(规则中如果依赖文件中任一个文件的时间戳比目标文件新,则使用规则所定义的命令重建目标文件)
8. 执行“终极目标”所在的规则
makefile的内容
makefile文件描述了整个工程的编译、链接等规则,其中包括:
(1) 工程中哪些文件需要编译以及如何编译
(2) 需要创建哪些库文件以及如何创建这些库文件
(3) 如何最后产生我们想要的可执行文件
makefile的规则组成
目标:依赖文件列表
命令行
...
注意:1.每一个命令行必须以Tab字符开始
2.可以将一个较长行使用反斜线"\"来分解为多行,但反斜线之后不能有空格。
尽量一个规则只存在一个目标文件,但可有多个依赖文件。
一个目标可以没有依赖而只有动作,比如make clean,用来删除make过程产生的中间文件。makefile中把那些没有任何依赖只有执行动作的目标称为“伪目标”(phony targets)
。
默认情况下,make执行makefile中的第一个规则,此规则的第一个目标称为“终极目标”。make在处理终极目标之前,会先处理终极目标的所有依赖文件的更新规则。
如果makefile中一个规则所描述的目标不是“终极目标”相关的依赖,那么这个规则将不会被执行,除非明确指定这个规则。
除了makefile的“终极目标”所在的规则以外,其他规则的顺序在makefile中没有意义。
一个完整的makefile中,包含了5个东西:显式规则、隐含规则、变量的定义、指示符和注释。
批处理文件:windows下.bat文件,Linux下.sh文件。
编译宏在代码中采用,如#if defined(_INSTALL_LINUX);编译变量在makefile中采用,如ifeq(RTOS_TYPE=_INSTALL_LINUX)
变量的使用: $(objects) ,objects为变量,makefile中的变量可以和C语言中的宏一样来理解。
当变量名为单字符时,直接使用$x的格式来实现。shell中变量的引用可以是"${xx}"或"$xx"格式,但在Makefile中多字符变量名的引用只能是"${xx}"或"$(xx)"格式。
两种变量定义(赋值)
1. 递归展开式变量:这种类型变量的定义,可以引用其它的之前没有定义的变量。
foo = $(bar)
bar = $(ugh)
使用此风格的变量定义,可能会由于出现变量的递归定义而导致make陷入到无限的变量展开过程中,最终使make执行失败,如:
x = $(y)
y = $(x)$(z)
2. 直接展开式变量
使用":="来定义变量。此风格变量在定义时就完成了对所引用变量的展开,因此它不能实现对其后定义变量的引用。$
追加变量值:
objects = main.o foo.o bar.o utils.o
objects += another.o
相当于
objects = main.o foo.o bar.o utils.o
objects := $(objects) another.o
如何定义一个空格:
nullstring:=
space:=$(nullstring)
"?="操作符
Foo?=bar //如果变量"Foo"之前没有定义过,就给它赋值"bar",否则不改变它的值
main.o: main.c defs.h
cc -c main.c
"cc -c"用来编译.c源文件,"cc -o"用来编译.o文件,对于目标为"N.o",依赖文件是"N.c"的规则,可使用make的隐规则,因此上述规则也可用 main.o:def.h
%CD% //当前目录
./ 为当前目录
../ 为上一级目录
“#”字符后的内容为注释内容,当正文中需要使用字符"#"时,可用"\#"来实现,如果需要用到"$",可用"$$"。
条件语句:ifdef/ifndef, ifeq/ifneq
clean:
rm edit $(objects)
可写为
.PHONY:clean
clean:
-rm edit $(objects)
(1) 通过".PHONY"特殊目标"clean"目标声明为伪目标,防止当磁盘上存在一个名为"clean"文件时,"clean"所在规则的命令无法执行;(2)在命令行之前使用"-",意思是忽略"rm"的执行错误。
默认情况下,make会在工作目录(执行make的目录)下按照文件名顺序寻找makefile文件读取并执行,查找的文件名顺序为:"GNUmakefile"、“makefile”、“Makefile”,推荐使用"Makefile"作为文件名。
“include”指示符告诉make暂停读取当前的Makefile,而转去读取“include”指定的一个或者多个文件,完成以后再继续当前Makefile的读取。
通配符
*.c代表当前工作目录下所有以.c结尾的文件。
变量定义中使用的通配符不会被展开,需要用函数wildcard,如object=$(wildcard *.o)
目录搜寻
make执行时的目录自动搜索顺序:
1.先在当前目录下搜索
2.如果当前工作目录下不存在,则继续搜索"VPATH"或"vpath"中指定的目录
3.对于库文件,如果还不存在,make程序将搜索系统默认目录,顺序是:/lib、/usr/lib、/usr/local/lib
VPATH为特殊变量,在其定义中,使用空格或冒号将多个目录分开。
vpath为关键字,它可以为不同类型的文件(由文件名区分)指定不同的搜索目录。
VPATH = src:../headers
vpath %.h ../headers
自动化变量
$^:通过目录搜索得到的依赖文件的完整路径名(目录+一般文件名)列表
$@:代表规则的目标
$<:代表规则中通过目录搜索得到的依赖文件列表的第一个依赖文件
命令回显:
make在执行命令行之前会把要执行的命令进行输出,称为回显。
echo 开始编译XXX模块 //将输出"开始编译XX模块"的信息
要取消回显,则用@echo
在Makefile中书定在同一行中的多个命令属于一个完整的shell命令行,书写在独立行的一个命令是一个独立的shell命令行。所以需要注意:在一个规则的命令中,命令行"cd"改变目录不会对其后的命令的执行产生影响。就是说其后的命令执行的工作目录不会是之前使用"cd"进入的那个目录。
在make执行失败时,修改错误之后执行make之前,使用"make clean"明确地删除第一次错误重建的所有目标。
makefile
最新推荐文章于 2021-11-19 23:14:39 发布