我们详细看看Makefile中关于变量的语法规则。先看一个简单的例子:

foo = $(bar)
bar = Huh? all:
@echo$(foo)

我们执行make将会打出Huh?。当make读到foo = $(bar)时,确定foo的值是$(bar),但并不立即展开$(bar),然后读到bar = Huh?,确定bar的值是Huh?,然后在执行规则all:的命令列表时才需要展开$(foo),得到$(bar),再展开$(bar),得到Huh?。因此,虽然bar的定义写在foo之后,$(foo)展开还是能够取到$(bar)的值。

这种特性有好处也有坏处。好处是我们可以把变量的值推迟到后面定义,例如:

main.o: main.c
$(CC)$(CFLAGS) $(CPPFLAGS) -c $< CC = gcc
CFLAGS = -O -g
CPPFLAGS = -Iinclude

编译命令可以展开成gcc -O -g -Iinclude -cmain.c。通常把CFLAGS定义成一些编译选项,例如-O、-g等,而把CPPFLAGS定义成一些预处理选项,例如-D、-I等。用=号定义变量的延迟展开特性也有坏处,就是有可能写出无穷递归的定义,例如CFLAGS = $(CFLAGS) -O,或者:

A = $(B)
B = $(A)

当然,make有能力检测出这样的错误而不会陷入死循环。有时候我们希望make在遇到变量定义时立即展开,可以用:=运算符,例如:

x := foo
y := $(x) bar all:
@echo"-$(y)-"

当make读到y :=$(x) bar定义时,立即把$(x)展开,使变量y的取值是foo bar,如果把这两行颠倒过来:

y := $(x) bar
x := foo

那么当make读到y :=$(x) bar时,x还没有定义,展开为空值,所以y的取值是 bar,注意bar前面有个空格。一个变量的定义从=后面的第一个非空白字符开始(从$(x)的$开始),包括后面的所有字符,直到注释或换行之前结束。如果要定义一个变量的值是一个空格,可以这样:

nullstring :=
space := $(nullstring) # end ofthe line

nullstring的值为空,space的值是一个空格,后面写个注释是为了增加可读性,如果不写注释就换行,则很难看出$(nullstring)后面有个空格。

还有一个比较有用的赋值运算符是?=,例如foo ?= $(bar)的意思是:如果foo没有定义过,那么?=相当于=,定义foo的值是$(bar),但不立即展开;如果先前已经定义了foo,则什么也不做,不会给foo重新赋值。

+=运算符可以给变量追加值,例如:

objects = main.o
objects += $(foo)
foo = foo.o bar.o

object是用=定义的,+=仍然保持=的特性,objects的值是main.o$(foo)(注意$(foo)前面自动添一个空格),但不立即展开,等到后面需要展开$(objects)时会展开成main.o foo.o bar.o。

再比如:

objects := main.o
objects += $(foo)
foo = foo.o bar.o

object是用:=定义的,+=保持:=的特性,objects的值是main.o$(foo),立即展开得到main.o (这时foo还没定义),注意main.o后面的空格仍保留。

如果变量还没有定义过就直接用+=赋值,那么+=相当于=。

上一节我们用到了特殊变量$@和$<,这两个变量的特点是不需要给它们赋值,在不同的上下文中它们自动取不同的值。常用的特殊变量有:

•   $@,表示规则中的目标。

•   $<,表示规则中的第一个条件。

•   $?,表示规则中所有比目标新的条件,组成一个列表,以空格分隔。

•   $^,表示规则中的所有条件,组成一个列表,以空格分隔。

例如前面写过的这条规则:

main: main.o stack.o maze.o
gccmain.o stack.o maze.o -o main

可以改写成:

main: main.o stack.o maze.o
gcc$^ -o $@

这样即使以后又往条件里添加了新的目标文件,编译命令也不需要修改,减少了出错的可能。

$?变量也很有用,有时候希望只对更新过的条件进行操作,例如有一个库文件libsome.a依赖于几个目标文件:

libsome.a: foo.o bar.o lose.owin.o
arr libsome.a $?
ranliblibsome.a

这样,只有更新过的目标文件才需要重新打包到libsome.a中,没更新过的目标文件原本已经在libsome.a中了,不必重新打包。

