GNU Make 功能说明及语法拾遗 - ifeq 和 函数参数中空格杂谈

  |   0 评论   |   976 浏览

makefile 是个古老又难以回避的技术. 同shell一样,其中一些语法特别古怪又难以琢磨. 这里 GNU Make的一些语法进行拾遗补充说明.

0 变量定义与赋值

0.1 一般写法

0.1.1 变量名 = 变量值

特点:

  • 延时扩展
  • 递归扩展

样例:

b = var b says $(a) ! 
a = hello wolrd

$(warning $(b))

# 输出:  makefile:4: var b says hello wolrd !

释义: 变量a比变量b晚定义. 但是在引用变量b的时候.能够递归扩展到a的新值.

0.1.2 变量名 := 变量值

特点:

  • 立即扩展

样例:

b := var b says $(a) ! 
a = hello wolrd

$(warning $(b))

# 输出:  makefile:4: var b says  !

释义: 在定义b的时候,立即进行了扩展.此时a还没有定义. (所有未定义的变量的引用是空值),因此最后b的值如上.

0.1.3 变量名 != 变量值

特点:

  • 执行一段shell并把输出赋值给变量

The shell assignment operator ‘!=’ can be used to execute a shell script and set a variable to its output. This operator first evaluates the right-hand side, then passes that result to the shell for execution. If the result of the execution ends in a newline, that one newline is removed; all other newlines are replaced by spaces. The resulting string is then placed into the named recursively-expanded variable. For example:

hash != printf '\043'
file_list != find . -name '*.c'

样例:

c != pwd
$(warning c value:$(c))

# 输出; makefile:9: c value:

这里并没有像文档中所说的能够输出一个shell的输出. 但是另外一种使用shell函数是可以的.

var := $(shell find . -name "*.c")
$(warning $(var))

# makefile:19: ./main.c

这个特性不推荐使用. 可以参考:what-is-the-difference-between-the-gnu-makefile-variable-assignments-a,making-wrong-code-look-wrong
可能的原因是: 这个 != 与其它的语言的不等可能有歧义吧.

0.2 单行变量值的边界界定

变量名: 等号左边的操作符往左trim
变量值: 等号右边的操作符号右trim , 且遇到注释结束.(如果有的话)

image.png

1 函数调用

GNU MAKE 包含如下函数列表:

Syntax of Functions How to write a function call.
Text Functions General-purpose text manipulation functions.
File Name Functions Functions for manipulating file names.
Conditional Functions Functions that implement conditions.
Foreach Function Repeat some text with controlled variation.
File Function Write text to a file.
Call Function Expand a user-defined function.
Value Function Return the un-expanded value of a variable.
Eval Function Evaluate the arguments as makefile syntax.
Origin Function Find where a variable got its value.
Flavor Function Find out the flavor of a variable.
Make Control Functions Functions that control how make runs.
Shell Function Substitute the output of a shell command.
Guile Function Use GNU Guile embedded scripting language.

1.1 函数调用语法

使用圆括号

$(function arguments)

使用花括号

${function arguments}

function 是调用函数的函数名. 可取的函数名是Make内置的函数列表中的其中一个;

arguments 是函数调用的参数列表. 参数列表使用一个或者多个 空格或者是tab 与函数名function分隔. 这些分隔的tab空格不是参数的一部分.(注: 换言之, 其它的空格是参数的一部分) , 把函数调用包起来的分隔符(即: (){}) , 其也可以出现在参数部分,但是必须是匹配的对; 其它类型的分隔符号可以单个出现. 如果参数部分包含其它的变量引用或函数调用,那最好是其它所有的分割符都使用相同于此处的函数调用的分割符; 即: 可以写作 $(subst a,b,$(x)), 但是不要写作 $(subst a,b,${x}). 这是因为,这样更清晰,同时在进行引用替换的时候也只会取一种界定符号作为引用替换的边界符.

示例:

func_name arg1,arg2,...,argn

The text written for each argument is processed by substitution of variables and function calls to produce the argument value, which is the text on which the function acts. The substitution is done in the order in which the arguments appear.

写来用于每一个参数的文本被变量替换和函数调用处理,用以来产生参数值: 即当前函数作用于其之上的文本.
替换使用与参数出现的顺序依次完成.

1.2 验证参数间的空格的影响.

基于以上的描述,

  • 函数名与参数之间是使用tab 或者是空格 进行分割. 如$(subst x, y , x 111 )
  • 然后分割完成后,使用逗号进行参数分割.
    • 函数名:substr
    • 参数:
      • 1: 'xspace'
      • 2: 'spaceyspace'
      • 3: 'spacexspace111space'
    • 返回: _space__space_y_space__space_111_space_
      • 参数y两边的空格都生效了. 因此替换后的字符串y两边会比原来各多一个空格.
      • 源字符的x前面的空格也生效了,否则替换后的字符最前面只有一个空格.
      • 111后面有一个空格.说明 source string的 结尾空格(training space)也没有被清除.
# 定义一个 null string
nullstr :=
# 定义一个冒号
colon := :
# 定论一个只包含一个空格的变量
space := $(nullstr) # end of the line
# 定义一个显式的空格表示符(用于格式化输出)
space_format := _space_
# 试验的函数
result := $(subst x, y , x 111 )
# 做格式化操作 , 这里不引入特别的空格.规避空格的问题
formatstr := $(subst $(space),$(space_format),$(result))
# 把格式化后面的数据输出.以便查验
$(warning str$(colon)$(formatstr))

default:

输出:

makefile:14: str:_space__space_y_space__space_111_space_
make: Nothing to be done for `default'.

1.3 结论

  • 函数调用的参数部分不会做任何的trim
  • 函数名与参数之间使用空格,或者是tab进行分隔. 也有可能是多个空格. 都会被trim.
  • 如果第一个参数需要一个前导的空格. 需要使用变量引用来引入空格.否则会被清除.
  • 参数之间使用逗号分隔, 且应该只有这一种分隔.

image.png

2 ifeq 的参数空格

2.1 前导空格

ifeq ( a,a)
$(warning 第一个参数前导空格trim)
else
$(warning param1 leading space not trim)
endif

#输出: param1 leading space not trim

ifeq (a, a)
$(warning 第二个参数前导空格trim)
else
$(warning param2 leading space not trim)
endif

# 输出:  第二个参数前导空格trim

2.2 尾部空格

ifeq (a ,a)
$(warning 第一个参数尾部空格trim)
else
$(warning param1 tail space not trim)
endif

# 输出: 第一个参数尾部空格trim

ifeq (a,a )
$(warning 第二个参数尾部空格trim)
else
$(warning param2 tail space not trim)
endif

# 输出:  param2 tail space not trim

2.3 总结:

image.png

2.4 Open-JDK中的一些样例

下面的两个样例中ifeq的第二个参数一个有空格,一个没有空格. 实际最后都是与 nullstring 的比较

https://hg.openjdk.java.net/jdk8u/jdk8u/file/a323800a7172/Makefile:44

# 
# line: 44
ifeq ($(filter /%,$(lastword $(MAKEFILE_LIST))),)
  makefile_path:=$(CURDIR)/$(lastword $(MAKEFILE_LIST))

https://hg.openjdk.java.net/jdk8u/jdk8u/file/a323800a7172/Makefile:#39

# line: 39

# Assume we have GNU make, but check version.
ifeq ($(strip $(foreach v, 3.81% 3.82% 4.%, $(filter $v, $(MAKE_VERSION)))), )

评论

发表评论


取消