Makefile中的变量
2007-11-03 12:03

Makefile中变量有以下几个特征:

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

2. 变量可以用来代表一个文件名列表、编译选项列表、程序运行的选项参数列表、搜索源文件的目录列表、编译输出的目录列表和所有我们能够想到的事物。

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

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

5. 另外有一些变量名只包含了一个或者很少的几个特殊的字符(符号)。称它们为自动化变量。像“$<”、“$@”、“$?”、“$*”等。

(1) 变量的引用

Makefile中在对一些简单变量的引用,我们也可以不使用“()”和“{}”来标记变量名,而直接使用“$x”的格式来实现,此种用法仅限于变量名为单字符的情况。另外自动化变量也使用这种格式。对于一般多字符变量的引用必须使用括号了标记,否则make将把变量名的首字母作为作为变量而不是整个字符串(“$PATH”在Makefile中实际上是“$(P)ATH”)。这一点和shell中变量的引用方式不同。shell中变量的引用可以是“${xx}”或者“$xx”格式。但在Makefile中多字符变量名的引用只能是“$(xx)”或者“${xx}”格式。

(2) 变量的定义

两种风格:递归展开式变量和直接展开式变量。前者前者在引用的地方是严格的文本替换,后者用:=定义,变量值中对其他量或者函数的引用在定义变量时被展开(对变量进行替换),此风格变量在定义时就完成了对所引用变量和函数的展开,因此不能实现对其后定义变量的引用(前者是嫩购实现这个功能的)。如:
CFLAGS := $(include_dirs) -O
include_dirs := -Ifoo -Ibar
由于变量“include_dirs”的定义出现在“CFLAGS”定义之后。因此在“CFLAGS”的定义中,“include_dirs”的值为空。“CFLAGS”的值为“-O”而不是“-Ifoo -Ibar -O”。这一点也是直接展开式和递归展开式变量的不同点。

在复杂的Makefile中,推荐使用直接展开式变量。因为这种风格变量的使用方式和大多数编程语言中的变量使用方式基本上相同。它可以使一个比较复杂的Makefile在一定程度上具有可预测性。而且这种变量允许我们利用之前所定义的值来重新定义它(比如使用某一个函数来对它以前的值进行处理并重新赋值),此方式在Makefile中经常用到。尽量避免和减少递归式变量的使用。

当定义不包含尾空格的变量时,就不能使用这种方式,将变量定义和注释书写在同一行并使用若干空格分开。否则,注释之前的空格会被作为变量值的一部分。例如下边的做法就是不正确的:
dir := /foo/bar # directory to put the frobs in
变量“dir”的值是“/foo/bar ”(后面有4个空格),这可能并不是想要实现的。如果一个文件以它作为路径来表示“$(dir)/file”,那么大错特错了。

“?=”操作符
GNU make中,还有一个被称为条件赋值的赋值操作符“?=”。被称为条件赋值是因为:只有此变量在之前没有赋值的情况下才会对这个变量进行赋值。例如:
FOO ?= bar
其等价于:
ifeq ($(origin FOO), undefined)
FOO = bar
endif
含义是:如果变量“FOO”在之前没有定义,就给它赋值“bar”。否则不改变它的值。

“+=”操作符

使用“+=”操作符,相当于:
objects = main.o foo.o bar.o utils.o
objects := $(objects) another.o

如果被追加值的变量之前没有定义,那么,“+=”会自动变成“=”,此变量就被定义为一个递归展开式的变量。如果之前存在这个变量定义,那么“+=”就继承之前定义时的变量风格。

variable := value
variable += more

就是
variable := value
variable := $(variable) more

variable = value
variable += more
相当于:
temp = value
variable = $(temp) more

(3) 变量的高级用法

1.   变量的替换引用

对于一个已经定义的变量,可以使用“替换引用”将其值中的后缀字符(串)使用指定的字符(字符串)替换。

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

又如:
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
这个例子同样使变量“bar”的值为“a.c b.c c.c”。这种格式的替换引用方式比第一种方式更通用。

使用环境变量需要注意以下几点:
1. 在Makefile中对一个变量的定义或者以make命令行形式对一个变量的定义,都将覆盖同名的环境变量(注意:它并不改变系统环境变量定义,被修改的环境变量只在make执行过程有效)。而make使用“-e”参数时,Makefile和命令行定义的变量不会覆盖同名的环境变量,make将使用系统环境变量中这些变量的定义值。
2. make的递归调用中,所有的系统环境变量会被传递给下一级make。默认情况下,只有环境变量和通过命令行方式定义的变量才会被传递给子make进程。在Makefile中定义的普通变量需要传递给子make时需要使用“export”指示符来对它声明。

