GUN学习笔记之make变量
来源:互联网 发布:网络大v引导舆情走向 编辑:程序博客网 时间:2024/05/19 06:14
在之前的学习中,我们曾使用到变量,现在,让我们回顾曾在范例中出现的一些变量。其中最简单的变量语法如下:
$(variable-name)
这表明我们想扩展名字为variable-name的变量。变量可以包含任何文本,变量名可以包含多数字符包括点字符(.)。例如,包含C编译命令的COMPILE.c变量。通常,变量名必须放在$()括号里,才能被make识别。一个例外是:单个字符变量名则不需要括号。
通常,makefile定义了许多变量,不过其中有许多变量是make自动定义的。一些变量可以被用户设置,以方便用户控制make的行为,其余的变量则是供make与makefile通信使用的。
自动变量
当规则匹配时,make会定义自动变量。它们提供了对目标和前提条件列表的访问,这样你就不必显示知名任何文件名。这是避免代码重复的有效方式,但更重要的是定义更为一般的模式规则(稍后讨论)。
有六种核心的自动变量($^和$+视为一个):
变量
作用
$@
目标文件名称
$%
归档成员规范的文件名元素
$<
第一个前提条件的文件名称
$?
由空格隔开的,时间比目标新的前提条件的所有文件名称
$^
由空格隔开的,所有的前提条件的文件名称,这个列表删除了重复的文件名,因为,对于多数使用来说,例如编译、复制等,并不想要重复的文件
$+
类似于$^,但包含重复的文件名称,传给连接器时有用
$*
目标的主文件名。文件的主文件名不包含后缀,不要在模式规则以外使用
除此之外,为了与其他make兼容,上面的每个变量都包含两个变体。一个变体是返回值的目录部分。它通过在变量后添加字符D实现,$(@D),$(<D)等。另一个变体返回值的文件部分。它通过在变量后添加字符F实现,$(@F),$(<F)等。注意到,这些变体名字长度超过两个字符,因此必须用放在括号里。GUNmake提供了dir和notdir函数以提高可读性。我们将在后面介绍。
在规则匹配后,make使用目标和前提条件设置自动变量,因此,这些变量只能在该规则的命令脚本中使用。
现在,我们的makefile使用自动变量替代文件名:
count_words: count_words.o counter.o lexer.o -lflgcc $^ -o $@count_words.o: count_words.cgcc -c $<counter.o: counter.cgcc -c $<lexer.o: lexer.cgcc -c $<lexer.c lexer.lflex -t $< > $@
通过VPATH和vpath查找文件
目前,我们的范例非常简单,makefile和源文件位于同一单一目录。现实中程序是复杂的。现在让我们重构先前的范例,进行较为实际的文件布局。我们可以通过将main重构成一个名为counter的函数来修改我们先前的单词计数程序。
<pre name="code" class="plain">#include <lexer.h>#include <counter.h>void counter(int counts[4]){while (yylex())/* nothing to do */;counts[0] = fee_count;counts[1] = fie_count;counts[2] = foe_count;counts[3] = fum_count;}
一个可重用的库函数应该有一个头文件声明,因此,创建counter.h包含我们的声明:
#ifndef COUNTER_H_#define COUNTER_H_extern void counter(int counts[4]);#endif
我们同样将lexer.l符号的声明放在lexer.h中:
#ifndef LEXER_H_#define LEXER_H_extern int fee_count, fie_count, foe_count, fum_count;extern int yylex(void);#endif
在传统的源代码树中,头文件放在include目录中,源文件放在src目录中。我们这样做,并将makefile放在父目录。现在,范例程序的布局如下图1:
图1源代码树
因为我们的源文件现在包含头文件,新产生的依存关系需要记录在makefile中,这样,头文件修改后,相应的目标文件会被更新。
count_words: count_words.o couter.o lexer.o -lflgcc $^ -o $@count_words.o: count_words.c include/counter.hgcc -c $<counter.o: counter.c include/counter.h include/lexer.hgcc -c $<lexer.o: lexer.c include/lexer.hgcc -c $<lexer.c: lexer.lflex -t $< > $@
现在,当执行我们的makefile文件时,得到:
$ makemake:*** No rule to make `counter_words.c', needed by `count_words.o'.Stop.
发生了什么?makefile尝试更新count_words.c,但是这是源文件。让我们来扮演make。我们的第一个前提条件是count_words.o。我们看到不存在这个文件,但有创建它的规则。创建count_words.o引用count_words.c。但是,为什么make没有找到源文件呢?因为源文件在src目录而不在当前目录。除非高诉make,否则make只会在当前目录查找目标和前提条件文件。我们怎样让make查找src目录呢?或者,更为一般的,我们怎样告知make我们的源代码在哪儿?
你可以通过使用VPATH和vpath特性告诉make在不同的目录查找源文件。为了修复我们当前的问题,我们可以在makefile中加入VPATH赋值:
VPATH= src
这表明,如果需要的文件不在当前目录,make应该在src目录查找。现在,我们运行makefile,我们得到:
$ makegcc-c src/count_words.c -o count_words.osrc/count_words.c:2:21:counter.h: No such file or directorymake:*** [count_words.o] Error 1
注意,现在我们可以编译第一个文件,因为填入了源代码的相对路径。使用自动变量的另一个原因是:如果你使用具体的文件名,make不能使用源代码适当的路径。不幸的是,编译失败了因为gcc不能找到头文件。我们可以解决这个问题通过自定义隐含编译规则的-I选项:
CPPFLAGS= -I include
此时的makefile文件如下:
VPATH = src includeCPPFLAGS = -I includecount_words: count_words.o couter.o lexer.o -lflgcc $^ -o $@count_words.o: count_words.c include/counter.hgcc $(CPPFLAGS) -c $<counter.o: counter.c include/counter.h include/lexer.hgcc $(CPPFLAGS) -c $<lexer.o: lexer.c include/lexer.hgcc $(CPPFLAGS) -c $<lexer.c: lexer.lflex -t $< > $@
现在可以成功编译了:
$ makegcc-I include -c src/count_words.c -o count_words.ogcc-I include -c src/counter.c -o counter.oflex-t src/lexer.l > lexer.cgcc-I include -c lexer.c -o lexer.ogcccount_words.o counter.o lexer.o /lib/libfl.a -o count_words
VPATH变量包含make查找文件的目录列表。这个列表目录同时用于目标和前提条件文件的搜索,但是不包含在命令脚本的文件中。在Unix系统上,列表目录由空格和冒号分割;在Windows系统上,列表目录由空格和分号分割。我更倾向使用空格,因为它在所有的系统上都适用,这样可以避免在冒号和分号之间纠结。此外,使用空格更容易阅读。
VPATH很好的解决了上面文件搜索的问题。但是,它也有很大的不足。make会对它需要的文件在每个目录中进行查找。如果一个相同的文件在不同的地方多次出现且都在VPATH列表中,make会丢弃前面的。有时这会导致问题产生。
vpath指令以更精确的方式实现我们的目标。vpath指令的语法如下:
vpath pattern directory-list
因此,我们之前的VPATH可以重写为如下形式:
vpath %.c src
vpath %.h include
现在,我们告诉make应该在src目录下查找.c文件,include目录下查找.h文件(因此,我们可以移除前提条件中的include/)。在更复杂的应用中,这种控制可以解决一些头痛的问题和调试时间。
范例中,我们使用vpath指令解决分布在多个目录的源文件问题。怎样构建应用程序与此类似但又不同,目标文件写入二进制树中,然而源文件保存在源文件树中。正确使用vpath可以解决这个新问题,但是这个任务变得非常复杂,并且单独vpath是不足够。在后面我们会详细的讨论这个问题。
- GUN学习笔记之make变量
- GUN make 学习笔记之make初探
- GUN Make学习笔记
- GUN make学习笔记之make显示规则
- GUN Make学习1
- GUN make
- GUN make
- make之学习笔记
- Make 学习笔记三-使用变量
- GUN make 中文手册
- GUN Make指南
- GUN Make 环境配置
- Make编译学习笔记(二)Makefile中的变量
- Ubuntu 更改 Gun Make 版本
- php5学习笔记之变量
- c++学习笔记之变量
- Python 学习笔记之变量
- Java学习笔记之变量
- DataInputStream的过期方法readLine()
- js_day3
- Android下载repo文件报错
- Shell脚本实现每个工作日定时执行指定程序(学习记录)
- 在Github和Git上fork之简单指南
- GUN学习笔记之make变量
- 医疗博文锚文本
- Linux makefile 教程 非常详细,且易懂
- Android中传递对象的三种方法
- mimetype1
- caret包应用之一:数据预处理
- eclipse hibernate tools安装和入门使用
- Jsoup使用指南
- C#中委托和事件的区别实例解析