Makefile基础学习
Makefile基础学习
Makefile
:描述哪些文件需要编译、哪些文件需要重新编译的文件,使用make
命令对工程进行编译本篇文章参考《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.7》的
3.3
Makefile
基础部分
1.示例
1.1 代码
main.c
1 |
|
input.c
1 |
|
calcu.c
1 |
|
input.h
1 |
|
calcu.h
1 |
|
1.2 编译运行
gcc
手动编译
1 | $ gcc main.c calcu.c input.c -o main |
Makefile
编译
1 | main: main.o input.o calcu.o |
使用
make
命令编译工程,./main
执行可执行程序
1 | $ make |
二、Makefile语法
2.1 Makefile规则格式
1 | 目标...: 依赖文件集合... |
make
命令会为Makefile
中的每个以TAB
开始的命令创建一个Shell
进程去执行
- 具体例子:
1 | main: main.o input.o calcu.o |
这条规则的目标是
main
,``main.o、input.o、calcu.o是生成
main的依赖文件,如果要更新目标
main`,就必须先更新它的所有依赖文件,如果依赖文件中的任何一个有更新,那么目标也必须更新,“更新”就是执行一遍规则中的命令列表
1.2
中Makefile
解释如下:
首先更新第一条规则中的
main
,第一条规则的目标成为默认目标,只要默认目标更新了那么就认为Makefile
的工作完成。在第一次编译的时候由于
main
还不存在,因此第一条规则会执行,第一条规则依赖于文件main.o input.o 、calcu.o
这个三个.o
文件,这三个.o
文件目前还都没有,因此必须先更新这三个文件。make
会查找以这三个.o
文件为目标的规则并执行。以
main.o
为例,发现更新main.o
的是第二条规则,因此会执行第二条规则,第二条规则里面的命令为gcc–c main.c
,不链接编译main.c
只是生成main.o
,其它两个.o
文件同理。最后一个规则目标是
clean
,它没有依赖文件,因此会默认为依赖文件都是最新的,所以其对应的命令不会执行,当我们想要执行clean
的话可以直接使用命令make clean
,执行以后就会删除当前目录下所有的.o
文件以及main
,因此clean
的功能就是清理工程
- 综上,
Make
的执行过程如下:
1、
make
命令会在当前目录下查找以Makefile
(makefile
其实也可以)命名的文件。2、当找到
Makefile
文件以后就会按照Makefile
中定义的规则去编译生成最终的目标文件。3、当发现目标文件不存在,或者目标所依赖的文件比目标文件新(也就是最后修改时间比目标文件晚)的话就会执行后面的命令来更新目标
2.2 Makefile变量及赋值
2.2.1 变量
Makefile
变量都是字符串,类似于C
语言中的宏这里使用变量
objects
表示main.o input.o calcu.o
,引用变量的方式是$(变量名)
,这里就是$(objects)
来使用变量objects
1 | # "#"号开头表示注释 |
2.2.2 赋值
Makefile
赋值符有=
、:=
、?=
以及+=
四种,只不过第四种是变量追加
- 赋值符
=
1 | name = lyj |
由于
Make
在执行过程中会自动输出命令执行过程,使用@
表示不输出命令执行过程
1 | $ make print |
这里输出
thee
而不是lyj
的原因是该赋值符=
表示被赋值变量curname
会根据赋值变量name
的改变而改变即name
最终的值(变量真实值取决于它所引用变量最后一次有效值)
- 赋值符
:=
1 | name = lyj |
1 | $ make print |
这里输出
lyj
是因为赋值符:=
不会使用后面的定义的变量,只能使用前面已经定义好的(类似于C
语言中变量的赋值,赋值结束后就不能更改了)
- 赋值符
?=
1 | curname ?= jack |
该赋值符
?=
表示如果变量curname
前面没有被赋值,那么此变量就赋值为jack
,否则该变量的值不变
- 变量追加
+=
1 | name = lyj |
1 | $ make |
2.2.3 Makefile模式规则
%
表示任意长度的非空字符串,类似于通配符,如%.c
表示所有以.c
结尾的文件
2.2.4 Makefile自动化变量
自动化变量就是这种变量会把模式中所定义的一系列的文件自动的挨个取出,直至所有的符合模式的文件都取完
- 常用自动化变量(其中
$@/$</$^
较常用)
自动化变量 | 描述 |
---|---|
$@ |
规则中的目标集合,在模式规则中,如果有多个目标的话,$@ 表示匹配模式中定义的目标集合 |
$% |
当目标是函数库的时候表示规则中的目标成员名,如果目标不是函数库文件,那么其值为空 |
$< |
依赖文件集合中的第一个文件,如果依赖文件是以模式(即% )定义的,那么$< 就是符合模式的一系列的文件集合 |
$? |
所有比目标新的依赖目标集合,以空格分开 |
$^ |
所有依赖文件的集合,使用空格分开,如果在依赖文件中有多个重复的文件,$^ 会去除重复的依赖文件,只保留一份 |
$+ |
和$^ 类似,但是当依赖文件存在重复的话不会去除重复的依赖文件 |
$* |
这个变量表示目标模式中% 及其之前的部分,如果目标是 test/a.test.c ,目标模式为a.%.c ,那么$* 就是test/a.test |
1 | objects = main.o input.o calcu.o |
这里使用模式规则和自动化变量将
1.2
中Makefile
简化如上
2.2.5 Makefile伪目标
Makefile
中一般的目标名都是要生成的文件,而伪目标不代表真正的目标名,在执行make
命令的时候通过指定这个伪目标来执行其所在规则的定义的命令
使用伪目标主要是为了避免
Makefile
中定义的执行命令的目标和工作目录下的实际文件出现名字冲突,有时候我们需要编写一个规则用来执行一些命令,但是这个规则不是用来创建文件的
例如对于
clean
这个工程清理的代码,如果工作目录中存在clean
的文件,此时执行make clean
则会因为没有依赖文件而认定目标是最新的,后面的命令也不会执行,预先设想的清理功能也无法实现,为避免这个问题,将clean
声明为伪目标,声明方式如下:
1这样不管当前工作目录是否存在名为
clean
的文件,执行make clean
都会执行工程清理
2.2.6 Makefile条件判断
- 语法
1
1 | <条件关键字> |
- 语法
2
1 | <条件关键字> |
- 条件关键词
ifeq/ifneq
、ifdef/ifndef
ifeq
使用方法(相同为真) 参数可以为函数返回值
1
2
3
4
5 ifeq (<参数 1>, <参数 2>)
ifeq '<参数 1>','<参数 2>'
ifeq "<参数 1>","<参数 2>"
ifeq "<参数 1>",'<参数 2>'
ifeq '<参数 1>',"<参数 2>"
ifneq
使用方法同ifeq
(不相同为真)
ifdef
使用方法
1 ifdef <变量名>如果”变量名”值非空,那么表达式为真,否则为假,”变量名”也可以是函数返回值
ifndef
使用方法同ifdef
,但是含义与之相反这里的
ifdef
和ifndef
与C
语言中含义类似
2.2.7 Makefile函数使用
Makefile
支持函数,但无法自定义函数函数用法如下:
1
2
3 $(函数名 参数集合)
# 或者是如下:
${函数名 参数集合}接下来是常用的几个函数
subst
函数:字符串替换
1 | $(subst <from>,<to>,<text>) |
例如:
1 | $(subst thee,lyj,hello thee!) |
patsubst
函数:模式字符串替换
1 | $(patsubst <pattern>,<replacement>,<text>) |
例如:
1 | $(patsubst %.c,%.o,a.c b.c c.c d.c) |
dir
函数:获取目录
1 | $(dir <names...>) |
例如:
1 | $(dir <src/a.c>) |
notdir
函数:去除文件中的目录部分
1 | $(notdir <names...>) |
例如:
1 | $(dir <src/a.c>) |
foreach
函数:循环
1 | $(foreach <var>,<list>,<text>) |
wildcard
函数
通配符
%
只能用在规则中,只有在规则中它才会展开,如果在变量定义和函数使用时,通配符不会自动展开,这个时候就要用到函数wildcard
将其展开
1 | $(wildcard PATTERN...) |
例如:
1 | $(wildcard *.c) |