跳转至

make 变量

1 语法

1.1 变量赋值

如果没有特殊需求(不需要`=`的递归扩展变量特性),建议使用`:=`赋值方式。

变量的赋值有 4 种符号,如下 - 简单赋值 := 编程语言中常规理解的赋值方式,只对当前语句的变量有效。 - 递归赋值 = 赋值语句可能影响多个变量,所有目标变量相关的其他变量都受影响。 - 条件赋值 ?= 如果变量未定义,则使用符号中的值定义变量。如果该变量已经赋值,则该赋值语句无效。 - 追加赋值 += 原变量用空格隔开的方式追加一个新值。

其中 = 允许等号右边的变量在后面出现,且赋值像 C++中引用赋值一样,而 := 赋值就像 C++中的复制赋值; - := 赋值

x:=foo
y:=$(x)b
x:=new
test:
      @echo "y=>$(y)"
      @echo "x=>$(x)"
  • 结果:
y=>foob
x=>new
  • = 赋值
x=foo
y=$(x)b
x=new
test:
      @echo "y=>$(y)"
      @echo "x=>$(x)"
  • 结果:
y=>newb
x=>new

1.1.1 变量赋值规则总结

对于附加运算符“+=”,如果变量之前被设置为简单变量(“:=”或“::=”),则右侧被视为立即 immediate,否则被延迟 deferred。

immediate = deferred  
immediate ?= deferred  
immediate := immediate  
immediate ::= immediate  
immediate += deferred or immediate  
immediate != immediate  
define immediate  
deferred  
endef  
define immediate =  
deferred  
endef  
define immediate ?=  
deferred  
endef  
define immediate :=  
immediate  
endef  
define immediate ::=  
immediate  
endef  
define immediate +=  
deferred or immediate  
endef  
define immediate !=
immediate  
endef

1.2 变量的使用

变量使用可以用 3 种语法, 相较于 shell,make 中可以使用 $(VAR) 格式 - $VAR - $(VAR) - ${VAR}

1.3 替换引用

替换引用实际上是 patsubst 函数的简写形式,比如 $(foo:.o=.c) 等价于 $(patsubst %.o,%.c,$(foo))

all:
    @echo top makefileg

foo := a.o b.o l.a c.o
bar := $(foo:.o=.c)
$(info bar:$(bar))

输出结果:

[shw@rocky make]$ make
bar:a.c b.c l.a c.c
top makefile

1.4 $$$ 的使用区别

