嵌入式Linux开发之Makefile
Posted 大奥特曼打小怪兽
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了嵌入式Linux开发之Makefile相关的知识,希望对你有一定的参考价值。
makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。
makefile的好处就是:
—“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。二、节约编译时间(没改动的文件不编译)。
make是一个命令工具,是一个解释makefile中指令的命令工具。
一、Makefile规则
1.1 基本规则
一个简单的Makefile文件包含一系列的规则,其样式如下:
target:prerequisites
command
...
...
=====================
目标 : 依赖文件
[tab键] 命令
...
...
- target: 是目标文件,可以是object file,也可以是执行文件,还可以是一个标签label。如果目标文件的更新时间晚于依赖文件的更新时间,则说明依赖文件没有改动,目标文件不需要重新编译。否则重新编译并更新目标。
- prerequisites:即目标文件由哪些文件生成。如果依赖条件中存在不存在的依赖条件,则会寻找其它规则是否可以产生依赖条件。例如:规则一是生成目标 hello.elf需要使用到依赖条件 hello.o,但是 hello.o 不存在。则 Makefile 会寻找到一个生成 hello.o 的规则二并执行。
- command:即通过执行该命令,由依赖文件生成目标文件。注意每条命令前必须有且仅有一个 tab 保持缩进,这是语法要求。
- ALL:Makefile 文件默认只生成第一个目标文件即完成编译,但是我们可以通过 “ALL” 指定需要生成的目标文件。
这是一个文件依赖关系,也就是说target是由一个或多个目标文件依赖于prerequisites中的文件,其生成规则定义在command中,而且只要prerequisites中有一个以上的文件比target文件更新的话,command所定义的命令就会被执行,这是makefile的最基本规则,也是makefile中最核心的内容。
1.2 示例
我们仍然以hello.c文件为例:
#include <stdio.h>
int main(int argc,char *argv[])
{
printf("Hello World!\\n");
return 0;
}
然后编写Makefile(这里使用的是gcc、而不是arm-linux-gcc):
ALL: hello.o
hello.o: hello.c
gcc hello.c -o hello.o
编译并执行:
make
./hello.o
二、Makefile函数
2.1 获取匹配模式文件名函数wildcard
- 语法:$(wildcard PATTERN)
- 函数功能:列出当前目录下所有符合模式“ PATTERN”格式的文件名。“PATTERN”使用 shell可识别的通配符,包括“ ?”(单字符)、“*”(多字符)等。
- 返回值:空格分割的、存在当前目录下的所有符合模式“ PATTERN”的文件名。
例如:
SRC = $(wildcard ./*.c)
匹配目录下所有的 .c 文件,并将其赋值给 SRC 变量。
2.2 模式替换函数patsubst
pat 是 pattern 的缩写,subst 是 substring 的缩写。
- 语法:$(patsubst PATTERN,REPLACEMENT,TEXT)
- 函数功能:搜索“ TEXT”中以空格分开的单词,将否符合模式“ TATTERN ”替换为“REPLACEMENT ”。参数“PATTERN”中可以使用模式通配符 “%”来代表一个单词中的若干字符。 如果参数“REPLACEMENT ”中也包含一个“%”,那么“ REPLACEMENT ”中的“ %”将是“ TATTERN”中的那个“ %”所代表的字符串。在“ TATTERN ”和“REPLACEMENT ”中,只有第一个“ %”被作为模式字符来处理,之后出现的不再作模式字符(作为一个字符)。在参数中如果需要将第一个出现的“ %”作为字符本身而不作为模式字符时,可使用反斜杠“ ”进行转义处理(转义处理的机制和使用静态模式的转义一致,
- 返回值:替换后的新字符串。
参数 “TEXT ”单词之间的多个空格在处理时被合并为一个空格,并忽略前导和结尾空格。
例如:
OBJ = $(patsubst %.c, %.o, $(SRC))
这个函数有三个参数,意思是取出 SRC 中所有的值,然后将 “.c” 替换为 “.o”,最后赋值给 OBJ 变量。
2.3 foreach 函数
函数“foreach ”不同于其它函数。它是一个循环函数。类似于 Linux 的 shell 中的for 语句:
- 语法:$(foreach VAR,LIST,TEXT)
- 函数功能: 这个函数的工作过程是这样的:如果需要(存在变量或者函数的引用),首先展开变量“ VAR”和“ LIST”的引用;而表达式“ TEXT”中的变量引用不展开。执行时把“ LIST”中使用空格分割的单词依次取出赋值给变量“VAR”,然后执行“ TEXT”表达式。重复直到“ LIST”的最后一个单词(为空时结束)。“TEXT ”中的变量或者函数引用在执行时才被展开,因此如果在“TEXT”中存在对“ VAR”的引用,那么“ VAR”的值在每一次展开式将会到的不同的值。
- 返回值: 空格分割的多次表达式“ TEXT ”的计算的结果。
2.4 过滤函数 -filter
“filter”函数可以用来去除一个变量中的某些字符串, 我们下边的例子中就是用到了此函数。
1).找出符合PATTERN 格式的值
- 语法:$(filter PATTERN ⋯,TEXT)
- 函数功能:过滤掉字串“ TEXT”中所有不符合模式“ PATTERN ”的单词,保留所有符合此模式的单词。可以使用多个模式。模式中一般需要包含模式字符“%”。存在多个模式时,模式表达式之间使用空格分割。
- 返回值:空格分割的“ TEXT”字串中所有符合模式“ PATTERN ”的字串。
2).找出不符合PATTERN 格式的值
- 语法:$(filter-out PATTERN ⋯,TEXT)
- 函数功能:过滤掉字串“ TEXT”中所有符合模式“ PATTERN ”的单词,保留所有不符合此模式的单词。可以使用多个模式。模式中一般需要包含模式字符“%”。存在多个模式时,模式表达式之间使用空格分割。
- 返回值:空格分割的“ TEXT”字串中所有不符合模式“ PATTERN ”的字串。
假如我们目录下有很多个 “.c” 后缀的源文件,就不需要写很多条规则语句了,
2.5 示例
我们修改我们的Makefile文件:
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))
ALL: hello.out
hello.out: $(OBJ)
gcc $(OBJ) -o hello.out
$(OBJ): $(SRC)
gcc -c $(SRC) -o $(OBJ)
这里我们先将所有的 “.c” 文件编译为 “.o” 文件,这样后面更改某个 “.c” 文件时,其它的 “.c” 文件将不再编译,而只是编译有更改的 “.c” 文件,可以大大节约大项目中的编译速度。
需要注意的是:
- .o文件一般是通过编译的但还未链接的
- .out文件一般都是经过相应的链接产生的可执行文件(linux下)
三、makefile通配符
Makefile 中也有一些已经定义好的常用变量,这里介绍其中常用的3个。
3.1 $@
表示规则中目标,例如 hello.out
3.2 $<
表示规则中的第一个依赖条件,例如 hello.c
3.3 $^
表示规则中的所有依赖条件,由于我们示例中都只有一个依赖条件,这种情况下 $^ 和 $< 区别不大
3.4 示例
我们修改我们的Makefile文件:
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))
ALL: hello.out
hello.out: $(OBJ)
gcc -o $@ $<
$(OBJ): $(SRC)
gcc -c -o $@ $<
四、其它常用功能
4.1 代码清理clean
我们可以编译一条属于自己的 clean 语句,来清理 make 命令所产生的所有文件。例如
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))
ALL: hello.out
hello.out: $(OBJ)
gcc -o $@ $<
$(OBJ): $(SRC)
gcc -c -o $@ $<
clean:
rm -rf $(OBJ) *.out
这样我们就可以使用make clean 命令来清理生成的文件了:
提示:make命令是可以带上目标名的,如果make后面不跟目标名字的话,默认生成第一个目标,当带上目标名的话,生成指定的目标。
4.2 伪目标 .PHONY
上面我们写了一个 clean 语句,使得我们执行 “make clean” 命令的时候,可以清理我们生成的文件。
但是假如还存在一个文件名就是 clean 文件,那么我们再执行 “make clean” 命令的时候就只是显示:
make clean
make: `clean\' is up to date.
为什么?我们看一看Makefile的核心规则:
- 目标文件不存在;
- 某个依赖文件比目标文件新;
但是现在目录中有名为clean的文件,那目标文件存在,那就取决于依赖,但是在Makefile中clean目标没有依赖,所以没有办法通过判断依赖的的时间去更新clean目标,所以clean目标文件一直都是目录中那么clean文件。所以说,如果目录中有何clean同名文件时就没有办法执行clean操作了。
解决方法就是我们使用伪目标,把这个目标定义为假想目标这样就可以避免出现上面的问题了,例如:
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))
ALL: hello.out
hello.out: $(OBJ)
gcc -o $@ $<
$(OBJ): $(SRC)
gcc -c -o $@ $<
clean:
-rm -rf $(OBJ) hello.out
.PHONY: clean ALL
通常,我们也会把 ALL 也设置为伪目标。
五、Makefile变量
变量的种类分为:
- 既时变量(简单变量):例子: 赋值方式: A := xxx #A的值在定义就可以确定,即刻赋值
-
延时变量:例子:赋值方式:A =xxx #A的值在使用到的时候才会确定
- ?= : 延时变量,第一次定义才有效,如果这个变量在前面已经定义过,那么不执行这句赋值
- += : 可以是即时变量也可以是延时变量,取决于这个变量的定义
比如我们编写Makefile文件:
A:=$(C)
B=$(C)
C=123
ALL:
@echo A=$(A)
@echo B=$(B)
C赋值给A、但是C现在的值为空,B是延时变量,等到用到时才确定,所以执行到C=123才会显示B的值,也就是123.
参考文章
以上是关于嵌入式Linux开发之Makefile的主要内容,如果未能解决你的问题,请参考以下文章