1 三要素:
- 依赖:判断依赖是否存在,若存在则判断跟目标的时间戳关系
- 目标:判断目标是否存在,若存在则退出;否则执行动作
- 动作:shell语句,行首为Tab
image:a.o b.o c.o
gcc a.o b.o c.o -o image
a.o:a.c
gcc a.c -o a.o -c
b.o:b.c
gcc b.c -o b.o -c
c.o:c.c
gcc c.c -o c.o -c
运行结果:
make
gcc a.c -o a.o -c
gcc b.c -o b.o -c
gcc c.c -o c.o -c
gcc a.o b.o c.o -o image
@:不打印命令本身
1 image:a.o b.o c.o
2 gcc a.o b.o c.o -o image
3
4 a.o:a.c
5 @gcc a.c -o a.o -c
6 b.o:b.c
7 @gcc b.c -o b.o -c
8 c.o:c.c
9 @gcc c.c -o c.o -c
//输出:
gcc a.o b.o c.o -o image
2 变量
- 格式:$(变量名)
- 变量和函数的展开实在make读取Makefile时进行。
- 变量名大小写敏感,建议一般变量使用小写方式,参数列表采用大写方式
2.1 自定义变量&系统预定义变量
OBJ = a.o b.o c.o
image:a.o b.o c.o
$(CC) $(OBJ) -o image
a.o:a.c
@$(CC) a.c -o a.o -c
b.o:b.c
@$(CC) b.c -o b.o -c
c.o:c.c
@$(CC) c.c -o c.o -c
$(CC)默认为:CC
//输出
cc a.o b.o c.o -o image
在不同平台重新给预定义变量赋值一次即可,如在ARM平台重新赋值,此时代表交叉编译工具链
OBJ = a.o b.o c.o
CC = arm-Linux-gun-gcc
image:a.o b.o c.o
$(CC) $(OBJ) -o image
2.2 Makefile预定义变量
- RM:删除命令,默认为:rm -f
- CC:c编译程序,默认为:cc
- CPP:c程序的预处理器,默认为:-E
- CXX:c++编译程序,默认为:g++
- AS:汇编程序,默认为:as
- AR:函数库打包程序,可创建静态库 .a文档,默认为:ar
- ARFLAGS:执行AR命令的命令行参数,默认为:rv
- CFLAGS:CC编译器的命令行参数
- CXXFLAGS:g++编译器的命令行参数
- ASFLAGS:汇编器AS的命令行参数
2.3 自动化变量
- @:所在规则的目标的完整名称
@D:目标文件的目录部分
@F:目标文件的文件名
OBJ = a.o b.o c.o
output/image:a.o b.o c.o
echo $(@) $(@D) $(@F)
//输出
output/image output image
- <:所在规则的依赖列表的第一个文件的完整名称
<D:第一个依赖文件的目录部分
<F:第一个依赖文件的文件名
output/image:a.o b.o c.o
echo $(<) $(<D) $(<F)
//输出
a.o . a.o
^:所在规则的依赖列表,去除重复的依赖文件
^D:所有依赖列表的目录部分
^F:所有依赖列表的文件名部分+:所在规则的依赖列表,同一文件可重复
+D:所有依赖列表的目录部分
+F:所有依赖列表的文件名部分?:被更新的依赖文件列表,用空格隔开
?F:被更新的依赖文件的目录部分
?D:被更新的依赖文件的文件名部分*:在模式规则和静态规则中,代表茎
%:代表所在规则的静态库文件的一个成员名
用自动化变量代表依赖库和目标
OBJ = a.o b.o c.o
output/image:$(OBJ)
$(CC) $(^) -o $(@)
a.o:a.c
@$(CC) $(^) -o $(@)
b.o:b.c
@$(CC) $(^) -o $(@)
c.o:c.c
@$(CC) $(^) -o $(@)
2.4 变量的定义
-
递归定义
A = Here is $(B)
B = China
在整个文件搜索变量B
-
直接定义
B = China
A := Here is $(B)
在语句之前搜索变量B,若搜不到则返回空
-
条件定义
A = apple
A ?= banana
如果A已定义则不做操作;
如果A没有定义,则定义为“banana”。
-
多行命令定义
define commands
echo "ok!"
echo "done!"
enddef
-
追加变量
A = apple
A += tree
变量A的值为:apple tree
-
替换变量的值
A = a.c b.c c.c
B = $(A:%.c=%.o)
变量B的值为,将变量A中所有以".c"结尾的单词替换为 “.o”结尾
内嵌函数patsubst也可实现该功能:
B = $(patsubst %.c, %.o, $(A))
-
override变量
在执行make命令时,可以携带一个变量的定义。
如果这个变量跟Makefile中的变量重名,则覆盖Makefile中变量的定义。
如果Makefile中无此变量,则追加该变量的定义。
override CFLAGS += -Wall
OBJ = a.o b.o c.o
image:$(OBJ)
$(CC) $(^) -o $(@) $(CFLAGS)
a.o:a.c
@$(CC) $(^) -o $(@) -c
b.o:b.c
@$(CC) $(^) -o $(@) -c
c.o:c.c
@$(CC) $(^) -o $(@) -c
//输出
//make
cc a.o b.o c.o -o image -Wall
//make CFLAGS="-g"
cc a.o b.o c.o -o image -g -Wall
-
传递变量
将变量传递给子Makefile
//cat Makefile
export A = apple
B = banana
all:
echo "$A"
echo "$B"
$(MAKE) -C src/ #调用 src/目录下的Makefile
子Makefile:
//cat src/Makefile
image:
echo "src/: $A"
echo "src/: $B"
输出:
echo "apple"
apple
echo "banana"
banana
make -C src/
make[1]: Entering directory '...projectA/src'
echo "src/: apple"
src/: apple
echo "src/: "
src/:
make[1]: Leaving directory '...projectA/src'
-
默认传递的变量:
SHELL
MAKEFLAGS
MAKEFILES
执行make前就已存在的环境变量
对于默认传递的变量可以用unexport阻止传递给子Makefile
-
VPATH
VPATH = 路径1:路径2
VPATH = 路径1 路径2
指定文件搜索时,除当前路径之外的备用路径,以空格或者冒号隔开
VPATH = src1:src2
小写的指示符:vpath 可以为不同类型的文件指定不同的路径
vpath %.c = src1:src2
vpath %.h = include/
VPATH是一个变量,而vpath是一个指示符。
-
MAKE
调用子Makefile:
$(MAKE) -C subdir/
-
MAKEFLAGS
执行make时的命令行参数,这个变量默认传递给子Makefile。
3 规则
3.1 隐式规则
make可以自动找到依赖文件所需要的源程序文件,并自动编译。
但是只能自动找到与目标同名的依赖文件。
OBJ = a.o b.o c.o
image:$(OBJ)
$(CC) $(^) -o $(@)
//输出
cc -c -o a.o a.c
cc -c -o b.o b.c
cc -c -o c.o c.c
cc a.o b.o c.o -o image
若存在文件clean.c
OBJ = a.o b.o c.o
image:$(OBJ)
$(CC) $(^) -o $(@)
clean:
//make clean
cc clean.c -o clean
为区分动作的代号和要生成的文件,用指示符 .PHONY 来定义伪目标,即不运用隐式规则。
OBJ = a.o b.o c.o
image:$(OBJ)
$(CC) $(^) -o $(@)
clean:
.PHONY: clean
//make clean
make: Nothing to be done for 'clean'.
3.2 静态规则
编译.o文件时,需要加特定的编译选项。
OBJ = a.o b.o c.o
image:$(OBJ)
$(CC) $(^) -o $(@)
$(OBJ):%.o:%.c
$(CC) $(^) -o $(@) -Wall -c
clean:
$(RM) $(OBJ)
.PHONY: clean
//make
cc a.c -o a.o -Wall -c
cc b.c -o b.o -Wall -c
cc c.c -o c.o -Wall -c
cc a.o b.o c.o -o image
$(OBJ):%o:%.c
在(a.o、b.o、c.o)中匹配“.o”为后缀的文件,冒号后面的内容是目标对应的依赖。
3.3 多目标规则
- 可描述文件的依赖关系
如:OBJS中的.o文件都依赖于head.h文件,而又各自依赖其他的头文件
OBJS = a.o b.o c.o
a.o:a.h
b.o:b.h B.h
c.o:c.h
$(OBJS):head.h
- 有多个具有类似构建命令的目标(也可用静态规则)
SRC = a.c b.c x.c y.c
OBJS = $(SRC:.c=.o)
image:$(OBJS)
$(CC) $(OBJS) -o image
$(OBJS):$(SRC)
$(CC) $(subst .o,.c,$@) -o $@ -c
以a.o为例,多目标规则将构建如下对a.o的规则:
a.o:a.c b.c x.c y.c
$(CC) a.c -o a.o -c
意味着所有的目标依赖相同的文件
3.4 双冒号规则
用于同一个文件作为多个规则的目标。
在Makefile中,一个目标可以出现在多个规则中,但是这些规则必须是同一种规则,要么都是双冒号规则,要么都是普通规则。
普通规则会将所有依赖合并到一个目标文件。
双冒号规则的作用:
- 当依赖列表为空,即使目标文件已存在,规则中的shell命令无条件执行
- 根据依赖文件修改的情况,执行对应的命令
image::a.c
$(CC) a.c -o $@ -L. -lx
image::b.c
$(CC) b.c -o $@ -L. -ly
同一个目标出现在多个双冒号规则中,执行顺序按照书写顺序执行;
当a.c更新时,按照a.c文件重建目标image;
当b.c更新时,按照b.c文件重建目标image。
4 条件判断
OBJ = a.o b.o x.o y.o
#判断变量TOOLCHAIN是否有定义
ifdef TOOLCHAIN
CC= $(TOOLCHAIN)
else
CC = gcc
endif
image:$(OBJ)
#判断CC的值是否等于gcc,ifeq与后面的括号之间有空格
ifeq ($(CC), gcc)
$(CC) $(OBJ) -o image -lgcc
else
$(CC) $(OBJ) -o image
endif
...
执行:
make TOOLCHAIN=arm-none-linux-gnueabi-gcc
5 函数
函数格式:
$(function arg1,arg2,arg3,...)
5.1 wildcard
找到参数匹配的文件名
SRC=$(wildcard *.c) #找到所有.c文件
5.2 文本处理函数
5.2.1 subst FROM,TO,TEXT
将字符串TEXT中的字符FROM替换为TO
5.2.2 patsubst PATTERN,REPLACEMENT,TEXT
按照PATTERN搜索TEXT中所有以空格隔开的单词,并将它们替换为REPLACEMENT
A = $(patsubst %.c,%.o,a.c b.c)
A的值为:a.o b.o
5.2.3 strip STRING
去掉字符串中开头和结尾的多余的空白符(空格、制表符),并将其中连续的多个空白符合并为一个。
5.2.4 findstring FIND,STRING
在字符串STRING中查找FIND子串
找到返回FIND,否则返回空
5.2.5 filter PATTERN,TEXT
过滤TEXT中不符合给定模式PATTERN的单词
A = a.c b.o c.s d.txt
B = $(filter %.c %.o,$(A))
B的值为:a.c b.o
5.2.6 filter-out PATTERN,TEXT
过滤TEXT中符合PATTERN模式的单词,与filter相反
5.2.7 sort LIST
将字符串LIST中的单词按字母升序的顺序排序,去掉重复的单词
5.2.8 word N,TEXT
取TEXT中的第N个单词,N必须为正整数
5.2.9 wordlist START,END,TEXT
取TEXT中介于START和END之间的子串;
START,END均为正整数
START大于TEXT中的单词总数或者START大于END时返回空
5.2.10 words TEXT
计算TEXT中的单词数
5.2.11 firstword TEXT
取字符串TEXT中的第一个单词;
相当于$(word 1 TEXT)
5.3 文件名的处理函数
5.3.1 dir NAMES
取文件列表NAMES中的每一个路径的目录部分
5.3.2 notdir NAMES
取文件列表NAMES中的每一个路径的文件名部分
5.3.3 suffix NAMES
取文件列表NAMES中的每一个路径文件的后缀部分;
指最后一个"."及后面的子串
5.3.4 basename NAMES
取文件列表NAMES中的每一个路径文件的前缀部分;
指最后一个"."前面的子串
5.3.5 addsuffix SUFFIX,NAMES
为文件列表NAMES中每一个路径的文件名添加后缀SUFFIX
5.3.6 addprefix PREFIX,NAMES
为文件列表NAMES中每一个路径的文件名添加前缀PREFFIX
5.3.7 wildcard PATTERN
获取满足匹配模式PATTERN的文件名
5.3.8 foreach VAR,LIST,TEXT
将LIST中以空格分割的单词依次取出赋值给VAR,然后执行TEXT表达式,重复直到LIST的最后一个单词
DIR = dir1 dir2
FILES = $(foreach dir,$(DIR),$(wildcard $(dir)/*))
all:
echo $(FILES)
依次执行:
$(wildcard dir1/*)
$(wildcard dir2/*)
5.3.9 if CONDITION,THEN-PART[,ELSE-PART]
5.3.10 call VAR,ARGS,...
将ARGS一一替换VAR中的(2)...
A = hello, $(1) $(2)
B = $(call A )
5.3.11 origin VAR
查看参数VAR的出处
5.3.12 Shell COMMANDS
执行COMMANDS命令
6 实例
参考:
https://blog.csdn.net/xukai871105/article/details/37083675
6.1 单文件编译
#可执行文件
TARGET = test
#c文件
SRCS = main.c
#依赖目标
OBJS = $(SRCS:.c=.o)
#指令编译器和选项
CC=gcc
CFLAGS = -Wall
$(TARGET):$(OBJS)
# @echo TARGET:$@
# @echo OBJECTS:$^
$(CC) -o $@ $^
clean:
$(RM) $(TARGET) $(OBJS)
%.o:%.c
$(CC) $(CFLAGS) -o $@ -c $
//执行:
make
gcc -Wall -c -o main.o main.c
gcc -o test main.o
使用@echo指令:
make
gcc -Wall -c -o main.o main.c
6.2 多文件编译
#可执行文件
TARGET = test
#c文件
SRCS = a.c b.c c.c d.c
#依赖目标
OBJS = $(SRCS:.c=.o)
#指令编译器和选项
CC=gcc
CFLAGS = -Wall
$(TARGET):$(OBJS)
# @echo TARGET:$@
# @echo OBJECTS:$^
$(CC) -o $@ $^
%.o:%.c
$(CC) $(CFLAGS) -o $@ -c $<
clean:
$(RM) $(TARGET) $(OBJS)
6.3 生成自定义库
# 指令编译器和选项
CC = gcc
CFLAGS = -Wall
# 目标文件
#需以lib开头, .so结尾
#在使用该共享库时系统会自动的去除lib和.so
#使用时写成-ltest
TARGET = libtest.so
# C文件
SRCS = test_func.c
# 目标文件
OBJS = $(SRCS:.c=.o)
# 链接为可执行文件
#shared参数意为共享形式的目标文件
$(TARGET):$(OBJS)
$(CC) -shared -o $@ $^
clean:
$(RM) $(TARGET) $(OBJS)
# 编译规则 $@代表目标文件 $< 代表第一个依赖文件
#-fPIC参数,意为生成和地址无关的目标文件
%.o:%.c
$(CC) $(CFLAGS) -o $@ -fPIC -c $
//执行
gcc -Wall -o test_func.o -fPIC -c test_func.c
gcc -shared -o libtest.so test_func.o
6.4 使用自定义库文件
# 指令编译器和选项
CC = gcc
CFLAGS = -Wall
# 目标文件
TARGET = test
# C文件
SRCS = main.c
# 头文件查找路径
INC = -I.
# 库文件和库查找路径
DLIBS = -ltest
LDFLAGS = -L./lib
# 指定运行时的库文件路径
RPATH = -Wl,-rpath=./lib
# 目标文件
OBJS = $(SRCS:.c=.o)
# 链接为可执行文件
$(TARGET):$(OBJS)
$(CC) -o $@ $^ $(LDFLAGS) $(DLIBS) $(RPATH)
clean:
$(RM) $(TARGET) $(OBJS)
# 连续动作,请清除再编译链接,最后执行
exec:clean $(TARGET)
@echo 开始执行
./$(TARGET)
@echo 执行结束
# 编译规则 $@代表目标文件 $< 代表第一个依赖文件
%.o:%.c
$(CC) $(CFLAGS) $(INC) -o $@ -c $<
//输出
gcc -Wall -I. -o main.o -c main.c
gcc -o test main.o -L./lib -ltest -Wl,-rpath=./lib
```k