资讯专栏INFORMATION COLUMN

makefile变量

Galence / 2063人阅读

摘要:运算符称为附带条件的变量赋值运算符。在命令行上,每个变量赋值运算符的右边部分必须是一个多带带的参数。对于宏定义,宏的变量名会被立即扩展,宏的主体延后扩展。

makefile 变量与宏

变量和宏其实说的是同一东西。一个变量的内容是一个字符串,从一个变量名获得变量内容的过程叫做变量的扩展,用$()或者${}扩住变量名即可。而不想编程语言那样,使用变量名就能引用变量的值。

变量的类型

make的变量有两种:简易变量和递归扩展的变量。变量的定义是一个赋值动作,把等号右边的内容赋给左边。这里等号可以有多种::=、=、?=,他们决定了怎样赋值。等号两边的东西可以只是字面值,或者含有变量。赋值时,变量名需要是明确的,等号左边的内容立即扩展(没有变量就保持不变),右边的内容根据赋值符号决定何时扩展。

简易变量

:=或者::=赋值运算符定义的是一个简易扩展的变量。一旦make读入该变量的定义语句,赋值运算符右边部分会立刻扩展,而扩展后的文本会被存储成该变量的值。变量名和变量内容加入到数据库。
MAKE_DEPEND := $(CC) -M
此变量一般被扩展为
gcc -M
然而,如果CC变量尚未定义,则扩展为:
-M
变量没有定义不算错误。

递归变量

=定义的变量。或者define定义的变量。
make只会读进赋值运算符后边的部分,并将之存储成该变量的值,但不会进行任何扩展的动作,扩展的动作会被延迟到该变量被使用的时候进行。

其他赋值类型

make还提供了另外两种赋值运算符:?=+=

?= 运算符称为附带条件的变量赋值运算符。此运算符只会在变量的值尚不存在是进行复制动作。
+= 运算符称为附加运算符。此运算符会将文本附加到变量里。对递归变量仍有用。
对于简单变量,等效于
simple := $(simple) new stuff
然而,对于递归变量
recursive = $(recursive) new stuff
这种表达式是非法的,会被无限扩展。要想将某段文本附加到递归变量上,就需要附加运算符了。

工作目标与模式的专属变量

make提供了工作目标的专属变量。这些变量的定义会附加在工作目标上,且只有在该工作目标以及相应的必要条件被处理的时候,他们才会发生作用

工作目标的专属变量语法如下:

target...: variable = value
target...: variable := value
target...: variable ?= value
target...: variable += value

如果在编译某个源文件的时候需要多带带指定一个宏定义,然而该文件又在某个模式规则中:

gui.o: CPPFLAGS += -DUSE_NEW_MALLOC=1
gui.o: gui.h

当make处理gui.o这个工作目标时,CPPFLAGS这个变量会附加-DUSE_NEW_MALLOC=1,当处理完gui.o这个目标之后,CPPFLAGS会恢复它原来的值。

变量的来源

文件

变量可以被定义在makefile中,或是被makefile引入(include指令)

命令行

可以直接在make命令行上定义或者重新定义变量: $ make CFLAG=-g CPPFLAGS="DBSD -DDEBUG"
每个命令行参数中所包含的等号,都是一个变量赋值运算符。在命令行上,每个变量赋值运算符的右边部分必须是一个多带带的shell参数。如果变量的值含有空格,则必须为参数加上括号或是引号。
命令行上变量的赋值将会覆盖掉环境变量以及makefile中的赋值结果。如果要使makefile中的变量覆盖命令行中的变量,可以在makefile中的变量前加override指令。

环境

当make启动时,所有来自环境的变量都会被定义为make的变量。这些变量具有非常低的优先级,makefile文件或命令行参数的赋值结果都会覆盖环境变量的值。但是,可以使用--environment-overrides(或-e)命令行选项,让环境变量覆盖相应的makefile变量。
当make被递归调用时,有若干来自上层make的变量会通过环境传递给下层的make。默认只有原来就来自环境的变量会被导出到下层的环境中。可以使用export指令导出任何变量。

