【嵌入式八股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:指定交叉编译工具链的前缀。
  • CCASLD:分别指定编译器、汇编器和链接器。
  • 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

-includeinclude 类似,但当包含过程中出现错误(如文件不存在)时,不会报错,而是继续执行 Makefile。例如:

-include optional.mk

六、MAKEFILES 环境变量

MAKE 会自动包含 MAKEFILES 环境变量中指定的值。可以通过设置该环境变量来指定额外的 Makefile 文件,例如:

export MAKEFILES="common.mk"
make

这样,make 在执行时会自动包含 common.mk 文件。

七、VPATHvpath

VPATH

VPATH 用于指定 Makefile 搜索文件的路径。当 Makefile 中需要的文件不在当前目录时,可以通过 VPATH 指定搜索路径,例如:

VPATH = src:include

这表示 make 会在 srcinclude 目录中搜索所需的文件。

vpath

vpathmake 的关键词,用于设置文件的搜索路径。与 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)

list1list2 中的元素一一对应连接起来,例如:

$(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:判断 arg1arg2 是否相等。
  • ifneq:判断 arg1arg2 是否不相等。
  • 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)

该函数用于获取变量的属性值,返回值有以下几种情况:

  1. undefined:变量 VARIABLE 没有被定义。
  2. default:变量 VARIABLE 是一个默认定义(内嵌变量),如 CCMAKERM 等变量。如果在 Makefile 中重新定义这些变量,函数返回值将相应发生变化。
  3. environment:变量 VARIABLE 是一个系统环境变量,并且 make 没有使用命令行选项 -e(Makefile 中不存在同名的变量定义,此变量没有被替代)。
  4. environment override:变量 VARIABLE 是一个系统环境变量,并且 make 使用了命令行选项 -e。Makefile 中存在一个同名的变量定义,使用 make -e 时环境变量值替代了文件中的变量定义。
  5. file:变量 VARIABLE 在某一个 Makefile 文件中定义。
  6. command line:变量 VARIABLE 在命令行中定义。
  7. override:变量 VARIABLE 在 Makefile 文件中定义并使用 override 指示符声明。
  8. 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,万一有点用呢

全部评论
mark收藏了
点赞 回复 分享
发布于 2025-03-09 15:24 黑龙江

相关推荐

03-01 19:30
已编辑
南京大学 Java
点赞 评论 收藏
分享
03-26 08:58
已编辑
门头沟学院 Java
ttl:&nbsp;3.19一面晚上过3.20二面3.23oc3.25offerbase:末9有一段中小厂实习一面面经:(总体时长一个小时二十分钟左右没什么八股,主要都是问项目和场景题1.实习(问了有四十分钟,感觉面试官很看重实习这一块,一直在拷打,问到后面我都要疯了,好在准备得比较充分1️⃣用的是什么中间件,有参与技术选型吗,实习的项目里为什么选这个RabbitMQ而不是kafka,为什么不用RocketMQ,为什么放弃异步,自己的项目里面使用的是kafka,那你觉得项目和实习的中间件选型有差异的原因是什么,他们之间的区别在哪里,底层的原因知道吗(高柱到这里已经快疯了,但是硬着头皮答完了,主要是从一致性吞吐量和框架的契合度答,面试官说答得挺好的,应该是没什么问题,这一块就问了快半个小时,到这里我已经快疯了2️⃣项目怎么对接上下游3️⃣介绍项目的难点重点4️⃣微服务(高柱实习是单体项目没涉及这一块5️⃣Redis的使用2.项目:1️⃣智能客服是怎么应用在项目里的(langchain4j➕rag➕functioncalling)2️⃣RAG了解多少3️⃣文本向量化的难点是什么,了解哪些大模型的知识(我一点不懂,纯瞎扯,但貌似扯对了4️⃣对ai的态度是什么,aicoding相关5️⃣怎么保证多节点下Caffeine缓存里面数据都是一致的(答的是短ttl,面试官不是很满意,但是我确实不太懂这个怎么保证,后来查了还是不懂怎么保证6️⃣Redis的使用,和你的实习项目的使用有区别吗,还有一些引申问题3.八股(含量不高,就是走个过场1️⃣进程的内存布局2️⃣Redis三剑客3️⃣微服务相关知识(高柱已经忘得差不多了…勉强答上来4️⃣JVM5️⃣线程状态6️⃣线程安全,在你的实习项目里怎么保证线程安全的(又绕回来了4.智商题找异常球5.手撕:1️⃣五道sql,不难2️⃣力扣不重叠的滑动窗口数组,贪心➕双指针秒了强度拉满了这个一面,高柱到后面人都是傻的二面面经:(就半个小时实习拷打,简历上写了几点就问了几点,问完就结束了,无手撕
查看19道真题和解析
点赞 评论 收藏
分享
评论
3
20
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务