3. 一个比较特殊的是环将变量“SHELL”。在系统中这个环境变量的用途是用来指定用户和系统的交互接口,显然对于make是不合适的。因此make的执行环境变量“SHELL”没有使用同名的环境变量定义,而是“/bin/sh”。make默认“/bin/sh”作为它的命令行解释程序(make在执行之前将变量“SHELL”设置为“/bin/sh”)。

gcc -O2 -o $@ $< 意思

-O2表示优化选项,2表示最优优化,即编译器会优化你的程序;-o表示后边接的是文件名称;$@是Makefile的通配符,代指你前面指定的文件名,例如有规则%.o:%.c,那么$@表示xxx.o文件(xxx是你的源代码文件的名称前缀);$<表示搜索到的第一个匹配的文件,对于规则%.o:%.c,$<表示第一个找到的.c文件。简而言之,假设在一个文件夹下有若干.c文件,那么下面的规则:
%.o:%.c
<TAB>gcc -O2 -o $@ $<    #<TAB>表示Tab键
表示把所有的.c文件编译成中间.o文件。
http://zhidao.baidu.com/question/246239567.html

四、嵌套执行make

在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile,这有利于让我们的Makefile变得更加地简洁,而不至于把所有的东西全部写在一个Makefile中,这样会很难维护我们的Makefile,这个技术对于我们模块编译和分段编译有着非常大的好处。

例如,我们有一个子目录叫subdir,这个目录下有个Makefile文件,来指明了这个目录下文件的编译规则。那么我们总控的Makefile可以这样书写:

subsystem:
cd subdir && $(MAKE)

其等价于:

subsystem:
$(MAKE) -C subdir

定义$(MAKE)宏变量的意思是,也许我们的make需要一些参数,所以定义成一个变量比较利于维护。这两个例子的意思都是先进入“subdir”目录,然后执行make命令。

我们把这个Makefile叫做“总控Makefile”,总控Makefile的变量可以传递到下级的Makefile中(如果你显示的声明),但是不会覆盖下层的Makefile中所定义的变量,除非指定了“-e”参数。

如果你要传递变量到下级Makefile中,那么你可以使用这样的声明:

export <variable ...>

GNU组织建议把编译器为每一个源文件的自动生成的依赖关系放到一个文件中,为每一个“name.c”的文件都生成一个“name.d”的Makefile文件,[.d]文件中就存放对应[.c]文件的依赖关系。

于是,我们可以写出[.c]文件和[.d]文件的依赖关系,并让make自动更新或自成[.d]文件,并把其包含在我们的主Makefile中,这样,我们就可以自动化地生成每个文件的依赖关系了。
这里,我们给出了一个模式规则来产生[.d]文件:
%.d: %.c
@set -e; rm -f $@; \
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
这个规则的意思是,所有的[.d]文件依赖于[.c]文件,“rm -f $@”的意思是删除所有的目标,也就是[.d]文件,第二行的意思是,为每个依赖文件“$<”,也就是[.c]文件生成依赖文件,“$@”表示模式“%.d”文件,如果有一个C文件是name.c,那么“%”就是“name”,“$$$$”意为一个随机编号,第二行生成的文件有可能是“name.d.12345”,第三行使用sed命令做了一个替换,关于sed命令的用法请参看相关的使用文档。第四行就是删除临时文件。
总而言之,这个模式要做的事就是在编译器生成的依赖关系中加入[.d]文件的依赖,即把依赖关系:
main.o : main.c defs.h
转成:
main.o main.d : main.c defs.h
于是,我们的[.d]文件也会自动更新了,并会自动生成了,当然,你还可以在这个[.d]文件中加入的不只是依赖关系,包括生成的命令也可一并加入,让每个[.d]文件都包含一个完赖的规则。一旦我们完成这个工作,接下来,我们就要把这些自动生成的规则放进我们的主Makefile中。我们可以使用Makefile的“include”命令,来引入别的Makefile文件(前面讲过),例如:
sources = foo.c bar.c
include $(sources:.c=.d)
上述语句中的“$(sources:.c=.d)”中的“.c=.d”的意思是做一个替换,把变量$(sources)所有[.c]的字串都替换成[.d],关于这个“替换”的内容,在后面我会有更为详细的讲述。当然,你得注意次序,因为include是按次来载入文件,最先载入的[.d]文件中的目标会成为默认目标。
书写命令

