变量

  1. Makefile中变量和函数的展开(除规则命令行中的变量和函数以外),是在make读取makefile文件时进行的,包括“define”定义的变量。

  2. 变量可以用来代表一个文件名列表、编译选项列表、程序运行的选项参数列表、搜索源文件的目录列表、编译输出的目录列表等。

  3. 变量名是不包括“:”、“#”、“=”、前置空白和尾空白的任何字符串。

  4. 变量名是大小写敏感的。推荐的做法是:在对于内部定义定义的一般变量使用小写方式,而对于一些参数列表(例如:编译选项CFLAGS)采用大写方式。

  5. 自动化变量。像“$<”、“$@”、“$?”、“$*”等。

  6. 变量的引用方式是:“$(VARIABLE_NAME)”或者“${ VARIABLE_NAME }”来引用一个变量的定义。

变量引用我们建议如下:

  1. 引用使用“$(VAR)”格式,无论“VAR”是单字符变量名还是多字符变量名。

  2.  出现在规则命令行中shell变量引用使用shell的“$tmp”格式。

  3. 对出现在命令行中的make变量我们同样使用“$(CMDVAR)” 格式来引用。

递归展开式变量:

  这一类型变量的定义是通过“=”或者使用指示符“define”定义的。在引用的地方是严格的文本替换过程,此变量值的字符串原模原样的出现在引用它的地方。如果此变量定义中存在对其他变量的引用,这些被引用的变量会在它被展开的同时被展开。就是说在变量定义时,变量值中对其他变量的引用不会被替换展开;而是变量在引用它的地方替换展开的同时,它所引用的其它变量才会被一同替换展开。语言的描述可能比较晦涩,让我们来看一个例子:

  foo = $(bar)

  bar = $(ugh)

  ugh = Huh?

  all:;echo $(foo)

执行“make”将会打印出“Huh?”。

其优点是:

这种类型变量在定义时,可以引用其它的之前没有定义的变量

直接展开式变量:

为了避免“递归展开式”变量存在的问题和不方便。GNU make支持另外一种风格的变量,称为“直接展开”式。

  x := foo

  y := $(x) bar

  x := later

  就等价于:

  y := foo bar

  x := later

"?="操作符:

  被称为条件赋值是因为:只有此变量在之前没有赋值的情况下才会对这个变量进行赋值。例如:

  FOO ?= bar

  其等价于:

  ifeq ($(origin FOO), undefined)

  FOO = bar

  endif

含义是:如果变量“FOO”在之前没有定义,就给它赋值“bar”。否则不改变它的值。

变量的替换引用:

格式为“$(VAR:A=B)”(或者“${VAR:A=B}”),意思是,替换变量“VAR”中所有“A”字符结尾的字为“B”结尾的字。例如:

foo := a.o b.o c.o

bar := $(foo:.o=.c)

在这个定义中,变量“bar”的值就为“a.c b.c c.c”。使用变量的替换引用将变量“foo”以空格分开的值中的所有的字的尾字符“o”替换为“c”,其他部分不变。

变量的替换引用其实是函数“patsubst”的一个简化实现。在GNU make中同时提供了这两种方式来实现同样的目的,以兼容其它版本make。

另外一种引用替换的技术使用功能更强大的“patsubst”函数。它的格式和上面“$(VAR:A=B)”的格式相类似,不过需要在“A”和“B”中需要包含模式字符“%”。这时它和“$(patsubst A,B $(VAR))”所实现功能相同。例如:

foo := a.o b.o c.o

bar := $(foo:%.o=%.c)

这个例子同样使变量“bar”的值为“a.c b.c c.c”。这种格式的替换引用方式比第一种方式更通用。

变量的追加:

  objects = main.o foo.o bar.o utils.o

  objects += another.o

上边的两个操作之后变量“objects”的值就为:“main.o foo.o bar.o utils.o another.o”。