title: 为什么使用$$
因为在makefile文件中`$`具有特殊函数字符,当我们需要`$`这个字符时,需要通过`$$`来达到转义作用,类似c语言中转义字符`\`。

$(variable) 用在 makefile 变量,而 $$(variable) 用在 shell 变量中 - 示例

var1=hello
test:
  echo $var1
  var2=2;echo $$var2

2 特殊变量

2.1 override 变量

  • 功能:当在执行 make 时,变量赋值在命令行参数中设置,此时这个变量再次在 makefile 文件中被重新赋值不会覆盖命令行设置的变量值,除非在 makefile 文件中给变量增加 override 指示符。
  • 示例:
  • make 命令行
## make 命令行指令,变量赋值被单引号包裹
make print 'VAR1=hello'
  1. makefile 文件
## makefile 文件部分内容
override VAR1=world
print:
  echo $(VAR1)
  1. 执行结果, 设置 override 后 VAR1 被 makefile 重新赋值了,如果没有 override,输出结果会是 hello
$ make print 'VAR1=hello'
echo world
world

2.2 export unexport 变量

  • 功能:
  • export : 导出变量使子 makefile 中能够使用
  • unexport : 取消导出指定变量
  • 示例:假设当前目录下有子目录 subdir,且 subdir 中有 makefile 文件
## 父makefile ,-C 指定目录
export VAR1=hello
build:
  $(MAKE) -C subdir
## 子makefile,可以使用VAR1变量
build:
  echo $(VAR1)

2.3 define 多行变量(仿函数实现方式)

  • 功能:定义多行变量,自定义函数的实现方式
  • 示例
define printparam
    @echo "param is"
    @echo $1
endef
.PHONY: print
print:
    $(call printparam , hello)
  • 执行结果
$ make print
param is
hello

2.4 undefine 取消变量

  • 功能:使之前定义的变量不再生效
  • 示例
VAR=123
undefine VAR

2.5 自动化变量

  • $@ : 表示目标文件名
  • $< : 表示依赖的第一个文件名
  • $^ : 表示所有依赖文件 (每一个文件名之间用空格分隔),会去除重复文件名
  • $+ : 与 $^ 类似,但会保留重复文件名
  • 示例
main:main.o math.o
    g++ $^ -o $@
main.o:main.cpp math.h
    g++ -c $< -o main.o

上面示例中,$^ 指的是 main.o math.o, $@ 指的是 main, $<main.cpp

2.6 目标特定变量

  • 语法:pattern ... : variable-assignment
  • 在目标依赖里进行变量的定义,这里定义的变量将会适用所有由这个目标展开的规则中去
## CFLAGS变量不仅适用prog,还适用以prog.o foo.o bar.o为目标规则中,也适用他们依赖的目标的规则中
prog : CFLAGS = -g
prog : prog.o foo.o bar.o

2.7 模式特定变量

  • 语法:pattern ... : variable-assignment
  • 类似与目标特定变量,此用来模式匹配目标。 如:%.o : CFLAGS = -O 将会匹配所有 .O 后缀的目标,如果存在多个模式特点变量,优先匹配匹配限定更多的那个,如下:
%.o: %.c  
    $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@  
lib/%.o: CFLAGS := -fPIC -g  
%.o: CFLAGS := -g  
all: foo.o lib/bar.o

上面如果目标是 lib/1.o,则优先使用 lib/%.o: CFLAGS := -fPIC -g 这个匹配。

2.8 抑制目标变量的继承

在目标特定变量、模式特定变量中定义的变量是可以被依赖的目标使用的,如果不想要这样,可以是 private 抑制变量的继承。如:

EXTRA_CFLAGS =  
prog: private EXTRA_CFLAGS = -L/usr/local/lib  
prog: a.o b.o

上面的 a.ob.o 将不会继承 EXTRA_CFLAGS 的值。

2.9 模式变量

2.9.1 通配符%和*

  • %* 都是用来匹配多个字符的作用,但使用地方不同
  • % :通常在目标和依赖中成对出现,如 %.o:%.c, 此时假设有 test.o 的目标被构建,则依赖自动扩展成 test.c
  • * :一般用于模糊匹配目录下文件,如 files=*.c,表示变量 files 等于当前所有以 .c 结尾的文件;但当如果目录下,没有一个以 .c 结尾的文件时,就会扩展成 files=*c,所有一般还是使用 wildcard 函数来取代

2.9.2 %示例

  • 示例
.PHONY:targ1
targ1:1.o 2.o
    @echo 
%.o:%.c
    @echo 【%.o:%.c】 目标=$@ 依赖=$^
    touch $@
%.c:
    @echo 【%.c:】 目标=$@ 依赖=$^
    touch $@
  • 结果
$ make
【%.c:】 目标=1.c 依赖=
touch 1.c
【%.o:%.c】 目标=1.o 依赖=1.c
touch 1.o
【%.c:】 目标=2.c 依赖=
touch 2.c
【%.o:%.c】 目标=2.o 依赖=2.c
touch 2.o

rm 2.c 1.c

2.9.3 *示例

  • 示例 1:当下面示例中存在任务一个以 .c 结尾的文件(如 test.c)时
.PHONY:targ1
targ1:*.c
    @echo 目标=$@ 依赖=$^
  • 执行 make,结果如下,*.c 扩展成了 test.c 文件
$ make
目标=targ1 依赖=test.c
  • 示例 2:当下面示例中存在任务没有任何以 .c 结尾的文件时
.PHONY:targ1
targ1:*.c
    @echo 目标=$@ 依赖=$^
  • 执行 make,结果如下, *.c 没有被扩展
$ ls
makefile
$ make
echo *.c
*.c
目标=targ1 依赖=*.c

3 内置变量

3.1 常用程序变量名

make 变量 实际值 说明
$(RM) rm -f 删除文件
$(SHELL) /bin/bash
$(AR) ar 用于生成静态库的程序
$(AS) as 汇编工具
$(MAKE) make
$(CC) cc c 编译器
$(CXX) g++ cpp 编译器
$(CPP) $(CC) -E c 预处理命令
$(INSTALL) install 复制文件并设置属性的工具
$(LD) ld GNU 链接器
$(LDCONFIG) ldconfig 刷新动态链接库缓存
$(BISON) bison
$(FLEX) flex
$(LEX) lex 用于将 Lex 语法转换为源代码的程序
$(MAKEINFO) makeinfo 将 Texinfo 源文件转换为 Info 文件的程序
$(RANLIB) ranlib
$(TEXI2DVI) texi2dvi
$(YACC) yacc 用于将 Yacc 语法转换为源代码的程序
$(LINT) lint 用于在源代码上运行 lint 的程序

3.2 常用的编译选项

make 变量 说明 默认值
ARFLAGS ar 工具参数 rv
ASFLAGS as 工具参数
CFLAGS c 编译器选项
CXXFLAGS cpp 编译器选项
LDFLAGS 用于链接库的参数,如 -lfoo
LDFLAGS ld 参数

3.3 其它变量

3.3.1 SHELL

SHELL 一般是 /bin/sh

all:
    @echo top makefile

$(info SHELL:$(SHELL))
SHELL=/bin/bash
$(info SHELL:$(SHELL))

输出结果:

[shw@rocky make]$ make
SHELL:/bin/sh
SHELL:/bin/bash
top makefile

3.3.2 MAKEFILE_LIST

MAKEFILE_LIST : 这个变量会按顺序保存所有执行过的 makefile 文件名的绝对路径,如下输出结果 2 个 makefile 路径 makefile common/makefile

##### makefile
include common/makefile
all:
    @echo mian makefile
    @echo MAKEFILE_LIST列表:$(MAKEFILE_LIST)

##### common/makefile
test:
    @echo sub makefile

输出结果:

[shw@rocky make]$ make test all
sub makefile
mian makefile
MAKEFILE_LIST列表:makefile common/makefile

3.3.3 .RECIPEPREFIX

命令行的首字符默认是 tab 字符,通过修改 .RECIPEPREFIX 就能改变命令行首字符。比如如,用 > 字符替换 tab 字符 :

.RECIPEPREFIX = >  
all:  
> @echo Hello, world

.RCEPPEREFIX 的值可以多次更改;一旦设置,它将对解析的所有规则保持有效,直到修改。

3.3.4 .INCLUDE_DIRS

.INCLUDE_DIRS 保存了 include 指定搜索 makefile 文件的位置,这个变量能通过 make -I DIRECTORY, --include-dir=DIRECTORY 设置,如: 项目结构:

[shw@rocky make]$ tree
.
├── common
│   └── sub.mk
└── makefile

makefile 如下

include sub.mk
all:
    @echo top makefile

$(info .INCLUDE_DIRS:$(.INCLUDE_DIRS))

输出结果:

[shw@rocky make]$ make -I ./common
.INCLUDE_DIRS:common /usr/include /usr/local/include /usr/include
sub makefile

3.3.5 .DEFAULT_GOAL

.DEFAULT_GOAL 表示默认的 target 是哪一个,这个值保存的是第一个 target, include 的 makefile 也参与这个规则。即使 make 是指定了 target,这个变量也不会被影响。

test:
    @echo test target
all:
    @echo top makefile

$(info .DEFAULT_GOAL:$(.DEFAULT_GOAL))

输出结果:

[shw@rocky make]$ make all
.DEFAULT_GOAL:test
top makefile

3.3.6 .FEATURES

.FEATURES 变量能获取此版本的 make 支持的特殊功能列表。可能的值包括但不限于: - archives : Supports ar (archive) files using special file name syntax.
- check-symlink : Supports the -L (--check-symlink-times) flag. - else-if : Supports “else if” non-nested conditionals. - jobserver : Supports “job server” enhanced parallel builds - oneshell : Supports the .ONESHELL special target - order-only : Supports order-only prerequisites - second-expansion : Supports secondary expansion of prerequisite lists.
- shortest-stem : Uses the “shortest stem” method of choosing which pattern, of multiple applicable options, will be used. - target-specific : Supports target-specific and pattern-specific variable assignments.
- undefine : Supports the undefine directive - guile : Has GNU Guile available as an embedded extension language - load : Supports dynamically loadable objects for creating custom exten
sions

3.3.7 .EXTRA_PREREQS

.EXTRA_PREREQS 可以给单个目标或者全局设置依赖,使用场景:假设 gcc 被更新了,我们也希望我们的目标能够重新构建,这时可以这样:

myprog: myprog.o file1.o file2.o  
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)  
myprog: .EXTRA_PREREQS = $(CC)