《GNU make项目管理》笔记--变量与宏(1)

make包含两种语言。第一种语言用来描述工作与必要条件所组成的依存图。第二种语言是宏语言,用来进行文字替换。像

C预处理器,m4以及宏汇编器。

一个变量名称几乎可以由任何字符自称。包括大部分的标点符号,即使空格也可以使用,但应该避免这么做。事实上只有:

、#和=等字符不允许使用在变量名称中。

变量名称是区分大小写的。要取得某个变量的值,请使用$()括住该变量的名称,有一个特例:变量名称若为单一字母(letter)

则可以省略圆括号,所以请直接使用$letter。

你还可以使用花括号来扩展变量,例如${CC}.现代化的makefile多半使用园括号。

当 变量用来表示用户在命令行上或环境中所定义的常数时,习惯上会全部以大写来编写其名称,单词之间以下划线符号_隔开。

至于只在makefile文件中出现的变量,则会全部以小写来编写其名称,单词之间以下划线符号隔开。最后,内含用户自定义

函数的变量以及宏都会以小写来编写其名称,单词之间以破折号-隔开。

#常数

CC := gcc

MKDIR := mkdir -p

#内部变量

sources = *.c

objects = $(subst .c,.o,$(sources))

#函数

maybe-make-dir = $(if $(wildcard $1),,$(MKDIR) $1)

1.变量的类型

make的变量有两种类型:经简单扩展的变量以及经递归扩展的变量

你可以使用:=赋值运算来定义一个经简单扩展的变量:

MAKE_DEPEND := $(CC) -M

之所以称此变量为经简单扩展时因为,一旦make从makefile读进该变量的定义语句,赋值运算符的右边部分会立即被扩展。

赋值运算符的右边部分只要出现make变量的引用就会被扩展,而扩展后所产生的文本则会被存储称该变量的值。此行为跟

大多数的程序和脚本语言相同。举例,此变量被扩展之后一般会成为下面这样:

gcc -M

然而,如果上面的CC变量尚未定义,则此变量被扩展后一般会变成这样:

<space> -M

第二种变量类型称为经递归扩展的变量。你可以用=赋值运算符来定义一个经递归扩展的变量:

MAKE_DEPEND = $(CC) -M

之所以称此变量为经递归扩展是因为,make只会读进赋值运算符右边的部分,并将之存储成该变量的值,但不会进行任何

扩展的动作,扩展动作会被延迟到该变量被使用的时候才进行,将此变量称为延后扩展的变量或许比较恰当。因为扩展动作

会延迟到该变量实际被使用的时候才进行。

MAKE_DEPEND = $(CC) -M

...

#稍后

CC = gcc

这样,当MAKE_DEPEND被使用的使用,即使CC并未定义,MAKE_DEPEND在脚本中的值也会被扩展成gcc -M。

2.其他的赋值类型

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

?= 运算符称为附带条件的变量赋值运算符。此运算符只会在变量的值尚不存在的状况下进行变量要求赋值的动作。

#将所产生的每个文件放到$(PROJECT_DIR)/out目录中。

OUTPUT_DIR ?= $(PROJECT_DIR)/out

我们只会在输出目录变量OUTPUT_DIR的值尚不存在的状况下对它进行赋值的动作。

+=运算符通常被称为附件运算符,此运算符会将文本附加到变量中,对于简单变量进行附加的动作,+=运算符可以被实现

成这样:

simple := $(simple) new stuff

但是递归变量会导致一个问题,如果将+=运算符实现成下面这样,是不允许的。

recursive = $(recursive) new stuff

这时一个错误,因为make没有办法妥善地加以处理。如果make存储recursive当前的定义加上new stuff,则make就不能在

运行时再次扩展它。此时试图扩展一个自我引用的递归变量将会产生一个无限循环。

所以,+=被特别实现成可将文本附加到递归变量中并做正确的事。

3.宏

变量适合用来存储单行形式的值,对于多行形式的值,例如命令脚本,如果我们想在不同的地方执行它,该怎么办?例如

下面的命令序列:

ehco create [email protected]

$(RM) $(TMP_DIR)

$(MKDIR) $(TMP_DIR)

$(CC) main.c

$(RM) $(TMP_DIR)

在GNU make中,我们可以通过define指令以创建宏,在make中,宏只是用来定义变量的另一种方式,此变量还可以包含

内置换行符号。

define create-obj

@echo create [email protected]

$(RM) $(TMP_DIR)

$(MKDIR) $(TMP_DIR)

$(CC) main.c

$(RM) $(TMP_DIR)

endef

define指令后面跟着变量名称以及一个换行符号。变量的主体包含了所有的命令序列直到endef关键字出现为止,endef关键字

必须自成一行。一个由define创建的变量,就像任何其他的边来个你一样,会被扩展许多次,除非它被使用在命令脚本的语境

中,下面是宏的使用范例:

$(UI-OBJ):$(UI-CLASSES)

$(create-obj)

请注意,我们为echo命令前置了一个@字符,当执行命令脚本时,前置@字符的命令行不会被make输出,因此,当我们运行