自动创建

make会在执行一个规则的命令脚本之前立即创建自动变量。注意自动变量创建的时机。

在makefile中定义变量

variable := value

variable = value

variable ?= value

define variable =
...
...
endef
define variable :=
...
...
endef

define的特长是可以定义多行内容的变量。

一个变量的值由赋值符号右边除去前导空格的所有字符组成。跟在所有字之后的空格不会被删除。这有时会导致问题。

宏是以前对变量的另一种称呼。
可以通过define指令创建“封装命令序列”,称为宏。在make中,宏只是用来定义变量的另一种形式,此变量还可以包含换行符。一般,将由define定义的变量称为宏,由赋值运算符定义的变量称为变量。

define后跟着变量名和换行,变量的主体是由跳格符开头的命令行,最后以endef结尾。

define create-jar
    @echo Creating $@...
    $(RM) $(TMP_JAR_DIR)
    $(MKDIR) $(TMP_JAR_DIR)
    $(CP) -r $^ $(TMP_JAR_DIR)
    cd $(TMP_JAR_DIR) && $(JAR) $(JARFLAGS) $@ .
    $(JAR) -ufm $@ $(MANIFEST)
    $(RM) $(TMP_JAR_DIR)
endef

@的作用:make将每条命令交给shell执行之前都会打印出此条命令,在该命令之前加@可以使make不这样做。如果在命令中应用了一个宏,使用@将使整个宏扩展后的命令之前都加上@。

当make运行时,它会以两个阶段来完成他的工作。第一阶段,make读进makefile以及被引入的任何其他makefile。这时,其中所定义的变量会被加载到make的内部数据库,并建立依存图。第二阶段,make分析依存图并判断需要更新的目标,然后执行脚本。

当make在处理递归变量或者define指令的时候,会将变量里的每一行或宏的主体存储起来,包括换行符号,但不会予以扩展。宏定义的最后一个换行符不会作为宏的一部分,否则,宏被扩展时会多一个换行。
当宏被扩展时,make会立即扫描被扩展的文本中是否存在宏或变量的引用,如果存在就予以扩展,如此递归下去。如果宏是在命令脚本里被扩展的,则宏的主体的每一行都会被插入一个跳格符。

下面是makefile中的元素何时被扩展的原则:

对于变量赋值,make会在第一阶段读取该行时,立即扩展赋值运算符左边的部分。

=?=的右边会被延后到他们被使用时扩展,并且在第二阶段运行。

:=右边的部分会被立即扩展

如果+=的左边部分原本被定义成一个简单变量,+=的右边就会被立即扩展,否则,求值动作就会延后。

对于宏定义,宏的变量名会被立即扩展,宏的主体延后扩展。

对于规则,工作目标和必要条件总是被立即扩展,而命令总是延后扩展。

延后扩展发生在它所在的表达式需要被扩展时,比如在规则的目标和必要条件中、在一个将要执行的命令中或者出现在简易变量赋值的右边等等。

OUTPUT_DIR := /tmp

$(OUTPUT_DIR)/very_big_file:
    $(free-space)

define free-space
    $(PRINTF) "Free disk space"
    $(DF) . | $(AWK) "NR == 2 { print $$4 }"
endef

BIN := /usr/bin
PRINTF := $(BIN)/printf
DF := $(BIN)/df
AWK := $(BIN)/awk

说明:

第一阶段:make逐行读取makefile并将变量加入内部数据库,建立依存图。
OUTPUT_DIR是简单变量,它的值就是普通的字面值(如果这里有$引用的变量,将会进行扩展动作),放到数据库中。
接下来是一条规则,规则的目标和条件都是立即扩展的,而命令是延后扩展的,保持不变。所以这条规则变为:

/tmp/very_big_file:
    $(free-space)