在上一节我们看到make的隐含规则数据库中用到了很多变量,有些变量没有定义(例如CFLAGS),有些变量定义了缺省值(例如CC),我们写Makefile时可以重新定义这些变量的值,也可以在缺省值的基础上追加。以下列举一些常用的变量,请读者体会其中的规律。

AR

静态库打包命令的名字,缺省值是ar。

ARFLAGS

静态库打包命令的选项,缺省值是rv。

AS

汇编器的名字,缺省值是as。

ASFLAGS

汇编器的选项,没有定义。

CC

C编译器的名字,缺省值是cc。

CFLAGS

C编译器的选项,没有定义。

CXX

C++编译器的名字,缺省值是g++。

CXXFLAGS

C++编译器的选项,没有定义。

CPP

C预处理器的名字,缺省值是$(CC) -E。

CPPFLAGS

C预处理器的选项,没有定义。

LD

链接器的名字,缺省值是ld。

LDFLAGS

链接器的选项,没有定义。

TARGET_ARCH

和目标平台相关的命令行选项,没有定义。

OUTPUT_OPTION

输出的命令行选项,缺省值是-o $@。

LINK.o

把.o文件链接在一起的命令行,缺省值是$(CC) $(LDFLAGS) $(TARGET_ARCH)。

LINK.c

把.c文件链接在一起的命令行,缺省值是$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)。

LINK.cc

把.cc文件(C++源文件)链接在一起的命令行,缺省值是$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)。

COMPILE.c

编译.c文件的命令行,缺省值是$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c。

COMPILE.cc

编译.cc文件的命令行,缺省值是$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c。

RM

删除命令的名字,缺省值是rm -f。

常用的make命令行选项:

-n选项只打印要执行的命令,而不会真的执行命令,这个选项有助于我们检查Makefile写得是否正确,由于Makefile不是顺序执行的,用这个选项可以先看看命令的执行顺序,确认无误了再真正执行命令。

-C选项可以切换到另一个目录执行那个目录下的Makefile,比如先退到上一级目录再执行我们的Makefile(假设我们的源代码都放在testmake目录下):

$ cd ..
$ make -C testmake
make: Entering directory`/home/djkings/testmake'
cc -c -o main.o main.c
cc -c -o stack.o stack.c
cc -c -o maze.o maze.c
gcc main.o stack.o maze.o -o main
make: Leaving directory`/home/djkings/testmake'

一些规模较大的项目会把不同的模块或子系统的源代码放在不同的子目录中,然后在每个子目录下都写一个该目录的Makefile,然后在一个总的Makefile中用make -C命令执行每个子目录下的Makefile。例如Linux内核源代码根目录下有Makefile,子目录fs、net等也有各自的Makefile,二级子目录fs/ramfs、net/ipv4等也有各自的Makefile。

在make命令行也可以用=或:=定义变量,如果这次编译我想加调试选项-g,但我不想每次编译都加-g选项,可以在命令行定义CFLAGS变量,而不必修改Makefile编译完了再改回来:

$ make CFLAGS=-g
cc -g -c -o main.o main.c
cc -g -c -o stack.o stack.c
cc -g -c -o maze.o maze.c
gcc main.o stack.o maze.o -o main

如果在Makefile中也定义了CFLAGS变量,则命令行的值覆盖Makefile中的值。