make时,它不会输出echo命令本身,只会输出该命令的输出,如果在宏内部使用@前缀,这个前缀字符只会影响使用到它的

命令行。然而,如果将这个前缀用在宏引用上,则整个宏主体都会被隐藏起来:

$(UI-OBJ):$(UI-CLASSES)

@$(create-obj)

当make运行时只会显示:

$make

create ui-obj...

时间: 02-02

《GNU make项目管理》笔记--变量与宏(1)的相关文章

《GNU make项目管理》笔记--规则(2)

1.模式规则 有如下的源代码树: 根目录 |---makefile |----|include| |           |___myutil.h |___|src| |----myutil.c |__main.c 各文件内容如下: main.c: #include <stdio.h> #include "myutil.h" int main(void) { myprint(); return 0; } myutil.h: void myprint(); myutil.c:

《GNU make项目管理》笔记--规则(3)

1.管理程序库 程序库(archive library)是一个特殊的文件,该文件内含其他被称为成员的文件.程序库可用来将相关的目标文件聚集成 较容易操作的单元,例如,C的标准程序库lib.a就包含了许多低级的C函数.因为程序库如此常见,所以make对它们的创建. 维护以及引用提供了特别的支持.程序库的建立及修改可通过ar程序来进行. 下面我们看一个例子,程序包结构如下: 根目录 |---makefile |----|include| |           |___myutil.h |     

GNU/Linux复习笔记(1)

第一次接触GNU/Linux还是大四上学期实习的那两个月在window里装了 个虚拟机玩红帽的系统,那段时间稍微学了一点命令就不玩了.后来大四下学期认识了王总,装了双系统,那段时间又对linux有了进一步认识并产生了很大的 兴趣.直到上学期突然发疯把笔记本装debian8以后才完全进入linux的世界.学习真的是一个螺旋式上升的过程.下面进入正题: ---Linux的基本原则: 1.由目的单一的小程序组成,组合小程序完成复杂任务(KISS:keep it simple,stupid)2.一切皆文

Sass学习笔记 -- 变量及变量作用域历史遗留问题

sass有两种后缀名文件: 一种后缀名为sass,写选择器时不能使用大括号和分号 一种后缀名为scss,使用大括号和分号 //后缀名为sass的语法,不能出现大括号和分号 $highlight-color: #abcdef .selected   border: 1px $highlight-color solid    //后缀名为scss的语法,跟css一样,需要大括号和分号 $highlight-color: #abcdef; .selected{   border:1px solid $

PMP项目管理笔记 项目定义

项目的定义 项目是为创造独特的产品,服务或成果而进行临时性的工作. 项目是组织的经营需要与战略目标服务的. PMBOK 指南描述的项目管理知识,从本质上讲,是用来管理中等或以上规模,跨部门,跨专业的目的.PMP考试中的项目,通常也是这类项目. 项目的三个特新 1.临时性 2.独特性 3.渐进明细性     项目许多地方需要渐进明细,例如    1.项目的范围    2.项目的计划    3.项目的目标    项目管理特别强调项目的特性和项目的计划都是逐渐细化出来的,因为:     项目的各种情况

JavaScript学习笔记——变量和数据类型

一.javascript命名规范 1. 严格区分大小写 2. 变量的命名必须以字母或 _或 $开头,余下的部分可以是任意的字母,数字,或者是 _或者是$ 3.不能用关键字或者是保留字命名. 4.javascript自己的命名习惯 驼峰命名法:getElementById 首字母大写:Object 5.命名一定要有意义. 6. ;的用法

GO学习笔记 - 变量在定义时没有明确的初始化时会赋值为“零值 ”。

官方教程:https://tour.go-zh.org/basics/12 变量在定义时没有明确的初始化时会赋值为 零值 . 零值是: 数值类型为 0 , 布尔类型为 false , 字符串为 "" (空字符串). 官方示例: package main import "fmt" func main() { //下面声明的变量没有被初始化,但是也具有值,就是默认的零值 var i int var f float64 var b bool var s string fm

R语言学习笔记-变量的作用域

R语言是如何将变量值和变量绑定的 在r语言中,当前的 workspace就是global enviroment,当输入变量名时,首先会在global enviroment中搜索该变量,如有,则将它显示出来. 第二步,如在global enviroment中没有找到该变量民,则搜索search list中的各个包,search list 中的内容可以用search()得到 如果用户使用library()load了一个package ,则这个package将在search list中处于第二的位置

[Swift]学习笔记----变量不会被默认初始化

顶一个变量,如果不进行初始化,是不能实用的,Swift 会报错. 目测 对象也是必须要new 才可以使用. 这又是我很赞同苹果的一个做法. Swift真不错! 而如果把变量申明为可选值,则该变量默认就会是一个 nil 值 (既空值) 语法是 var i:Int?; (吐槽以下博客园:居然不能再博客页面直接编辑博文,必须进入后台管理才可以编辑,真心弱爆了啊!看来博客园也不是写博客的长久之计!)