makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。
makefile的好处就是:
- “自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
- 节约编译时间(没改动的文件不编译)。
make是一个命令工具,是一个解释makefile中指令的命令工具。
由于u-boot的Makefile中存在相互调用,这里介绍一下make -f和make -C区别:
- -C选项 :Makefile中使用make -C会改变当前的工作目录,表示到子目录下执行子目录的Makefile,顶层Makefile中的export的变量还有make默认的变量是可以传递给子目录中的Makefile的;
- -f选项:顶层Makefile使用make -f调用子目录中的文件(文件名可以随意,不一定用Makefile作为文件名,顶层Makefile中的export的变量也可以传递变量到底层目录,另外在命令行中加入变量赋值选项,将覆盖顶层Makefile中export的变量;
注意:在顶层Makefile中使用-f选项,例如make -f ./xxx/xx/build.mk 此时make命令的工作目录仍然是顶层目录,即CUDIR变量依然是./目录而不是./xxx/xx/目录。
一、Makefile规则
1.1 基本规则(注释规范)
一个简单的Makefile 主要的 5个部分 (显示规则, 隐晦规则, 变量定义, 文件指示, 注释),其样式如下:
target:prerequisites
command
...
...
=====================
目标 : 依赖文件
[tab键] 命令
...
...
这是一个文件依赖关系,也就是说target(一个或多个目标文件)依赖于prerequisites中的文件,其生成规则定义在command中,而且只要prerequisites中有一个以上的文件比target文件更新的话,command所定义的命令就会被执行,这是makefile的最基本规则,也是makefile中最核心的内容。
其中:
- target: 是目标文件,可以是object file,也可以是执行文件,还可以是一个标签label。如果目标文件的更新时间晚于依赖文件的更新时间,则说明依赖文件没有改动,目标文件不需要重新编译。否则重新编译并更新目标。
- prerequisites:即目标文件由哪些文件生成。如果依赖条件中存在不存在的依赖条件,则会寻找其它规则是否可以产生依赖条件。例如:规则一是生成目标 hello.elf需要使用到依赖条件 hello.o,但是 hello.o 不存在。则 Makefile 会寻找到一个生成 hello.o 的规则二并执行。
- command:即通过执行该命令,由依赖文件生成目标文件,规则中的命令被传递给shell进行解析执行。注意每条命令前必须有且仅有一个 tab 保持缩进,这是语法要求。如果同一行有多条命令,命令间用分号分割。。
(1) 显示规则 : 说明如何生成一个或多个目标文件(包括 生成的文件, 文件的依赖文件, 生成的命令);
(2) 隐晦规则 : make的自动推导功能所执行的规则;
(3) 变量定义: Makefile中定义的变量;
(4) 文件指示 : Makefile中引用其他Makefile; 指定Makefile中有效部分; 定义一个多行命令;
(5) 注释: Makefile只有行注释 "#", 如果要使用或者输出"#"字符, 需要进行转义, "#";
GNU make 的工作方式:
- 读入主Makefile (主Makefile中可以引用其他Makefile);
- 读入被include的其他Makefile;
- 初始化文件中的变量;
- 推导隐晦规则, 并分析所有规则;
- 为所有的目标文件创建依赖关系链;
- 根据依赖关系, 决定哪些目标要重新生成;
- 执行生成命令;
Makefile文件注释有以下几种方式:
- 单行注释:makefile 把 # 字符后面的内容作为注释内容处理(shell、perl 脚本也是使用 # 字符作为注释符)
-
多行注释:如果需要注释多行,在注释行的结尾加行反斜线(),下一行也被注释,可以注释多行。
1.2 规则中的通配符
- * : 表示任意一个或多个字符;
- ? : 表示任意一个字符;
- [...]: ex. [abcd] 表示a,b,c,d中任意一个字符, [^abcd]表示除a,b,c,d以外的字符, [0-9]表示 0~9中任意一个数字;
- ~ :表示用户的home目录;
- %:通配符,如%.o:%c,表示表示把所有的.c文件编译输出成.o文件。
1.3 路径搜索
当一个Makefile中涉及到大量源文件时(这些源文件和Makefile极有可能不在同一个目录中),这时, 最好将源文件的路径明确在Makefile中, 便于编译时查找。Makefile中有个特殊的变量VPATH就是完成这个功能的。指定了 VPATH 之后, 如果当前目录中没有找到相应文件或依赖的文件, Makefile会到VPATH 指定的路径中再去查找。
VPATH 使用方法:
- vpath <directories>: 当前目录中找不到文件时, 就从<directories>中搜索;
- vpath <pattern> <directories>:符合<pattern>格式的文件, 就从<directories>中搜索;
- vpath <pattern>:清除符合<pattern>格式的文件搜索路径;
- vpath:清除所有已经设置好的文件路径;
# 示例1 - 当前目录中找不到文件时, 按顺序从 src目录 ../parent-dir目录中查找文件
VPATH src:../parent-dir
# 示例2 - .h结尾的文件都从 ./header 目录中查找
VPATH %.h ./header
# 示例3 - 清除示例2中设置的规则
VPATH %.h
# 示例4 - 清除所有VPATH的设置
VPATH
1.4 示例
我们以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 (目标,省略的话默认执行第一个目标):
编译并执行:
make
./hello.o
把hello.c文件复制一份,重命名为hello_copy.c。修改Makefile的内容如下:
ALL: $(obj) $(obj):%.o: %.c gcc $< -o $@ .PHONY: clean clean: rm -f $(obj)
- 第一个target是ALL,而ALL由两个.o文件构成,对应的两个.c文件可以同时编译;
- target可以是变量,例子中的obj是一个变量;
- 这里加上了一个.PHONY、$<、$@,究竟是啥呢?这个后面我们会介绍。
二、Makefile函数
Makefile 中自带了一些函数, 利用这些函数可以简化 Makefile 的编写。
函数调用语法如下:
$(<function> <arguments>)
# 或者
${<function> <arguments>}
- <function> 是函数名;
- <arguments> 是函数参数;
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 变量。
示例:
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下)。
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
1).找出符合PATTERN 格式的值
- 语法:$(filter PATTERN... ,TEXT)
- 函数功能:过滤掉字串“ TEXT”中所有不符合模式“ PATTERN ”的单词,保留所有符合此模式的单词。可以使用多个模式。模式中一般需要包含模式字符“%”。存在多个模式时,模式表达式之间使用空格分割。
- 返回值:空格分割的“ TEXT”字串中所有符合模式“ PATTERN ”的字串。
示例:
# Makefile 内容
all:
@echo $(filter %.o %.a,program.c program.o program.a)
执行命令:
# bash 中执行 make $ make program.o program.a
2).找出不符合PATTERN 格式的值
- 语法:$(filter-out PATTERN... ,TEXT)
- 函数功能:过滤掉字串“ TEXT”中所有符合模式“ PATTERN ”的单词,保留所有不符合此模式的单词。可以使用多个模式。模式中一般需要包含模式字符“%”。存在多个模式时,模式表达式之间使用空格分割。
- 返回值:空格分割的“ TEXT”字串中所有不符合模式“ PATTERN ”的字串。
示例:
# Makefile 内容
all:
@echo $(filter-out %.o %.a,program.c program.o program.a)
执行命令:
# bash 中执行 make $ make program.c
2.5 if函数
这里的if是个函数,它用于判断条件是否存在或者为true。 和后面将会介绍的条件判断不一样, 后面介绍的条件判断属于Makefile的关键字。
- 语法:$(if <condition>,<then-part>) 或 $(if <condition>,<then-part>,<else-part>)
- 函数功能:用于条件判断。
- 返回值:返回符合条件的部分的值。
示例:
# Makefile 内容
val := a
objects := $(if $(val),$(val).o,nothing)
no-objects := $(if $(no-val),$(val).o,nothing)
all:
@echo $(objects)
@echo $(no-objects)
执行命令:
# bash 中执行 make
$ make
a.o
nothing
2.6 创建新的参数化函数call
- 语法:$(call <expression>,<parm1>,<parm2>,<parm3>...)
- 函数功能:函数调用。
- 返回值:返回调用的函数的执行结果。
示例:
# Makefile 内容
log = "====debug====" $(1) "====end===="
all:
@echo $(call log,"正在 Make")
执行命令:
# bash 中执行 make
$ make
====debug==== 正在 Make ====end====
2.7 判断变量的来源函数orgin
- 语法:$(origin <variable>)
- 函数功能:判断变量的来源,返回值有以下类型:
类型 |
含义 |
undefined | <variable> 没有定义过 |
default | <variable> 是个默认的定义, 比如 CC 变量 |
environment | <variable> 是个环境变量, 并且 make时没有使用 -e 参数 |
file | <variable> 定义在Makefile中 |
command line | <variable> 定义在命令行中 |
override | <variable> 被 override 重新定义过 |
automatic | <variable> 是自动化变量 |
示例:
# Makefile 内容
val-in-file := test-file
override val-override := test-override
all:
@echo $(origin not-define) # not-define 没有定义
@echo $(origin CC) # CC 是Makefile默认定义的变量
@echo $(origin PATH) # PATH 是 bash 环境变量
@echo $(origin val-in-file) # 此Makefile中定义的变量
@echo $(origin val-in-cmd) # 这个变量会加在 make 的参数中
@echo $(origin val-override) # 此Makefile中定义的override变量
@echo $(origin @) # 自动变量, 具体前面的介绍
执行命令:
# bash 中执行 make
$ make val-in-cmd=val-cmd
undefined
default
environment
file
command line
override
automatic
2.8 shell
- 语法:$(shell <shell command>)
- 函数功能:执行一个shell命令,作用和 `<shell command>` 一样, ` 是反引号。
- 返回值:将shell命令的结果作为函数的返回。
2.9 去空格函数strip
- 语法:$(strip <string>)
- 函数功能:去掉 <string> 字符串中开头和结尾的空字符。
- 返回值:被去掉空格的字符串值。
示例:
# Makefile 内容 VAL := " aa bb cc " all: @echo "去除空格前: " $(VAL) @echo "去除空格后: " $(strip $(VAL))
执行命令:
# bash 中执行 make $ make 去除空格前: aa bb cc 去除空格后: aa bb cc
2.10 查找字符串函数findstring
- 语法 $(findstring <find>,<in>)、
- 函数功能: 在字符串 <in> 中查找 <find> 字符串。
- 返回值: 如果找到, 返回 <find> 字符串, 否则返回空字符串。
示例:
# Makefile 内容 VAL := " aa bb cc " all: @echo $(findstring aa,$(VAL)) @echo $(findstring ab,$(VAL))
执行命令:
# bash 中执行 make $ make aa
2.11 更多函数
由于Makefile函数过多,就不一一介绍了,更多函数信息请移步此处。
三、makefile内置变量
3.1 shell变量(CXX、CC等)
下面只列出一些C相关的:
变量名 |
含义 |
RM | rm -f |
AR | ar |
CC | cc |
CXX | g++ |
示例:
# Makefile 内容
all:
@echo $(RM)
@echo $(AR)
@echo $(CC)
@echo $(CXX)
# bash 中执行make, 显示各个变量的值
$ make
rm -f
ar
cc
g++
3.2 自动变量($@、%<等)
Makefile 中很多时候通过自动变量来简化书写, 各个自动变量的含义如下:
自动变量 |
含义 |
$@ | 目标集合 |
$% | 当目标是函数库文件时, 表示其中的目标文件名 |
$< | 第一个依赖目标. 如果依赖目标是多个, 逐个表示依赖目标 |
$? | 比目标新的依赖目标的集合 |
$^ | 所有依赖目标的集合, 会去除重复的依赖目标 |
$+ | 所有依赖目标的集合, 不会去除重复的依赖目标 |
$* | 这个是GNU make特有的, 其它的make不一定支持 |
示例:
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 也设置为伪目标。
4.3 Makefile命令前缀
我们知道在Makefile文件中是可以编写shell命令的,Makefile 中书写shell命令时可以加2种前缀 @ 和 -, 或者不用前缀。
3种格式的shell命令区别如下:
- 不用前缀:输出执行的命令以及命令执行的结果, 出错的话停止执行;
- 前缀 @ :关闭命令回显,只输出命令执行的结果, 出错的话停止执行;
- 前缀 - :命令执行有错的话, 忽略错误, 继续执行;
不用前缀示例:
# Makefile 内容 (不用前缀)
all:
echo "没有前缀"
cat this_file_not_exist
echo "错误之后的命令" <-- 这条命令不会被执行
执行命令:
# bash中执行 make
$ make
echo "没有前缀" <-- 命令本身显示出来
没有前缀 <-- 命令执行结果显示出来
cat this_file_not_exist
cat: this_file_not_exist: No such file or directory
make: *** [all] Error 1
前缀 @ 示例:
# Makefile 内容 (前缀 @)
all:
@echo "没有前缀"
@cat this_file_not_exist
@echo "错误之后的命令" <-- 这条命令不会被执行
执行命令:
# bash中执行 make
$ make
没有前缀 <-- 只有命令执行的结果, 不显示命令本身
cat: this_file_not_exist: No such file or directory
make: *** [all] Error 1
前缀 - 示例:
# Makefile 内容 (前缀 -)
all:
-echo "没有前缀"
-cat this_file_not_exist
-echo "错误之后的命令" <-- 这条命令会被执行
执行命令:
# bash中执行 make
$ make
echo "没有前缀" <-- 命令本身显示出来
没有前缀 <-- 命令执行结果显示出来
cat this_file_not_exist
cat: this_file_not_exist: No such file or directory
make: [all] Error 1 (ignored)
echo "错误之后的命令" <-- 出错之后的命令也会显示
错误之后的命令 <-- 出错之后的命令也会执行
4.4 引用其它的Makefile
语法: include <filename> (filename 可以包含通配符和路径)。
示例:
# Makefile 内容
all:
@echo "主 Makefile begin"
@make other-all
@echo "主 Makefile end"
include ./other/Makefile
# ./other/Makefile 内容
other-all:
@echo "other makefile begin"
@echo "other makefile end"
执行命令:
# bash中执行 make
$ make
主 Makefile begin
make[1]: Entering directory `/path/to/makefile/other'
other makefile begin
other makefile end
make[1]: Leaving directory `/path/to/makefile/other'
主 Makefile end
如果我们想向other/Makefile传递参数怎么办?可以使用export:
export 语法格式如下:
- export variable = value;
- export variable := value;
- export variable += value;
# Makefile 内容
export VALUE1 := export.c <-- 用了 export, 此变量能够传递到 ./other/Makefile 中
VALUE2 := no-export.c <-- 此变量不能传递到 ./other/Makefile 中
all:
@echo "主 Makefile begin"
@cd ./other && make
@echo "主 Makefile end"
# ./other/Makefile 内容
other-all:
@echo "other makefile begin"
@echo "VALUE1: " $(VALUE1)
@echo "VALUE2: " $(VALUE2)
@echo "other makefile end"
执行命令:
# bash中执行 make
$ make
主 Makefile begin
make[1]: Entering directory `/path/to/makefile/other'
other makefile begin
VALUE1: export.c <-- VALUE1 传递成功
VALUE2: <-- VALUE2 传递失败
other makefile end
make[1]: Leaving directory `/path/to/makefile/other'
主 Makefile end
4.5 定义命令包
命令包有点像是个函数, 将连续的相同的命令合成一条, 减少 Makefile 中的代码量, 便于以后维护。语法:
define <command-name>
command
...
endef
示例:
# Makefile 内容
define run-hello-makefile
@echo -n "Hello"
@echo " Makefile!"
@echo "这里可以执行多条 Shell 命令!"
endef
all:
$(run-hello-makefile)
执行命令:
# bash 中运行make
$ make
Hello Makefile!
这里可以执行多条 Shell 命令!
4.6 条件判断
条件判断的关键字主要有 ifeq ifneq ifdef ifndef。语法:
<conditional-directive>
<text-if-true>
endif
# 或者
<conditional-directive>
<text-if-true>
else
<text-if-false>
endif
ifeq的例子, ifneq和ifeq的使用方法类似, 就是取反。示例:
# Makefile 内容
all:
ifeq ("aa", "bb")
@echo "equal"
else
@echo "not equal"
endif
执行命令:
# bash 中执行 make
$ make
not equal
ifdef的例子, ifndef和ifdef的使用方法类似, 就是取反。示例:
# Makefile 内容
SRCS := program.c
all:
ifdef SRCS
@echo $(SRCS)
else
@echo "no SRCS"
endif
执行命令:
# bash 中执行 make
$ make
program.c
五、Makefile变量
5.1 变量定义
变量的种类分为:
- 既时变量(简单变量):例子: 赋值方式: 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。@表示关闭命令回显。
5.2 变量替换 $(var:%.c=%.o)
# Makefile内容
SRCS := programA.c programB.c programC.c
OBJS := $(SRCS:%.c=%.o)
all:
@echo "SRCS: " $(SRCS)
@echo "OBJS: " $(OBJS)
$(SRCS:%.c=%.o)表示将SRCS变量中以.c结尾的字符串替换为.o结尾。运行结果:
# bash中运行make
$ make
SRCS: programA.c programB.c programC.c
OBJS: programA.o programB.o programC.o
5.3 变量追加值 +=
# Makefile内容
SRCS := programA.c programB.c programC.c
SRCS += programD.c
all:
@echo "SRCS: " $(SRCS)
运行结果:
# bash中运行make
$ make
SRCS: programA.c programB.c programC.c programD.c
5.4 变量覆盖 override
作用是使 Makefile中定义的变量能够覆盖 make 命令参数中指定的变量:
- override <variable> = <value>
- override <variable> := <value>
- override <variable> += <value>
下面通过一个例子体会 override 的作用:
# Makefile内容 (没有用override)
SRCS := programA.c programB.c programC.c
all:
@echo "SRCS: " $(SRCS)
# bash中运行make
$ make SRCS=nothing
SRCS: nothing
使用override:
# Makefile内容 (用override)
override SRCS := programA.c programB.c programC.c
all:
@echo "SRCS: " $(SRCS)
# bash中运行make
$ make SRCS=nothing
SRCS: programA.c programB.c programC.c
5.5 目标变量
作用是使变量的作用域仅限于这个目标(target), 而不像之前例子中定义的变量, 对整个Makefile都有效.
语法:
- <target> : <variable-assignment>
- <target> : override <variable-assignment> (override作用参见 变量覆盖的介绍)
# Makefile 内容
SRCS := programA.c programB.c programC.c
target1: TARGET1-SRCS := programD.c #目标变量定义
target1:
@echo "SRCS: " $(SRCS)
@echo "SRCS: " $(TARGET1-SRCS)
target2:
@echo "SRCS: " $(SRCS)
@echo "SRCS: " $(TARGET1-SRCS)
执行命令:
# bash中执行make
$ make target1
SRCS: programA.c programB.c programC.c
SRCS: programD.c
$ make target2 <-- target2中显示不了 $(TARGET1-SRCS)
SRCS: programA.c programB.c programC.c
SRCS:
六、Makefile中一些GNU约定俗成的伪目标
如果有过在Linux上, 从源码安装软件的经历的话, 就会对 make clean, make install 比较熟悉。
像 clean, install 这些伪目标, 广为人知, 不用解释就大家知道是什么意思了。
下面列举一些常用的伪目标, 如果在自己项目的Makefile合理使用这些伪目标的话, 可以让我们自己的Makefile看起来更专业。
伪目标 |
含义 |
all | 所有目标的目标,其功能一般是编译所有的目标 |
clean | 删除所有被make创建的文件 |
install | 安装已编译好的程序,其实就是把目标可执行文件拷贝到指定的目录中去 |
列出改变过的源文件 | |
tar | 把源程序打包备份. 也就是一个tar文件 |
dist | 创建一个压缩文件, 一般是把tar文件压成Z文件. 或是gz文件 |
TAGS | 更新所有的目标, 以备完整地重编译使用 |
check 或 test | 一般用来测试makefile的流程 |
七、案例
#定义方法 执行命令,并回显命令 echo-cmd=$(if $(1),echo $(1);) # 执行命令 test: @echo '-----------------------------' #关闭回显 $(echo-cmd) pwd #等价执行 echo 'pwd'; pwd $(echo-cmd) ls /data #等价执行 echo 'ls /data';ls /data # 测试1 调用shell命令 SRC_DIR := src SUBDIR += $(if $(SRC_DIR),`pwd`;) test1: echo $(SUBDIR) #====>echo `pwd` ``执行命令,返回命令结果 A=pwd test2: pwd #执行命令 echo pwd #pwd echo $A #pwd echo '$A' #pwd echo "$A" #pwd echo $(A) #pwd echo `$(A)` #执行命令 $(if $(A),echo $(A))
执行结果:
[root@VM-0-17-centos 1]# make test ----------------------------- pwd #等价执行 echo pwd;pwd /data/shell_test/1 ls /data #等价执行 echo 'ls /data';ls /data blog clear.sh cmc jdk1.8.0_231 mysql nacos nginx package redis shell_test webapp [root@VM-0-17-centos 1]# make test1 echo `pwd`; #====>echo `pwd` ``执行命令,返回命令结果 /data/shell_test/1 [root@VM-0-17-centos 1]# make test2 pwd #执行命令 /data/shell_test/1 echo pwd #pwd pwd echo pwd #pwd pwd echo 'pwd' #pwd pwd echo "pwd" #pwd pwd echo pwd #pwd pwd echo `pwd` #执行命令 /data/shell_test/1 echo pwd pwd
参考文章
文章来源: 博客园
- 还没有人评论,欢迎说说您的想法!