overried 指示符:

  如果通过命令行定义了一个变量,那么它将替代在Makefile中出现的同名变量的定义。就是说,对于一个在Makefile中使用常规方式(使用“=”、“:=”或者“define”)定义的变量,我们可以在执行make时通过命令行方式重新指定这个变量的值。

  如果不希望命令行指定的变量值替代在Makefile中的变量定义,那么我们需要在Makefile中使用指示符“override”来对这个变量进行声明,像下边那样:

  override VARIABLE = VALUE

或者:

  override VARIABLE := VALUE

也可以对变量使用追加方式:

  override VARIABLE += MORE TEXT

对于追加方式需要说明的是:变量在定义时使用了“override”,则后续对它值进行追加时,也需要使用带有“override”指示符的追加方式。否则对此变量值的追加不会生效。

  其存在的目的是为了使用户可以改变或者追加那些使用make的命令行指定的变量的定义。

例如:无论命令行指定那些编译参数,编译时必须打开“-g”选项,那么在Makefile中编译选项“CFLAGS”应该这样定义:

  override CFLAGS += -g

这样,在执行make时无论在命令行中指定了那些编译选项(“指定CFLAGS”的值),编译时“-g”参数始终存在。

多行定义

定义变量的另外一种方式是使用“define”指示符。它定义一个包含多行字符串的变量,我们就是利用它的这个特点实现了一个完整命令包的定义。使用“define”定义的命令包可以作为“eval”函数的参数来使用。

本文的前些章节已经不止一次的提到并使用了“define”。相信大家已经有所了解。本节就“define”定义变量从以下几个方面来讨论:

1. “define”定义变量的语法格式:以指示符“define”开始,“endif”结束,之间的所有内容就是所定义变量的值。所要定义的变量名字和指示符“define”在同一行,使用空格分开;指示符所在行的下一行开始一直到“endif”所在行的上一行之间的若干行,是变量值。

  define two-lines

  echo foo

  echo $(bar)

  endef

如果将变量“two-lines”作为命令包执行时,其相当于:

  two-lines = echo foo; echo $(bar)

它把变量“two-lines”的值作为一个完整的shell命令行来处理(是使用分号“;”分开的在同一行中的两个命令而不是作为两个命令行来处理),保证了变量完整。

2. 变量的风格:使用“define”定义的变量和使用“=”定义的变量一样,属于“递归展开”式的变量,两者只是在语法上不同。因此“define”所定义的变量值中,对其它变量或者函数引用不会在定义变量时进行替换展开,其展开是在“define”定义的变量被展开的同时完成的。

3. 可以套嵌引用。因为是递归展开式变量,所以在嵌套引用时“$(x)”将是变量的值的一部分。

4.变量值中可以包含:换行符、空格等特殊符号(注意如果定义中某一行是以[Tab]字符开始时,当引用此变量时这一行会被作为命令行来处理)。

5.可以使用“override”在定义时声明变量:这样可以防止变量的值被命令行指定的值替代。例如:

  override define two-lines

  foo

  $(bar)

  endef

系统环境变量

  make在运行时,系统中的所有环境变量对它都是可见的。在Makefile中,可以引用任何已定义的系统环境变量。(这里我们区分系统环境变量和make的环境变量,系统环境变量是这个系统所有用户所拥有的,而make的环境变量只是对于make的一次执行过程有效,以下正文中出现没有限制的“环境变量”时默认指的是“系统环境变量”,在特殊的场合我们会区分两者)正因为如此,我们就可以设置一个命名为“CFLAGS”的环境变量,用它来指定一个默认的编译选项。就可以在所有的Makefile中直接使用这个变量来对c源代码就行编译。通常这种方式是比较安全的,但是它的前提是大家都明白这个变量所代表的含义,没有人在Makefile中把它作其他的用途。当然了,你也可以在你的Makefile中根据你的需要对它进行重新定义。

使用环境变量需要注意以下几点:

1. 在Makefile中对一个变量的定义或者以make命令行形式对一个变量的定义,都将覆盖同名的环境变量(注意:它并不改变系统环境变量定义,被修改的环境变量只在make执行过程有效)。而make使用“-e”参数时,Makefile和命令行定义的变量不会覆盖同名的环境变量,make将使用系统环境变量中这些变量的定义值。

2. make的递归调用中,所有的系统环境变量会被传递给下一级make。默认情况下,只有环境变量和通过命令行方式定义的变量才会被传递给子make进程。

  我们不推荐使用环境变量的方式来完成普通变量的工作,特别是在make的递归调用中。任何一个环境变量的错误定义都对系统上的所有make产生影响,甚至是毁坏性的。因为环境变量具有全局的特征。所以尽量不要污染环境变量,造成环境变量名字污染。我想大多数系统管理员都明白环境变量对系统是多么的重要。

我们来看一个例子,结束本节。假如我们的机器名为“server-cc”;我们的Makefile内容如下:

 

  # test makefile

  HOSTNAME = server-http

  …………

  …………

  .PHONY : debug

  debug :

         @echo “hostname is : $( HOSTNAME)”

         @echo “shell is $(SHELL)”

1. 执行“make debug”将显示:

hostname is : server-http

shell is /bin/sh

2. 执行“make –e debug”;将显示:

hostname is : server-cc

shell is /bin/sh

3. 执行“make –e HOSTNAEM=server-ftp”;将显示:

hostname is : server-ftp

shell is /bin/sh

记住:除非必须,否则在你的Makefile中不要重置环境变量“SHELL”的值。因为一个不正确的命令行解释程序可能会导致规则定义的命令执行失败,甚至是无法执行!当需要重置它时,必须有充分的理由和配套的规则命令来适应这个新指定的命令行解释程序。

目标指定变量

在Makefile中定义一个变量,那么这个变量对此Makefile的所有规则都是有效的。它就像是一个“全局的”变量。

另外一个特殊的变量定义就是所谓的“目标指定变量(Target-specific Variable)”。此特性允许对于相同变量根据目标指定不同的值,有点类似于自动化变量。目标指定的变量值只在指定它的目标的上下文中有效,对于其他的目标没有影响。就是说目标指定的变量具有只对此目标上下文有效的“局部性”。

设置一个目标指定变量的语法为:

  TARGET ... : VARIABLE-ASSIGNMENT

或者:

  TARGET ... : override VARIABLE-ASSIGNMENT

一个多目标指定的变量的作用域是所有这些目标的上下文,它包括了和这个目标相关的所有执行过程。

目标指定变量的一些特点:

1. “VARIABLE-ASSIGNMENT”可以使用任何一个有效的赋值方式,“=”(递归)、“:=”(静态)、“+=”(追加)或者“?=”(条件)。

2. 使用目标指定变量值时,目标指定的变量值不会影响同名的那个全局变量的值。就是说目标指定一个变量值时,如果在Makefile中之前已经存在此变量的定义(非目标指定的),那么对于其它目标全局变量的值没有变化。变量值的改变只对指定的这些目标可见。

3. 目标指定变量和普通变量具有相同的优先级。就是说,当我们使用make命令行的方式定义变量时,命令行中的定义将替代目标指定的同名变量定义(和普通的变量一样会被覆盖)。另外当使用make的“-e”选项时,同名的环境变量也将覆盖目标指定的变量定义。因此为了防止目标指定的变量定义被覆盖,可以使用第二种格式,使用指示符“override”对目标指定的变量进行声明。

4. 目标指定的变量和同名的全局变量属于两个不同的变量,它们在定义的风格(递归展开式和直接展开式)上可以不同。

5. 目标指定的变量变量会作用到由这个目标所引发的所有的规则中去。例如:

  prog : CFLAGS = -g

  prog : prog.o foo.o bar.o

