3.6.2 Makefile变量
By admin
- 2 minutes read - 222 words上面示例的Makefile在实际中是几乎不存在的,因为它过于简单,仅包含两个文件和一个命令,在这种情况下完全不必要编写Makefile 而只需在Shell 中直接输入即可,在实际中使用的Makefile往往是包含很多的文件和命令的,这也是Makefile产生的原因。下面就可给
出稍微复杂一些的Makefile进行讲解:
sunq:kang.o yul.o
Gcc kang.o bar.o -o myprog
kang.o : kang.c kang.h head.h
Gcc –Wall –O -g –c kang.c -o kang.o
yul.o : bar.c head.h
Gcc – Wall –O -g –c yul.c -o yul.o
在这个Makefile中有3 个目标体(target),分别为sunq、kang.o和yul.o,其中第一个目标体的依赖文件就是后两个目标体。如果用户使用命令“make sunq”,则make管理器就是找到sunq目标体开始执行。
这时,make会自动检查相关文件的时间戳。首先,在检查“kang.o”、“yul.o”和“sunq”3 个文件的时间戳之前,它会向下查找那些把“kang.o”或“yul.o”作为目标文件的时间戳。
比如,“kang.o”的依赖文件为“kang.c”、“kang.h”、“head.h”。如果这些文件中任何一个的时间戳比“kang.o”新,则命令“gcc –Wall –O -g –c kang.c -o kang.o”将会执行,从而更新文件“kang.o”。在更新完“kang.o”或“yul.o”之后,make会检查最初的“kang.o”、“yul.o”和“sunq”3 个文件,只要文件“kang.o”或“yul.o”中的任比文件时间戳比“sunq”新,则第二行命令就会被执行。这样,make就完成了自动检查时间戳的工作,开始执行编译工作。
这也就是Make工作的基本流程。
接下来,为了进一步简化编辑和维护Makefile,make允许在Makefile中创建和使用变量。
变量是在Makefile中定义的名字,用来代替一个文本字符串,该文本字符串称为该变量的值。
在具体要求下,这些值可以代替目标体、依赖文件、命令以及makefile文件中其他部分。
在Makefile中的变量定义有两种方式:一种是递归展开方式,另一种是简单方式。
递归展开方式定义的变量是在引用在该变量时进行替换的,即如果该变量包含了对其他变量的应用,则在引用该变量时一次性将内嵌的变量全部展开,虽然这种类型的变量能够很好地完成用户的指令,但是它也有严重的缺点,如不能在变量后追加内容(因为语句:CFLAGS = $(CFLAGS) -O 在变量扩展过程中可能导致无穷循环)。
为了避免上述问题,简单扩展型变量的值在定义处展开,并且只展开一次,因此它不包含任何对其他变量的引用,从而消除变量的嵌套引用。
递归展开方式的定义格式为:VAR=var。
简单扩展方式的定义格式为:VAR:=var。
Make中的变量使用均使用格式为:$(VAR)。
注意:
变量名是不包括“:”、“#”、“=”结尾空格的任何字符串。同时,变量名中包含字母、数字以及下划线以外的情况应尽量避免,因为它们可能在将来被赋予特别的含义。
变量名是大小写敏感的,例如变量名“foo”、“FOO”、和“Foo”代表不同的变量。
推荐在makefile内部使用小写字母作为变量名,预留大写字母作为控制隐含规则参数或用户重载命令选项参数的变量名。
下面给出了上例中用变量替换修改后的Makefile,这里用OBJS代替kang.o和yul.o,用CC代替Gcc,用CFLAGS代替“-Wall -O –g”。这样在以后修改时,就可以只修改变量定义,而不需要修改下面的定义实体,从而大大简化了Makefile维护的工作量。
经变量替换后的Makefile如下所示:
OBJS = kang.o yul.o
CC = Gcc
CFLAGS = -Wall -O -g
sunq : $(OBJS)
$(CC) $(OBJS) -o sunq
kang.o : kang.c kang.h
$(CC) $(CFLAGS) -c kang.c -o kang.o
yul.o : yul.c yul.h
$(CC) $(CFLAGS) -c yul.c -o yul.o
可以看到,此处变量是以递归展开方式定义的。
Makefile 中的变量分为用户自定义变量、预定义变量、自动变量及环境变量。如上例中的OBJS 就是用户自定义变量,自定义变量的值由用户自行设定,而预定义变量和自动变量为通常在Makefile都会出现的变量,其中部分有默认值,也就是常见的设定值,当然用户可以对其进行修改。
预定义变量包含了常见编译器、汇编器的名称及其编译选项。表3.14 列出了Makefile中常见预定义变量及其部分默认值。
表3.14 Makefile中常见预定义变量
命 令 格 式 含 义
AR 库文件维护程序的名称,默认值为ar
AS 汇编程序的名称,默认值为as
CC C编译器的名称,默认值为cc
CPP C预编译器的名称,默认值为$(CC) –E
CXX C++编译器的名称,默认值为g++
FC FORTRAN编译器的名称,默认值为f77
RM 文件删除程序的名称,默认值为rm –f
ARFLAGS 库文件维护程序的选项,无默认值
ASFLAGS 汇编程序的选项,无默认值
CFLAGS C编译器的选项,无默认值
CPPFLAGS C预编译的选项,无默认值
CXXFLAGS C++编译器的选项,无默认值
FFLAGS FORTRAN编译器的选项,无默认值
可以看出,上例中的CC和CFLAGS是预定义变量,其中由于CC没有采用默认值,因此,需要把“CC=Gcc”明确列出来。
由于常见的Gcc编译语句中通常包含了目标文件和依赖文件,而这些文件在Makefile文件中目标体的一行已经有所体现,因此,为了进一步简化Makefile的编写,就引入了自动变量。自动变量通常可以代表编译语句中出现目标文件和依赖文件等,并且具有本地含义(即下一语句中出现的相同变量代表的是下一语句的目标文件和依赖文件)。表3.15 列出了
Makefile中常见自动变量。
表3.15 Makefile中常见自动变量
命 令 格 式 含 义
$* 不包含扩展名的目标文件名称
$+ 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件
$< 第一个依赖文件的名称
$? 所有时间戳比目标文件晚的依赖文件,并以空格分开
$@ 目标文件的完整名称
$^ 所有不重复的依赖文件,以空格分开
$% 如果目标是归档成员,则该变量表示目标的归档成员名称
自动变量的书写比较难记,但是在熟练了之后会非常的方便,请读者结合下例中的自动变量改写的Makefile进行记忆。
OBJS = kang.o yul.o
CC = Gcc
CFLAGS = -Wall -O -g
sunq : $(OBJS)
$(CC) $^ -o $@
kang.o : kang.c kang.h
$(CC) $(CFLAGS) -c $< -o $@
yul.o : yul.c yul.h
$(CC) $(CFLAGS) -c $< -o $@
另外,在Makefile 中还可以使用环境变量。使用环境变量的方法相对比较简单,make在启动时会自动读取系统当前已经定义了的环境变量,并且会创建与之具有相同名称和数值的变量。但是,如果用户在Makefile中定义了相同名称的变量,那么用户自定义变量将会覆盖同名的环境变量。