1. make 和 makefile
(1) make是一个应用程序
* 解析源程序之间的依赖关系
* 根据依赖关系自动维护编译工作
* 执行宿主操作系统中的各种命令
(2)makefile是一个描述文件
* 定义一系列的规则来指定源文件编译的先后顺序
* 拥有特定的语法规则,支持函数定义和函数调用
* 能够直接集成操作系统中的各种命令
(3)make和makefile之间的关系
* makefile中的描述用于指导make程序如何完成工作
* make根据makefile中的规则执行命令,最后完成编译输出
(4)最简单的makefile实例
hello:
echo "hello makefile"
注:目标后的命令需要tab键(‘’\t‘’)隔开
(5)make程序使用示例
make -f mf.txt hello
* -f 指定makefile
* 默认情况下,以hello为目标,查找当前目录makefile或Makefile文件,并执行hello处的命令
2 . makefile结构
(1)makefile的基本结构
targets : prerequisites ; command1
command2
(2)依赖规则
* 当目标对应的文件不存在时,执行对应命令
* 当依赖在时间上比目标更新,执行对应命令
* 当依赖关系连续发生时,对比依赖链上的每一个目标
注:命令前加@,取消回显
3. 伪目标
(1)makefile中的伪目标
* 通过.PHONY关键字声明一个伪目标
* 伪目标不对应任何实际文件
* 不管伪目标的依赖是否更新,命令总是执行
.PHONY : clean
clean :
rm *.o hello.out
4. 变量和不同的赋值方式
(1)makefile中支持变量的概念,它只代表文本数据(字符串)
(2)变量的定义和使用
CC := gcc
TARGET := hello.out
$(TARGET) : func.o main.o
$(CC) -o $(TARGET) func.o main.o
(3)makefile中变量的赋值方式
① 简单赋值(:=): 只对当前语句的赋值有效(局部变量)
② 递归赋值(=): 所有与目标变量相关的其他变量都将收到影响(全局变量)
③ 条件赋值(?=): 如果变量未定义,使用赋值符号中的值定义变量;如果变量已定义赋值无效
④ 追加赋值(+=): 在原变量之后追加一个新值,原变量值与新值之间由空格隔开
5. 预定义变量的使用
(1)在makefile中存在一些预定义的变量
① 自动变量
* $@: 当前规则中触发命令被执行的目标
* $^: 当前规则中所有的依赖
* $<: 当前规则中的第一个依赖
② 特殊变量
* $(MAKE)
* $(MAKECMDGOALS)
* $(MAKEFILE_LIST)
* $(MAKE_VERSION)
* $(CURDIR): 当前make解释器的工作目录
* $(.VARIABLES)
6. 变量的高级主题
(1)变量值得替换
* 使用指定字符串替换变量值中的字符串
* 语法格式:$(var:a=b)
.PHONY : test
src := a.c b.c c.c
obj := $(src:c=o)
test :
echo $(obj)
(2)变量的模式替换
* 使用%保留变量值中的指定字符,替换其它字符
* 语法格式:$(var:a%b=x%y)
.PHONY : test
src := a1b.c a2b.c a3b.c
obj := $(src:a%b.c=x%y.o)
test :
echo $(obj)
(3)规则中的模式替换
targets : target-pattern : prereq-pattern
command1
command2
...
意义:通过target-pattern从targets中匹配子目标;再通过prereq-pattern从子目标生成依赖;进而构成完整的规则
OBJ := func.o main.o
$(OBJ) : %.o : %.c
gcc -o $@ -c $^
(4)变量值的嵌套引用:变量名中可以包含对其它变量的引用
(5)命令行变量:在运行make时,可以在命令行定义变量,默认覆盖makefile中定义的变量
注:override关键字用于指示makefile中定义的变量不能被命令行覆盖
(6)define关键字可以定义多行变量
(7)环境变量
① makefile中可以直接使用环境变量的值
* 如果定义了同名变量,环境变量将被覆盖
* 运行make时加“-e”选项,优先使用环境变量
② makefile中使用环境变量
* 优势:环境变量可以在所有makefile中使用
* 劣势:过多的依赖于环境变量会导致移植性降低
(8)变量在不同makefile之间的传递
① 直接在外部定义环境变量进行传递
② 使用export定义变量进行传递
③ 定义make命令行变量进行传递(推荐)
(9)目标变量(局部变量):作用域只在指定目标及连带规则中
target : name <assignment> value
target : override name <assignment> value
.PHONE : test
var := Kevin
test : var := Test
test :
echo $(var)
(10)模式变量:作用只在符合模式的目标及连带规则中
pattern: name <assignment> value
pattern: override name <assignment> value
.PHONE : test
var := Kevin
%t : var := Test
test :
echo $(var)
7. 条件判断语句
(1)makefile中支持条件判断语句
* 可以根据条件的值来决定make的执行
* 可以比较两个不同变量或者变量和常量值
(2)条件判断关键字
* ifeq
* ifneq
* ifdef
* ifndef
.PHONY : test
var1 =
var2 = $(var1)
test :
ifdef var1
echo "var1 is defined"
endif
ifdef var2
echo "var2 is defined"
endif
注1:条件判断语句只能用于控制make实际执行的语句,不能控制规则中命令的执行过程(只能用于控制命令是否执行)
注2:关键字前不能有“\t”
8. 函数的定义及调用
(1) makefile中支持函数的概念
* make解释器提供了一系列的函数供makefile调用
* 在makefile中支持自定义函数实现,并调用执行
* 通过define关键字实现自定义函数
(2)自定义函数
.PHONY : test
define func1
echo "func 1"
echo "argv[0] = $(0)"
endef
define func2
echo "func 2"
echo "argv[0] = $(0)"
endef
test :
@$(call func1, "Hello")
@$(call func2, "Hello")
(3)深入理解自定义函数
* 自定义函数是一个多行变量,无法直接调用
* 自定义函数是一种过程调用,没有任何的返回值
* 自定义函数用于定义命令集合,并应用于规则中
(4)make解释器中的预定义函数
* make的函数提供了处理文件名,变量和命令的函数
* 可以在需要的地方调用函数来处理指定的参数
* 函数在调用的地方被替换为处理结果
* 预定义函数的调用
var := $(func_name arg1, arg2,...)
var := $(abspath ./)
(5)自定义函数本质剖析
* makefile中不支持真正意义上的自定义函数
* 自定义函数的本质是多行变量
* 预定义的call函数在调用时将参数传递给多行变量
* 自定义函数是call函数的实参,并在call中被执行
(6)makefile常用预定义函数
① $(wildcard _pattern): 获取当前目录中满足_pattern的文件或目录列表
SRCS := $(wildcard *.c)
② $(addprefix _prefix, _names): 给名字列表_names中的每个名字添加前缀_prefix
③ $(addsuffix _suffix, _names): 给名字列表_names中的每个名字添加后缀_suffix
④ $(abspath _file): 获取文件或目录的绝对路径
(7)Makefile 中函数的调用格式如下:
$(function arguments)
* function 为函数名,arguments 为参数。
* 函数名与参数之间由空格或Tab分隔,如果有多个参数,这些参数之间由逗号分隔
(8)makefile综合实例:编译当前目录的所有.c文件,并将编译结果保存在当前目录的Build文件夹
MKDIR := mkdir
RM := rm -rf
TARGET := Main
BUILD_DIR := Build
#TARGET = Build/Main
TARGET := $(BUILD_DIR)/$(TARGET)
# DIR_OBJS = /SambaShare/TestM/
DIR_OBJS := $(abspath ./)
# DIR_OBJS = /SambaShare/TestM/Build
DIR_OBJS := $(DIR_OBJS)/$(BUILD_DIR)
# SRCS := Main.c Test.c
SRCS := $(wildcard *.c)
# OBJS := Main.o Test.o
OBJS := $(SRCS:.c=.o)
# OBJS = /SambaShare/TestM/Build/Main.o /SambaShare/TestM/Build/Test.o
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
.PHONY : rebuild clean all
$(TARGET) : $(BUILD_DIR) $(OBJS)
@echo "SRCS = $(SRCS)"
@echo "OBJS = $(OBJS)"
@echo "DIR_OBJS = $(DIR_OBJS)"
$(CC) -o $@ $(OBJS)
@echo "Target File ==> $@"
$(BUILD_DIR) :
$(MKDIR) $@
$(DIR_OBJS)/%.o : %.c
ifeq ($(DEBUG),true)
$(CC) -o $@ -g -c $^
else
$(CC) -o $@ -c $^
endif
rebuild : clean all
all : $(TARGET)
clean :
$(RM) $(BUILD_DIR)
9. 自动生成依赖关系
(1)Linux sed命令
* sed 可以用来在管道或命令序列中编辑数据,它是stream editor(流编辑器)的简称
* sed 一般用法:
sed commond file
注:如果file文件没有指定,则使用标准输入
* sed 会将指定的命令应用在输入的每一行,并将结果写入到标准输出
* sed 字符串替换: sed ‘s:src:dst:g’
echo "test=>abc+abc=abc" | sed 's:abc:xyz:g'
* sed 正则表达式支持:
sed 's,\(.*\)\.o[ :]*,objs/\1.o : ,g'
Main.o: Main.c Test.h
objs/Main.o: Main.c Test.h
(2)gcc生成依赖关系的编译选项
* 获取目标的完整依赖关系
gcc -M Test.c
* 获取目标的部分依赖关系
gcc -MM Test.c
(3)Makefile中可以把目标的完整依赖拆分为多个部分依赖
.PHONY a b c
Test : a b c
echo "$^"
.PHONY a b c
Test : a b
Test : b c
Test :
echo "$^"
(4)Makefile中的include关键字
① include关键字将其他文件原封不动的搬入当前文件
② make对include关键字的处理方式
* 搜索成功:将文件内容搬入当前Makefile中
*搜索失败:
* 以文件名为目标查找并执行规则
* 当文件名对应的规则不存在时,最终产生警告
.PHONY : all
include test.txt
all:
@echo "this is $@"
text.txt :
@touch test.txt
(5)Makefile中命令的执行机制
* 规则中的每条命令默认是在一个新的进程中执行(Shell)(注:每条命令都会创建一个进程)
* 通过接续符(;)可将多条命令组合成一个命令
* 组合的命令依次在同一进程中被执行
* set -e可指定发生错误后立即退出执行(默认情况下不会退出执行)
.PHONY : all
all :
set -e; \
mkdir test; \
cd test; \
mkdir subtest
(6)自动生成依赖关系解决方案的初步思路
① 通过gcc -MM和sed的到.dep依赖文件(目标的部分依赖)
② 通过include指令包含所有的.dep依赖文件
.PHONY : all clean
MKDIR := mkdir
RM := rm -fr
CC := gcc
DIR_DEPS := deps
SRCS := $(wildcard *.c)
DEPS := $(SRCS:.c=.dep)
DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))
all :
@echo "all"
ifeq ("$(MAKECMDGOALS)", "all")
-include $(DEPS)
endif
ifeq ("$(MAKECMDGOALS)", "")
-include $(DEPS)
endif
$(DIR_DEPS) :
$(MKDIR) $@
ifeq ("$(wildcard $(DIR_DEPS))", "")
$(DIR_DEPS)/%.dep : $(DIR_DEPS) %.c
else
$(DIR_DEPS)/%.dep : %.c
endif
@echo "Creating $@ ..."
@set -e; \
$(CC) -MM -E $(filter %.c, $^) | sed 's,\(.*\)\.o[ :]*,objs/\1.o : ,g' > $@
clean :
$(RM) $(DIR_DEPS)
注:Makefile中"-"的作用是表示在操作失败时不报错
(7)关于include总结
① 当目标文件不存在:以文件名查找规则,并执行;如果在规则中创建了目标文件,则将创建成功的目标文件包含进Makefile
② 当目标文件存在
* 将目标文件包含进当前的Makefile
* 以目标文件名查找是否有相应规则
*YES:比较规则的依赖关系,决定是否执行规则的命令
*NO:NULL(无操作)
注:如果规则中有依赖更新,可能会触发重建目标文件,make会重新包含目标文件,替换掉之前的目标文件
(8)自动生成依赖关系示例代码:自动编译当前目录的代码
PHONY : all clean rebuild
MKDIR := mkdir
RM := rm -fr
CC := gcc
DIR_DEPS := deps
DIR_EXES := exes
DIR_OBJS := objs
DIRS := $(DIR_DEPS) $(DIR_EXES) $(DIR_OBJS)
EXE := app.out
EXE := $(addprefix $(DIR_EXES)/, $(EXE))
SRCS := $(wildcard *.c)
OBJS := $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
DEPS := $(SRCS:.c=.dep)
DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))
all : $(DIR_OBJS) $(DIR_EXES) $(EXE)
ifeq ("$(MAKECMDGOALS)", "all")
-include $(DEPS)
endif
ifeq ("$(MAKECMDGOALS)", "")
-include $(DEPS)
endif
$(EXE) : $(OBJS)
$(CC) -o $@ $^
@echo "Success! Target => $@"
$(DIR_OBJS)/%.o : %.c
$(CC) -o $@ -c $(filter %.c, $^)
$(DIRS) :
$(MKDIR) $@
ifeq ("$(wildcard $(DIR_DEPS))", "")
$(DIR_DEPS)/%.dep : $(DIR_DEPS) %.c
else
$(DIR_DEPS)/%.dep : %.c
endif
@echo "Creating $@ ..."
@set -e; \
$(CC) -MM -E $(filter %.c, $^) | sed 's,\(.*\)\.o[ :]*,objs/\1.o $@ : ,g' > $@
clean :
$(RM) $(DIRS)
rebuild :
@$(MAKE) clean
@$(MAKE) all
注1:以下代码是为了避免一些.deps文件会被重复创建多次
ifeq ("$(wildcard $(DIR_DEPS))", "")
$(DIR_DEPS)/%.dep : $(DIR_DEPS) %.c
else
$(DIR_DEPS)/%.dep : %.c
endif
注2:将依赖文件名作为目标加入自动生成的依赖关系中,是为了在仅修改头文件时,仅编译包含头文件的模块
(9)自动生成依赖关系小结:
* makefile 中可以将目标的依赖拆分写到不同的地方
* include 关键字能够触发相应规则的执行
* 如果规则的执行导致依赖更新,可能导致再次解释执行相应的规则
* 依赖文件也需要依赖于源文件得到正确的编译决策
* 自动生成文件间的依赖关系能够提高Makefile的移植性
10. make的隐式规则
(1)Makefile中出现同名目标时
* 依赖:所有依赖将合并在一起,成为目标的最终依赖
* 命令:所有之前定义的命令被最后定义的命令取代,并发出警告
注:当使用include关键字包含其它文件时,需要确保被包含文件中的同名目标只有依赖,没有命令,否则,同名目标的命令将被覆盖
(2)make的隐式规则(built-in rule):make提供了一些常用的,例行的规则实现,当相应目标的规则未提供时,make尝试使用隐式规则
SRCS := $(wildcard *.c)
OBJS := $(SRCS:.c=.o)
CC := gcc
all :
@echo "$(.VARIABLES)"
app.out : $(OBJS)
$(CC) -o $@ $^
$(RM) $^
@echo "Target ==> $@"
#%.o : %.c
# @echo "my rule"
# $(CC) -c -o $@ $^
(3)隐式规则初探
* make提供了生成目标文件的隐式规则
* 隐式规则会使用预定义变量完成编译工作
* 改变预定义变量将部分改变隐式规则的行为
* 当存在自定义规则时,不再使用隐式规则
(4)隐式规则深入理解
* 当make发现目标的依赖不存在时
* 尝试通过依赖名逐一查找隐式规则
* 并通过依赖名推导可能需要的源文件
(5)隐式规则副作用
* 编译行为难以控制:大量使用隐式规则可能产生意想不到的编译行为
* 编译效率低下: make从隐式规则和自定义规则中选择最终使用的规则
(6)隐式规则链:当依赖的目标不存在时,make会极力组合各种隐式规则对目标进行创建,进而产生意料之外的编译行为
(7)查看隐式规则:make -p (make -p | grep "%.o")
(8)禁用隐式规则:make -r
注:在Makefile中自定义规则和自定义模式,属于局部禁用隐式规则(会覆盖隐式规则)
(9)后缀规则(旧式的模式规则)
.c.o <==> %.o : %.c
.c <==> %:%.c
11. make中的路径搜索
(1)特殊的预定义变量VPATH(全大写)
* VPATH 变量的值用于指示make如何查找文件
* 不同文件夹可作为VPATH的值同时出现
* 文件夹的名字之间需要使用分隔符(空格" " 冒号 ":" 分号";")隔开
VPATH := inc src
VPATH := inc:src
VPATH := inc;src
(2)make对于VPATH值的处理方式
* 当前文件夹找不到需要的文件时,VPATH会被使用
* make会在VPATH指定的文件夹中依次搜索文件
* 当多个文件夹存在同名文件时,选择第一次搜索到的文件
注:VPATH 是make的搜索路径,不要与命令的搜索路径搞混,如:gcc -I include-path
(3)VPATH存在的问题:当Inc文件夹中意外出现源文件(c/cpp),可能产生编译错误
(4)vpath关键字
* 为不同类型的文件指定不同的搜索路径
* 语法: vpath pattern directory
vpath %h inc
vpath %c src
(5)取消搜索规则
* 取消已经设着的某个规则:vpath patter
* 取消所有已经设置的规则:vpath
(6)当VPATH和vpath同时出现时,make搜索顺序:当前目录 ====> vpath指定目录 ====> VPATH指定目录
注:一旦搜索成功,make将停止搜索,用第一个搜索到的文件
(7)当使用vpath对同一个Pattern指定多个文件夹时:先搜索当前文件夹,然后自上而下的顺序搜索vpath指定的文件夹
(8)VPATH和GPATH的区别:当目标文件在VPATH或GPATH指定的文件夹时,依赖更新,VPATH会在当前文件夹创建目标,GPATH会在指定文件夹创建目标
(9)小规模项目可以让源文件依赖于头文件(易于维护)
.PHONY : all clean
DIR_BUILD := build
DIR_SRC := src
DIR_INC := inc
TYPE_INC := .h
TYPE_SRC := .c
TYPE_OBJ := .o
CC := gcc
LFLAGS :=
CFLAGS := -I $(DIR_INC)
ifeq ($(DEBUG),true)
CFLAGS += -g
endif
MKDIR := mkdir
RM := rm -fr
APP := $(DIR_BUILD)/app.out
HDRS := $(wildcard $(DIR_INC)/*$(TYPE_INC))
HDRS := $(notdir $(HDRS))
OBJS := $(wildcard $(DIR_SRC)/*$(TYPE_SRC))
OBJS := $(OBJS:$(TYPE_SRC)=$(TYPE_OBJ))
OBJS := $(patsubst $(DIR_SRC)/%, $(DIR_BUILD)/%, $(OBJS))
vpath %$(TYPE_INC) $(DIR_INC)
vpath %$(TYPE_SRC) $(DIR_SRC)
all : $(DIR_BUILD) $(APP)
@echo "Target File ==> $(APP)"
echo $(HDRS)
echo $(OBJS)
$(DIR_BUILD) :
$(MKDIR) $@
$(APP) : $(OBJS)
$(CC) $(LFLAGS) -o $@ $^
$(DIR_BUILD)/%$(TYPE_OBJ) : %$(TYPE_SRC) $(HDRS)
$(CC) $(CFLAGS) -o $@ -c $<
clean :
$(RM) $(DIR_BUILD)
注:小型项目目录结构
Project(dir)
* inc(dir)
* src(dir)
build(dir)
* Makefile
12. 打造专业的编译环境
(1)目录结构(在project目录生存build目录,保存依赖关系文件和目标文件)
Project
→ Common → inc
→ Module ------------------ → src
→ Main → Makefile
→ Makefile
(2)示例代码
附录A:Makefile常用函数
1. 文本处理函数:
① $(subs from, to, text)
* 函数功能:把字符串text中的from替换为to(所有的都会替换)
* 示例
text := Hello.c Main.c Test.c
text2 := $(subst .c,.o,$(text))
output: Hello.o Main.o Test.o
注:和变量值的替换效果一致 $(var:a=b)
② $(patsubst pattern,replacement,text)
* 函数功能: 把字符串text中符合模式pattern的替换为replacement
* 示例
$(patsubst %.c,%.o,Test.c Main.c)
output: Test.o Main.o
注:和变量的模式替换效果一致$(var:a%b=x%y)
③ $(strip string)
* 函数功能:把多个空格合并为一个(无论头部、中间、尾部)
* 示例
text := " Hello.c Main.c Test.c "
text1 := $(strip $(text))
output: " Hello.c Main.c Test.c "
注:头部和尾部都是有一个空格
④ $(findstring find,in)
* 函数功能:在字符串in中搜索find,成功返回find字符串,否则返回空
* 示例
text := "Hello.c Main.c Test.c"
text1 := $(findstring Hello,$(text))
output: Hello
⑤ $(filter pattern…,text)
* 函数功能:返回text中符合pattern的字符串
* 示例
text := Hello.c Main.c Test.c Hello.o Main.o crt0.S
text1 := $(filter %.c %.S,$(text))
output: Hello.c Main.c Test.c crt0.S
注:如果text加“”,过滤返回的值会少半个双引号
⑥ $(filter-out pattern…,text)
* 函数功能:返回text中pattern匹配之外(不符合pattern)的字符串
* 示例
text := Hello.c Main.c Test.c Hello.o Main.o crt0.S
text1 := $(filter-out %.c %.S,$(text))
output:Hello.o Main.o
⑦ $(sort list)
* 函数功能:将list中的单词排序(升序,字母从前到后)
* 示例
text := Hello.c Main.c Test.c Hello.o Main.o crt0.S
text1 := $(sort $(text))
output: Hello.c Hello.o Main.c Main.o Test.c crt0.S
⑧ $(word n,text)
* 函数功能:返回text中第n个单词
* 示例
text := Hello.c Main.c Test.c Hello.o Main.o crt0.S
text1 := $(word 3,$(text))
output: Test.c
⑨ $(wordlist s,e,text)
* 函数功能:返回text中s到e的字符串(s和e为字符串中单词的索引,从1开始)
* 示例
text := Hello.c Main.c Test.c Hello.o Main.o crt0.S
text1 := $(wordlist 2,3,$(text))
output: Main.c Test.c
注:字符串中单词的索引可以从1到无穷大(很大得数)
⑩ $(words text)
* 函数功能:返回text中单词的数量
* 示例
text := Hello.c Main.c Test.c Hello.o Main.o crt0.S
text1 := $(words 2,8,$(text))
output: 6
⑪ $(firstword names...)
* 函数功能: 返回第一个单词
⑫ $(lastword names...)
* 函数功能: 返回最后一个单词
2. 文件名处理函数:
① $(dir names…)
* 函数功能:返回文件名列表names的目录部分(目录部分:文件名中最后一个‘/’以及之前的部分,包含最后一个'/')
* 示例
text := /SambaShare/TestMakefile9/Makefile /SambaShare/TestMakefile9/Test.c
text1 := $(dir $(text))
output: /SambaShare/TestMakefile9/ /SambaShare/TestMakefile9/
② $(notdir names…)
* 函数功能:返回文件名列表names的非目录部分
* 示例
text := /SambaShare/TestMakefile9/Makefile /SambaShare/TestMakefile9/Test.c
text1 := $(notdir $(text))
output: Makefile Test.c
③ $(suffix names...)
* 函数功能:返回文件名列表names中各个文件的后缀名(如果没有则返回空)
* 示例
text := /SambaShare/TestMakefile9/Makefile /SambaShare/TestMakefile9/Test.c /SambaShare/TestMakefile9/crt0.S
text1 := $(suffix $(text))
output: .c .S
④ $(basename names…)
* 函数功能:返回各个文件的不带后缀的文件名
* 示例
text := /SambaShare/TestMakefile9/Makefile /SambaShare/TestMakefile9/Test.c /SambaShare/TestMakefile9/crt0.S
text1 := $(suffix $(text))
output: /SambaShare/TestMakefile9/Makefile /SambaShare/TestMakefile9/Test /SambaShare/TestMakefile9/crt0
⑤ $(addsuffix suffix,names...)
* 函数功能:为文件名列表names的各个文件名添加后缀
* 示例
$(addsuffix .c,foo bar)
output:foo.c bar.c
⑥ $(addprefix prefix,names...)
* 函数功能:为文件名列表names的各个文件名添加前缀
* 示例
$(addprefix Build/,foo.o bar.o)
output:Build/foo.o Build/bar.o
⑦ $(join list1,list2)
* 函数功能:将list1中和list2中的单词、索引序号相同的连接在一起
* 示例
$(join test1 test2 test3 test4,.c .o .s)
output: test1.c test2.o test3.s test4
⑧ $(wildcard pattern)
* 函数功能:搜索当前目录,返回符合pattern的文件名列表
* 示例
$(wildcard *.c)
output: Main.c Test.c
⑨ $(realpath names...)
* 函数功能:返回文件的绝对路径,如果不存在则返回空(注:绝对路径不包含任何.或者..成分,没有任何重复的路径分隔符(/)或符号链接)
* 示例
text1 := $(realpath Main.c Test.c)
output: /SambaShare/TestMakefile9/Main.c /SambaShare/TestMakefile9/Test.c
注:只要有一个不存在,就会返回空
⑩ $(abspath names...)
* 函数功能:返回文件的绝对路径,如果不存在则返回当前路径
* 示例
text1 := $(abspath Main.c test.c)
output: /SambaShare/TestMakefile9/Main.c /SambaShare/TestMakefile9/test.c
注:如果当前文件不存在,也会返回绝对路径(以当前目录的绝对补齐)(上面例子中test.c文件并不存在)
3. 条件语句函数
① $(if condition,then-part[,else-part])
* 函数功能:如果condition为真,返回then-part,否则返回else-part
* 示例
text1 := $(if $(Test),Test then part,Test else part)
output: Test else part
② $(or condition1[,condition2[,condition3...]])
* 函数功能:当有不为空的条件存在,则返回第一个不为空的条件,如果都为空则返回空
* 示例
Test2 := tt2
text1 := $(or $(Test),$(Test1),$(Test2))
output : tt2
③ $(and condition1[,condition2[,condition3...]])
* 函数功能:如果有一个条件为空,则返回空,如果都不为空,则返回最后一个条件值
* 示例
Test1 := tt1
Test2 := "tt2"
Test3 := "tt3"
text1 := $(and $(Test1),$(Test2),$(Test3))
output: tt3
4. 其他函数:
① $(foreach var,list,text)
* 函数功能:把list中的单词依次放入var,执行text语句
* 示例
dirs := a b c d
text1 := $(foreach var,$(dirs),$(wildcard $(var)/*))
output:a/TestA.c b/TestB.c c/TestC.c d/TestD.c
text1 := $(wildcard a/* b/* c/* d/*)和例子中的语句等效
② $(file op filename[,text])
* 函数功能:文件写入/读出数据
* 示例
.PHONY: Test
Test:
$(file > text.txt,Hello)
@echo "$(file < text.txt)"
output: Hello
注:>是从头覆盖写入,>>是追加写入
③ $(call variable,param,param,...)
* 函数功能:用于自定义函数的调用
* 示例
.PHONY: Test
define TestCall
@echo "$(1)"
@echo "$(2)"
endef
Test:
$(call TestCall,Parm1,Parm2)
@echo "Test Done"
④ $(value variable)
* 函数功能:不对变量variable进行任何展开操作,直接返回变量variable的值(直接返回变量的文本值)
* 示例
P := E
FOO = $PATH
target:
@echo FOO=$(FOO)
@echo $(value FOO)
output:
FOO=EATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/SambaShare/gcc-arm-none-eabi-10.3-2021.07/bin
⑤ $(eval ariables/targets/rules)
* 函数功能:对参数进行展开,展开的结果作为Makefile的一部分,make可以对展开内容进行语法解析。展开的结果可以包含一个新变量、目标、隐含规则或者是明确规则等
* 示例
.PHONY : target
pointer := pointed_value
define foo
var := 123
arg := $1
$$($1) := ooooo
endef
#$(info $(call foo,pointer))
$(eval $(call foo,pointer))
target:
@echo -----------------------------
@echo var: $(var), arg: $(arg)
@echo pointer: $(pointer), pointed_value: $(pointed_value)
@echo done.
@echo -----------------------------
注:“eval”函数执行时会对它的参数进行两次展开。第一次展开过程发是由函数本身完成的,第二次是函数展开后的结果被作为Makefile内容时由make解析时展开的。
⑥ $(origin variable)
* 函数功能:返回变量从哪里来
* 示例
FOO = $PATH
text1 := $(origin FOO)
text2 := $(origin PATH)
target:
@echo $(text1)
@echo $(text2)
output:
file
environment
注:origin函数的返回值
* environment
* environment override
* file
* command line
* override
* automatic
⑦ $(flavor variable)
* 函数功能:返回变量的类型
* 示例
FOO = $PATH
FOO1 := Test
text1 := $(flavor FOO)
text2 := $(flavor FOO1)
target:
@echo $(text1)
@echo $(text2)
output:
recursive
simple
注:变量的类型
* undefined
* recursive
* simple
⑧ $(error text...)
* 函数功能:打印text信息并停止编译
* 示例
ERR = $(error found an error!)
.PHONY: err
err: ; $(ERR)
output:Makefile:5: *** found an error!. Stop.
⑨ $(warning text...)
* 函数功能:打印警告信息,不会停止编译
* 示例
⑩ $(info text...)
* 函数功能:打印text
* 示例
⑪ $(shell command)
* 函数功能:执行一条命令,并把输出返回
* 示例
⑫ $(guile)
附1:一个项目级的makefile示例
https://github.com/Kevin-l-wu/Mini2440Test
附2:GNU make 4.3 官方手册
链接:https://pan.baidu.com/s/1bPtqMp5gr3yv4Tg5qs7bHA
提取码:1cfk
附3:ChibiOS github路径(该项目为一个实时操作系统,它的makefile简洁且小巧)
https://github.com/ChibiOS/ChibiOS