这个例子中,无论Makefile中的全局变量“CFLAGS”的定义是什么。对于目标“prog”以及其所引发的所有(包含目标为“prog.o”、“foo.o”和“bar.o”的所有规则)规则,变量“CFLAGS”值都是“-g”。

使用目标指定变量可以实现对于不同的目标文件使用不同的编译参数。看一个例子:

  # sample Makefile

  CUR_DIR = $(shell pwd)

  INCS := $(CUR_DIR)/include

  CFLAGS := -Wall –I$(INCS)

  EXEF := foo bar

  .PHONY : all clean

  all : $(EXEF)

  foo : foo.c

  foo : CFLAGS+=-O2

  bar : bar.c

  bar : CFLAGS+=-g

  ………..

  ………..

 

  $(EXEF) : debug.h

  $(CC) $(CFLAGS) $(addsuffix .c,$@) –o $@

 

  clean :

         $(RM) *.o *.d $(EXES)

这个Makefile文件实现了在编译程序“foo”使用优化选项“-O2”但不使用调试选项“-g”,而在编译“bar”时采用了“-g”但没有“-O2”。这就是目标指定变量的灵活之处。目标指定变量的其它特性大家可以修改这个简单的Makefile来进行验证!

模式指定变量

  模式指定变量(Pattern-specific Variable)。使用目标定变量定义时,此变量被定义在某个具体目标和由它所引发的规则的目标上。而模式指定变量定义是将一个变量值指定到所有符合此模式的目标上。对于同一个变量如果使用追加方式,通常对于一个目标,它的局部变量值是:(为所有规则定义的全局值)+(引发它所在规则被执行的目标所指定的值)+(它所符合的模式指定值)+(此目标所指定的值)。这个大家也不需要深入了解。

设置一个模式指定变量的语法和设置目标变量的语法相似:

  PATTERN ... : VARIABLE-ASSIGNMENT

或者:

  PATTERN ... : override VARIABLE-ASSIGNMENT

和目标指定变量语法的唯一区别就是:这里的目标是一个或者多个“模式”目标(包含模式字符“%”)。例如我们可以为所有的.o文件指定变量“CFLAGS”的值:

  %.o : CFLAGS += -O

它指定了所有.o文件的编译选项包含“-O”选项,不改变对其它类型文件的编译选项。

需要说明的是:在使用模式指定的变量定义时。目标文件一般除了模式字符(%)以外需要包含某种文件名的特征字符(例如:“a%”、“%.o”、“%.a”等)。当单独使用“%”作为目标时,指定的变量会对所有类型的目标文件有效。

Makefile编写 二的更多相关文章

  1. Make和Makefile编写(详见GCC手册)

    Makefile和Make Rules 多模块软件.依赖树和Make 默认规则 Make使用程序对简单变量的支持 内建变量 虚目标 特殊目标 一般性语法错误及其纠正措施 命令行的使用和调试 Makef ...

  2. Windows 下的 Makefile 编写

    Windows 下的 Makefile 编写(一)Makefile的基本规则 作者:cntrump Makefile对于很多人来说是陌生的,特别是习惯于使用 IDE 的人来说,似乎没有听说过 Make ...

  3. makefile编写---单个子目录编译模板

    经过这次地库项目之后,虽然时间不久,跟团队在一起,虽然队员不一定在技术上有过人之处,但是来自大公司的员工,在工具使用和代码规范方面还是有点可鉴之处,在搭建主控模块是,就得面临makefile编写,因为 ...

  4. 多文件Makefile编写

    工作过程中,平时不怎么关注Makefile的书写规则,对于遇到的编译错误一般能看懂Makefile的基本规则也能解决.但如果想要编写Makefile文件还是有相当的难度的,更不用说包含多个目录和文件的 ...

  5. Yocto开发笔记之《Makefile编写》(QQ交流群:519230208)

    开了一个交流群,欢迎爱好者和开发者一起交流,转载请注明出处. QQ群:519230208,为避免广告骚扰,申请时请注明 “开发者” 字样 =============================== ...

  6. linux 下C语言编程库文件处理与Makefile编写

    做开发快3年了,在linux下编译安装软件算是家常便饭了.就拿gcc来说,都有不下10次了,可基本每次都会碰到些奇奇怪怪的问题.看来还是像vs.codeblocks这样的ide把人弄蠢了.便下定决心一 ...

  7. 单目录下多文件 makefile编写

    makefile很久就接触过了,但是一直没怎么深入的去学习和总结:在项目中我也只是看看makefile或者修改部分语句,全部自己动手写的话还真没有:知识在于沉淀,这句说的非常好,所以现在把自己理解的东 ...

  8. Makefile学习(二)[第二版]

    复杂实例 #演示样例1:在上一个演示样例的基础上再添加一个可运行文件03test[改动之处已标红] .PHONY: clean all CC = gcc CFLAGS = -Wall -g BIN = ...

  9. linux --> Makefile编写

    Makefile编写 单目录 测试程序在同一个文件中,共有func.h.func.c.main.c三个文件,Makefile写法如下所示: CC = gcc CFLAGS = -g -Wall mai ...