Makefile中的变量相关推荐

  1. Makefile中的变量和shell变量

    我们在写makefile时 多多少少会用到shell脚本, 对于变量的在shell中的使用有一些要注意的细节.让我们从一个简单的makefile来看看. 注意makefile中一定要有一个目标,且一定 ...

  2. Linux学习笔记-Makefile中的变量及函数

    Makefile中的变量 此处的变量不是C/C++,Java等中的变量(认为他是一个字符串就阔以了) 一般变量名用大写字母来写,如下所属: SUBDIR = src xml SUBDIR += osa ...

  3. Makefile中打印变量

    一.使用info/warning/error增加调试信息 方法1: $(info, "here add the debug info") 但是此不能打印出.mk的行号 方法2: $ ...

  4. makefile中使用变量

    直接看代码: [xxx@localhost makefile]$ ls makefile test1.cpp test2.cpp [xxx@localhost makefile]$ cat makef ...

  5. 对makefile中,变量定义中 通配符的理解

    见如下例子: LIB=*.oall: $(LIB)@echo $^$(LIB):@echo $@ 执行结果: *.o *.o 所以 转载于:https://www.cnblogs.com/gaojia ...

  6. 【Makefile由浅入深完全学习记录6】Makefile中变量的高级主题上

    抓住基础,学习更多技术,迎接挑战,加qq:1126137994 微信:liu1126137994 一起学习更多技术~ 上一篇文章学习了makefile中的预定义变量的使用,今天来继续学习makefil ...

  7. Makefile中wildcard函数的应用理解

    文章目录 前言 1 "*"通配符使用场景 2 "*"通配符实例 总结 前言 如果我们想定义一系列比较类似的文件,我们很自然地就想起使用通配符.make 支持三种 ...

  8. Makefile中的符号说明

     1   GNU make 在大型的开发项目中,通常有几十到上百个的源文件,如果每次均手工键入 gcc命令进行编译的话,则会 非常不方便.因此,人们通常利用 make工具来自动完成编译工作.这些工 ...

  9. Makefile中export的用法

    在实际的项目中,总不会是只有一个 Makefile 文件,我们会在 Makefile 中调用另外的 Makefile 文件,呈现出 Makefile 的层级结构. 上层 make 过程要将所执行的 M ...

最新文章

  1. 使用js实现微信小页面翻页的原理介绍
  2. BZOJ3133[ballmachine]——倍增+优先队列
  3. 实战Nginx与PHP(FastCGI)的安装、配置与优化
  4. 网页上有错误(类不能支持 Automation 操作)解决方法
  5. MySQL数据库视图(view),视图定义、创建视图、修改视图
  6. 涨薪关键之反射机制,引得项目经理对你的看重,加薪触手可及!!!!
  7. 详解EBS接口开发之采购申请导入
  8. 使用csc手动编译cs文件
  9. dreamwave php,thinkphp+dreamwaver技巧
  10. 3. Longest Substring Without Repeating Characters
  11. linux耳机的检测原理,耳机检测原理介绍
  12. 修改PPT文档属性工具使用教程
  13. Unity ipad UI适配
  14. 国际贸易术语_Incoterm
  15. element upload 上传文件报错status of undefined
  16. activeMQ启动失败61616port被占用问题
  17. 抗击疫情,程序员在家免费学这些!
  18. 实验一:信息隐藏与数字水印
  19. macM1下PD虚拟机中ubuntu安装git过程中apt-get update失败、E: 部分索引文件下载失败等问题
  20. rtx3050和rtx3050ti区别 rtx3050和rtx3050ti显卡什么水平

热门文章

  1. Servlet第二篇【Servlet调用图、Servlet细节、ServletConfig、ServletContext】
  2. JAVA 文件编译执行与虚拟机(JVM)简单介绍
  3. Spring学习使用标签来标记资源(@Component、@Repository、 @Service和@Controller)和用法(包括如何jsp正在使用)...
  4. JQuery ajax()实例
  5. phpize增加php模块
  6. HDU2546_用01背包做
  7. 【笔记】MATLAB中的图形(2)
  8. 区别 (function($){...})(jQuery)、$(function(){ })和$.fn
  9. 数据库系统原理(第5章:数据库编程)
  10. 【记录一下】从0到1 我的python开发之路