程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)

嵌入式Linux开发之Makefile

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 的工作方式:

  1. 读入主Makefile (主Makefile中可以引用其他Makefile);
  2. 读入被include的其他Makefile;
  3. 初始化文件中的变量;
  4. 推导隐晦规则, 并分析所有规则;
  5. 为所有的目标文件创建依赖关系链;
  6. 根据依赖关系, 决定哪些目标要重新生成;
  7. 执行生成命令;

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 安装已编译好的程序,其实就是把目标可执行文件拷贝到指定的目录中去
print 列出改变过的源文件
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

参考文章

[1] Makefile简单使用实例

[2] Makefile 语法入门

[3]Makefile 使用总结

[4]快速的理解MakeFile+读懂一个MakeFile

posted @ 2021-05-30 14:49  大奥特曼打小怪兽  阅读(989)  评论(0编辑  收藏  举报
如果有任何技术小问题,欢迎大家交流沟通,共同进步