随机推荐

  1. Ubuntu桌面环境安装

    图形程序安装 运行StartX命令检查是否已安装,若提示未安装则执行: sudo apt-get install xinit 环境管理器 GNOME: sudo apt-get install gdm ...

  2. [问题解决]不使用PWM调速系统,彻底解决一个L298N带动两个电机却转速不同的问题

    问题描述:由单片机的VCC引脚供电,使用L298N控制两个电机,发现左右两个轮子的转速老是不一样,更多的情况是左轮转速高(左轮电机接OUT1和OUT2),右轮转速低(右轮电机接OUT3和OUT4)甚至 ...

  3. 通过委托来实现异步 Delegate的BeginInvoke和EndInvoke

    什么是.net的异步机制呢? 解释这个话题之前,先让我们来看看同步执行的程序 https://github.com/chucklu/Test/blob/master/DotNet4.5开发指南/并行处 ...

  4. java bitmap/bitvector的分析和应用

    转自: http://shmilyaw-hotmail-com.iteye.com/blog/1741608  简介     bitmap在很多海量数据处理的情况下会用到.一些典型的情况包括数据过滤, ...

  5. k8s 学习笔记 etcd

    1. Etcd Etcd是Kubernetes集群中的一个十分重要的组件,用于保存集群所有的网络配置和对象的状态信息.在后面具体的安装环境中,我们安装的etcd的版本是v3.1.5,整个kuberne ...

  6. MongoDB 默认写入关注保存数据丢失问题与源码简单分析

    MongoDB 默认写入关注可能保存数据丢失问题分析 问题描述: EDI服务进行优化,将原有MQ发送成功并且DB写入成功,两个条件都达成,响应接收订单数据成功,修改为只有有一个条件成功就响应接收数据成 ...

  7. LA 6891 Money Transfers(最短路)

    https://vjudge.net/problem/UVALive-6891 题意: 给定一个加权无向图,还有起点和终点,现在有个SWERC公司,拥有图中的m个顶点,现在可以使图中的每一条边都加上k ...

  8. windows下hadoop安装配置(转载)

    Windows平台安装配置Hadoop 步骤: 1. JDK安装(不会的戳这) 2. 下载hadoop2.5.2.tar.gz,或者自行去百度下载. 3. 下载hadooponwindows-mast ...

  9. hdu1850nim博弈输出问题

    和之前一道题是类似的,输出第一步走的方法,遍历数组找到a[i]^s<a[i]的那个数a[i]-a[i]^s就是要取的数 #include<map> #include<set&g ...

  10. IOS-常用第三方开源框架介绍

    iOS开发-常用第三方开源框架介绍(你了解的ios只是冰山一角) 时间:2015-05-06 16:43:34      阅读:533      评论:0      收藏:0      [点我收藏+] ...