之后是一个宏定义,宏名是立即扩展的,这里只是字面值,不用扩展。宏体是延后扩展的,在使用该宏的时候才扩展。
最后4个简易变量都是直接扩展的,将变量值加入到数据库中。

第二阶段:
按照后序遍历规则树进行规则的执行动作。这时要使用规则中的命令部分,对命令中的变量和宏进行扩展,并执行命令。

自动变量

自动变量是一种make在处理规则时自动赋值的变量。

变量名 描述
$@ 工作目标的文件名
$% 档案文件成员结构中的文件名元素
$< 第一个必要条件的文件名
$? 时间戳在工作目标之后的所有必要条件,并以空格隔开。
$^ 所有必要条件的文件名,并以空格隔开。
$+ 如同$^,代表所有必要条件的文件名,并以空格隔开。不过$+包含重复的文件名。
$* 工作目标的主文件名。文件名由主文件名和扩展名构成。

说明: 档案文件中个别的成员可作为工作目标或必要条件。可以通过archive(member)这样的语法在档案文件archive中指定名为member的成员。若工作目标是foo.a(bar.o),则$%bar.o$@foo.a。当工作目标不是一个档案文件时,$%是空的。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/10627.html

相关文章

  • Makefile 速查笔记

    摘要:如果将放在命令前面,则表示无视这条命令的返回值是否为成功如果上一条命令的结果需要用于下一条命令时,需要将这些命令写在一行中。返回值则是的最终执行值。函数执行命令,并且将作为返回值返回,如控制输出这也同时是调试和定位的好方法。 做 Linux C++,一个稳定的工程,Makefile 是很少改动的。但是如果需要修改的时候,Makefile 的语法和用法一时半会就回忆不出来(原谅我记忆力差...

    AJie 评论0 收藏0
  • 实践:GNU构建系统

    摘要:原文实践构建系统在上一篇概念构建系统和,我对构建系统从用户视角和开发者视角分别进行了阐述。本篇从我的实践总结的角度,并阐述如何从头开始规划一个基于构建系统的项目。其中一种常用的宏是声明一个变量,检查程序是否存在并可执行 原文:实践:GNU构建系统 在上一篇概念:GNU构建系统和Autotool,我对GNU构建系统从用户视角和开发者视角分别进行了阐述。本篇从我的实践总结的角度,并阐述如何...

    13651657101 评论0 收藏0
  • linux学习笔记-makefile的写法

    摘要:我们来看一个示例在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个键作为开头。我们在一开始就这样定义于是我们的可以修改成这样子于是如果有新的文件加入,我们只需简单地修改一下变量就可以了。 前言   一个项目,拥有成百上千的源程序文件,那如何组织这些源码文件的编译和链接呢?此时就需要确定整个工程的编译链接规则,Makefile就是用来指定规则的。而make...

    Binguner 评论0 收藏0
  • 【Linux】linux环境基础开发工具使用

    摘要:函数名列出某个函数的源代码,含函数名上下各五行类比调试或从开始连续而非单步执行程序遇到断点停下。相当于中的或单条执行。 目录 一、调试器gdb 1、可以使用gdb的可执行文件生成 2、使用命令 1、开始调试和退出调试 2、list 3、类比vs调试 4、代码调试三剑客 5、变量 6、断点 二...

    dongfangyiyu 评论0 收藏0
  • 【转载】【工具使用】Linux下Makefile的automake生成全攻略

    摘要:生成我们使用命令来帮助我们根据目录下的源代码生成一个的模板文件。在中号表示注释,这个宏后面的内容将被忽略。这个宏将检查系统所用的编译器。这个是指定产生时所需要的源代码。生成发布软件包并对其进行测试检查,以确定发布包的正确性。 作为Linux下的程序开发人员,大家一定都遇到过Makefile,用make命令来编译自己写的程序确实是很方便。一般情况下,大家都是手工写一个简单Makefile...

    PingCAP 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<