C语言的本质(38)——makefile之变量的更多相关文章

  1. C语言的本质(36)——makefile基础

    除了Hello World这种极简单的程序之外,一般的程序都是由多个源文件编译链接而成的,这些源文件的处理步骤通常用Makefile来管理.makefile带来的好处就是--"自动化编译&q ...

  2. C语言的本质(4)——浮点数的本质与运算

    C语言的本质(4)--浮点数的本质与运算 C语言规定了3种浮点数,float型.double型和long double型,其中float型占4个字节,double型占8个字节,longdouble型长 ...

  3. C语言的本质(28)——C语言与汇编之用汇编写一个Helloword

    为了更加深入理解C语言的本质,我们需要学习一些汇编相关的知识.作为最基本的编程语言之一,汇编语言虽然应用的范围不算很广,但是非常重要.因为它能够完成许多其它语言所无法完成的功能.就拿 Linux 内核 ...

  4. C语言的本质(15)——C语言的函数接口入门

    C语言的本质(15)--C语言的函数接口 函数的调用者和其实现者之间存在一个协议,在调用函数之前,调用者要为实现者提供某些条件,在函数返回时,实现者完成调用者需要的功能. 函数接口通过函数名,参数和返 ...

  5. C语言的本质(7)——C语言运算符大全

    C语言的本质(7)--C语言运算符大全 C语言的结合方向 C语言中各运算符的结合性分为两种,即左结合性(自左至右)和右结合性(自右至左).例如算术运算符的结合性是自左至右,即先左后右.如有表达式 x- ...

  6. Makefile的变量赋值和函数

    在Makefile中的定义的变量,就像是C/C++语言中的宏一样,他代表了一个文本字串,在Makefile中执行的时候其会自动原模原样地 展开在所使用的地方.其与C/C++所不同的是,你可以在Make ...

  7. [转] Makefile 基础 (5) —— Makefile 使用变量

    该篇文章为转载,是对原作者系列文章的总汇加上标注. 支持原创,请移步陈浩大神博客:(最原始版本) http://blog.csdn.net/haoel/article/details/2886 我转自 ...

  8. makefile之变量赋值

    makefile中变量赋值有4种方法: = ,   := ,  += ,  ?= = :直接赋值 变量 = 值 :=   :位置相关赋值 如果右值为一个值,那么它和=没区别,如果右值为变量,那么左边变 ...

  9. JavaScript学习01 语言简介、基本使用和变量声明

    JavaScript语言简介.基本使用和变量声明 JavaScript是网景(Netscape)公司开发的一种基于客户端浏览器.面向对象.事件驱动式的网页脚本语言. JavaScript的前身叫Liv ...

  10. 转载~kxcfzyk:Linux C语言多线程库Pthread中条件变量的的正确用法逐步详解

    Linux C语言多线程库Pthread中条件变量的的正确用法逐步详解   多线程c语言linuxsemaphore条件变量 (本文的读者定位是了解Pthread常用多线程API和Pthread互斥锁 ...

随机推荐

  1. The end of other

    The end of other For language training our Robots want to learn about suffixes. In this task, you ar ...

  2. htpasswd.cgi 网页远程修改gerrit ht 认证的密码文件

    在搭建gerrit系统时,一般都会采用apache的.htacces 认证方法 但trac本身并不提供修改密码的功能,修改密码只能通过htpasswd/htpasswd2命令来进行,这的确是一件相当不 ...

  3. 【转】adb push&pull bug --- Permission denied----不错

    原文网址:http://blog.csdn.net/hengkong_horse/article/details/8708076 问题背景: adb push  E:\\rrr.txt  /data/ ...

  4. 【转】 ubuntu下fastboot找不到devices

    原文网址:http://memory.blog.51cto.com/6054201/1202420 转载补充: 1.首先,手机必须先进入bootloader状态,fastboot devices才能有 ...

  5. bzoj2049-洞穴勘测(动态树lct模板题)

    Description 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好 ...

  6. xagrs

    find /tmp/ -name "*.log" -mtime +4 | xargs -i -t mv {} /home/ find /tmp/ -name "*.log ...

  7. handsontable的核心方法

    1.为handsontable添加钩子方法 addHook(key,callback):key为钩子方法名 <span style="font-size:18px;"> ...

  8. Struts2+Spring+Hibernate 三大框架的合并集成

    这次来看看Struts2+Spring+Hibernate三大框架的整合应用,主要是Spring和Hibernate框架的整合,因为前边已经将Strtus2+Spring整合过了基本一样.  首先看一 ...

  9. Mac截屏快捷键总结

    Mac截屏快捷键总结 1)Command-Shift-3: 将整个屏幕拍下并保存到桌面. 2)Command-Shift-Control-3:     将整个屏幕拍下并保存到剪贴板(Clipboard ...

  10. Swift 全功能的绘图板开发

    要做一个全功能的绘图板,至少要支持以下这些功能: 支持铅笔绘图(画点) 支持画直线 支持一些简单的图形(矩形.圆形等) 做一个真正的橡皮擦 能设置画笔的粗细 能设置画笔的颜色 能设置背景色或者背景图 ...