【嵌入式八股16】Makefile
一、简单的 Makefile 示例
以下是一个简单的 Makefile 示例,展示了如何编译多个源文件生成可执行文件:
CROSS_COMPILE=/opt/4.5.1/bin/arm-linux-
CC=$(CROSS_COMPILE)gcc
AS=$(CROSS_COMPILE)as
LD=$(CROSS_COMPILE)ld
CFLAGS=-g -Wall
LIBS=-lpthread
all:main
main:main.o gsm_gprs.o socket.o telosb.o wifi.o
$(CC) $(CFLAGS) $(LIBS) $^ -o $@
main.o: main.c gsm_gprs.h option.h telosb.h
$(CC) $(CFLAGS) -c $<
gsm_gprs.o:gsm_gprs.c gsm_gprs.h socket.h
$(CC) $(CFLAGS) -c $<
socket.o:socket.c socket.h option.h
$(CC) $(CFLAGS) -c $<
telosb.o: telosb.c telosb.h option.h
$(CC) $(CFLAGS) -c $<
wifi.o: wifi.c wifi.h option.h
$(CC) $(CFLAGS) -c $<
clean:
-rm main -f *\.o *\*~ *~
代码解释
CROSS_COMPILE:指定交叉编译工具链的前缀。CC、AS、LD:分别指定编译器、汇编器和链接器。CFLAGS:编译选项,-g表示生成调试信息,-Wall表示开启所有常见的编译警告。LIBS:链接时需要的库文件,这里使用了-lpthread表示链接 pthread 库。all:默认目标,指定要生成的最终可执行文件。main:可执行文件的目标,依赖于多个.o文件,通过$(CC)进行链接。*.o:每个.o文件的目标,依赖于对应的.c文件和头文件,通过$(CC) -c进行编译。clean:清理目标,用于删除生成的可执行文件和.o文件。
二、Makefile 赋值方式
Makefile 提供了多种赋值方式,每种方式有不同的特点,如下表所示:
= |
基本的赋值方式,会在 Makefile 的最后才进行赋值,即采用延迟赋值。例如,一个变量在多处被赋值,最终的值是最后一次赋值的结果。 |
:= |
覆盖之前的值,并且会立即赋值。在使用该符号赋值时,变量的值在赋值语句执行时就确定了,不会受到后续赋值的影响。 |
?= |
如果变量没有被赋值过,则进行赋值;如果已经有值,则保持不变。这可以用于设置默认值。 |
+= |
向变量已有的值后面添加新的值,常用于追加编译选项、库文件等。 |
三、伪目标 .PHONY
.PHONY : clean
PHONY 用于声明伪目标。伪目标不是真正的文件,而是一种执行特定操作的目标。例如,clean 目标通常用于清理生成的文件,但如果当前目录下恰好存在一个名为 clean 的文件,make clean 可能不会按预期执行。使用 .PHONY 声明后,make 会忽略同名文件的存在,确保 clean 目标的操作能够正常执行。
四、make 命令默认支持的文件名
make 指令如果没有指定具体的 Makefile 文件,会自动按照以下顺序寻找 Makefile 文件:
| GNUmakefile |
| makefile |
| Makefile |
通常建议使用 Makefile 作为文件名,因为它是最常见的命名方式,便于识别和管理。
五、include 和 -include
include
include 用于在一个 Makefile 中包含另一个 Makefile 文件。这可以将一个大型的 Makefile 拆分成多个小的文件,提高代码的可读性和可维护性。例如:
include another.mk
-include
-include 与 include 类似,但当包含过程中出现错误(如文件不存在)时,不会报错,而是继续执行 Makefile。例如:
-include optional.mk
六、MAKEFILES 环境变量
MAKE 会自动包含 MAKEFILES 环境变量中指定的值。可以通过设置该环境变量来指定额外的 Makefile 文件,例如:
export MAKEFILES="common.mk"
make
这样,make 在执行时会自动包含 common.mk 文件。
七、VPATH 和 vpath
VPATH
VPATH 用于指定 Makefile 搜索文件的路径。当 Makefile 中需要的文件不在当前目录时,可以通过 VPATH 指定搜索路径,例如:
VPATH = src:include
这表示 make 会在 src 和 include 目录中搜索所需的文件。
vpath
vpath 是 make 的关键词,用于设置文件的搜索路径。与 VPATH 不同的是,vpath 可以针对不同类型的文件设置不同的搜索路径,例如:
vpath %.c src
vpath %.h include
这表示 make 会在 src 目录中搜索 .c 文件,在 include 目录中搜索 .h 文件。
八、Makefile 内建函数
(一)文本处理和分析函数
1. 替换函数 $(subst from,to,text)
该函数用于将 text 中所有的 from 替换为 to,例如:
$(subst old,new,old text)
结果为 new text。
2. 模式替换函数 $(patsubst pattern,replacement,text)
可以使用 % 作为通配符(只有第一个 % 有效),用于将 text 中匹配 pattern 的部分替换为 replacement,例如:
$(patsubst %.c,%.o,x.c.c bar.c)
结果为 x.c.o bar.o。
3. 去除空格函数 $(strip string)
该函数用于去掉 string 两端的空格,并将连续的多个空格替换为一个空格,例如:
$(strip hello world )
结果为 hello world。
4. 查找函数 $(findstring find,in)
用于在 in 中查找 find,如果找到则返回 find,否则返回空字符串,例如:
$(findstring abc,abcdef)
结果为 abc。
5. 过滤函数 $(filter pattern…,text)
只保留 text 中匹配 pattern 的部分,例如:
$(filter %.c,foo.c bar.h baz.c)
结果为 foo.c baz.c。
6. 过滤掉函数 $(filter-out pattern…,text)
与 filter 相反,不保留 text 中匹配 pattern 的部分,例如:
$(filter-out %.c,foo.c bar.h baz.c)
结果为 bar.h。
7. 排序函数 $(sort list)
对 list 中的元素进行排序,去除重复元素,例如:
$(sort b a c a)
结果为 a b c。
8. 取字符串函数 $(word n,text)
返回 text 中第 n 个单词,例如:
$(word 2,hello world)
结果为 world。
9. 取字符串列表函数 $(wordlist s,e,text)
返回 text 中从第 s 个到第 e 个单词组成的列表,例如:
$(wordlist 1,2,hello world foo)
结果为 hello world。
10. 取第一个函数 $(firstword names…)
返回 names 中的第一个单词,例如:
$(firstword a b c)
结果为 a。
11. 取最后一个函数 $(lastword names…)
返回 names 中的最后一个单词,例如:
$(lastword a b c)
结果为 c。
(二)文件名处理函数
1. 取目录函数 $(dir names…)
返回 names 中每个文件名的目录部分,例如:
$(dir src/foo.c bar.c)
结果为 src/ ./。
2. 取文件函数 $(notdir names…)
返回 names 中每个文件名的文件部分,但要注意如果文件名中包含斜杠,结果可能不准确,例如:
$(notdir src/foo.c bar.c)
结果为 foo.c bar.c。
3. 取文件后缀函数 $(suffix names…)
返回 names 中每个文件名的后缀部分,例如:
$(suffix src/foo.c bar.h)
结果为 .c .h。
4. 取文件名函数 $(basename names…)
返回 names 中每个文件名去掉后缀的部分,包括前面的目录部分,例如:
$(basename src/foo.c src-1.0/bar hacks)
结果为 src/foo src-1.0/bar hacks。
5. 添加后缀函数 $(addsuffix suffix,names…)
为 names 中的每个文件名添加后缀 suffix,例如:
$(addsuffix .c,foo bar)
结果为 foo.c bar.c。
6. 连接函数 $(join list1,list2)
将 list1 和 list2 中的元素一一对应连接起来,例如:
$(join a b,.c .o)
结果为 a.c b.o。
7. 通配符函数 $(wildcard pattern)
用于匹配符合 pattern 的现有文件,并返回一个以空格分隔的文件列表,例如:
$(wildcard *.c)
返回当前目录下所有 .c 文件的列表。
8. 真实路径函数 $(realpath names…)
返回 names 中每个文件的真实路径,例如:
$(realpath ../../)
返回上上级目录的真实路径。
9. 绝对路径函数 $(abspath names…)
返回 names 中每个文件的绝对路径,例如:
$(abspath foo.c)
返回 foo.c 的绝对路径。
(三)foreach 函数
$(foreach var,list,text) 相当于一个 for 循环函数,它会将 list 中的每个元素依次赋值给 var,并执行 text 中的表达式,最终返回一个由 text 结果组成的列表,例如:
find_files = $(wildcard $(dir)/*) # “=” 等号是延时加载(deferred)
dirs := a b c d
files := $(foreach dir,$(dirs),$(find_files))
相当于:
files := $(wildcard a/* b/* c/* d/*)
(四)if 函数
if 函数用于条件判断,有以下几种形式:
ifeq (arg1, arg2)
ifneq (arg1, arg2)
ifdef variable-name
ifndef variable-name
ifeq:判断arg1和arg2是否相等。ifneq:判断arg1和arg2是否不相等。ifdef:判断变量variable-name是否已经定义。ifndef:判断变量variable-name是否未定义。
(五)call 函数
$(call VARIABLE,PARAM,PARAM,...)
call 函数是唯一一个可以创建定制化参数函数的引用函数。可以将一个变量定义为一个复杂的表达式,然后使用 call 函数根据不同的参数对它进行展开,获得不同的结果。例如:
reverse = $(2) $(1)
foo = $(call reverse,a,b)
此时 foo 的值为 b a。
(六)value 函数
$(value variable)
该函数返回变量 variable 的值,并且不会对值进行展开。例如:
FOO = $PATH
all:
@echo $(FOO)
@echo $(value FOO)
第一行输出会将 $P 作为 Makefile 变量进行展开,结果可能是错误的;而第二行输出会直接输出当前环境变量 $PATH 的值,因为 value 函数避免了展开。
(七)origin 函数
$(origin variable)
该函数用于获取变量的属性值,返回值有以下几种情况:
undefined:变量VARIABLE没有被定义。default:变量VARIABLE是一个默认定义(内嵌变量),如CC、MAKE、RM等变量。如果在 Makefile 中重新定义这些变量,函数返回值将相应发生变化。environment:变量VARIABLE是一个系统环境变量,并且make没有使用命令行选项-e(Makefile 中不存在同名的变量定义,此变量没有被替代)。environment override:变量VARIABLE是一个系统环境变量,并且make使用了命令行选项-e。Makefile 中存在一个同名的变量定义,使用make -e时环境变量值替代了文件中的变量定义。file:变量VARIABLE在某一个 Makefile 文件中定义。command line:变量VARIABLE在命令行中定义。override:变量VARIABLE在 Makefile 文件中定义并使用override指示符声明。automatic:变量VARIABLE是自动化变量。
(八)shell 函数
contents := $(shell cat foo)
files := $(shell echo *.c)
shell 函数用于执行 shell 命令,并将命令的输出结果作为函数的返回值。例如,$(shell cat foo) 会读取 foo 文件的内容,$(shell echo *.c) 会列出当前目录下所有 .c 文件。
(九)Make 日志以及控制函数
$(info text) # 打印日志
$(warning text) # 产生警告信息,但不会导致 make 退出
$(error text) # 产生致命错误,并提示 “text” 信息给用户,然后退出 make 的执行
$(info text):用于在 Makefile 执行过程中打印日志信息,方便调试和查看执行状态。$(warning text):会输出警告信息,但不会终止make的执行,常用于提示一些可能存在的问题。$(error text):会输出错误信息,并立即终止make的执行,通常用于在发现严重错误时停止编译过程。
一些八股模拟拷打Point,万一有点用呢


查看19道真题和解析