Makefile学习笔记

1 静态模式:
编译器可以为.c文件生成依赖关系,由于GNU组织建议生成的这个依赖关系存放于同名的.d文件中,所以Makefile可以利用这个特性而无需自己去维护。生成依赖关系的命令:gcc -MM name.c;存放于name.d文件中:name.o:name.c name.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

  查一下sed命令,要彻底消化。

2 在命令之间的空格或空行会被忽略,但假如这些空格或空行是以【TAB】键开始的,则make会认为这是一个空命令。
3 如果要让上一条命令的执行结果应用于下一条命令,则两条命令应该写在同一行上,并用分号隔开。
4 UNIX下的make一般使用系统变量SHELL定义的系统shell来执行命令,如果找不到则在其当前盘符的当前目录下查找,如果仍然找不到则在PATH环境变量定义的路径下查找。
5 make会一条一条地执行命令,每执行一条命令就检查其返回码。如果命令返回0,则表示成功,继续执行下一条;如果命令返回非0,则表示失败,会停止执行makefile。如果想在命令出错之后继续执行,则可以在命令前【TAB】键后加上"-"。譬如,我们想用mkdir创建一个目录,如果该目录不存在则创建,如果存在就不创建,这时mkdir会执行失败。那么可以在"  -mkdir 目录"来创建。
如果是make -i 或 make --ignore-errors是全局设置忽略所有命令的错误;如果一个规则是以“.IGNORE”为目标的,则该会忽略该规则的命令错误。
另外一个参数是make -k或make --keep-going,这个参数的意思是如果一个规则中命令错误,则终止该规则的执行;其他规则还是要继续执行的。
6 make -C 目录 表示进入“目录”执行make
7 总控makefile的变量如果显式声明可以传递到下级makefile中但是不会覆盖下层makefile的变量,除非make命令带“-e”参数
如果想让变量传递到下层makefile中,可以以“export <variable...>”的形式声明变量;如果不想让变量传递到下层makefile中可以“unexport <variable...>”的形式
如果想传递所有的变量则用“export”就可以了,后面不用带任何参数
SHELL系统变量(说明执行命令的shell程序)和MAKEFLAGS系统变量不管有没export都会传递到下层makefile中。MAKEFLAGS变量会存储make的一些参数,譬如在执行“总控makefile”时设置了参数或在上层makefile中定义了该变量,都会传递到下层makefile中。这是一个系统级的环境变量。但参数-C、-f、-h、-o和-W都不会往下层传递。如果在下层makefile中不想包含上层设置的参数,可以如下处理:
    subsystem:

      cd subdir && $(MAKE) MAKEFLAGS=

make参数-w或--print-directory会打印出当前的工作目录。当使用-C进入某个目录执行make时-w事是默认打开的。除非make带有-s或--slient或--no-print-directory参数,才会关闭-w参数。
8 使用命令包。在makefile中,如果出现相同的命令序列,那么可以定义为这个命令序列定义一个变量,成为命令包。譬如
    define run-yacc #用define开始,run-yacc是命令包名

    yacc $(firstword $^) #yacc是一个程序

    mv y.tab.c $@

    endef  #命令包名用endef结束

    实际使用范例如下:

    foo.c : foo.y

      $(run-yacc)   #像使用变量一样使用命令包名

    在上面这个例子中,$^就表示foo.y,$@就表示foo.c,yacc的运行结果总是y.tab.c,这个命令包就是将y.tab.c改名称目标的名字。

9 makefile中的变量相当于C/C++中的宏,在makefile中做一个文本替换,在用到的时候会原模原样地展开。不过在makefile中你可以修改变量的值。变量的命名可以使用字符、数字和下划线(可以用数字开头),但不能包含“:”、“#”、“=”和空字符(空格、回车和制表符等)。变量的命名是大小写敏感的,传统的命名方法推荐全大写,但可能会与系统变量冲突,所以最好使用驼峰记法。
10 变量在创建时需要给予初值,取值时使用“$变量”的形式,但最好采用$(变量)和${变量}的形式。如果想使用“$”字符,要使用“$$”的形式。
11 给变量赋值有两种方式,一种是“=”,一种是“:=”。两者的区别就是前者可以使用后面才赋值的变量,后者只能使用前面赋值的变量。
给变量赋值时可以使用另外一个变量的值,如 foo=$(XX)
使用“=”给变量赋值可以使用后面才定义的变量,如:
    foo=$(XX)

    XX="xx"

   但是使用“:=”赋值就不会使用后面定义的变量的值,如:

    foo=$(XX) foo.c

    XX=xx

   展开时foo的值就是“foo.c”,而不是“xx foo.c”

  使用“=”给变量赋值时要注意的是“陷入死循环”

12 MAKELEVEL系统变量主要用于“嵌套调用make”中,表示当前的make调用层数,譬如0、1、2等
要注意注释符的一个特性,譬如定义一个定义一个变量,如下:
    myPath:=/home    #注释

    那么myPath的值并不只是"/home"这么简单,而是“/home    ”,把空格包含在内,直到注释符处结束。这个特性要记住,也就是说假如在变量赋值语句中有注释,那么注释符就应该在变量值结束的地方开始。

要注意另外一个特殊字符“?=”,这个字符也用在变量赋值中,意思是当前变量是否已经定义,如果未定义,则赋值;如果已定义,则不赋值。譬如:

fun?=foo

相当于:

ifeq ($(origin myname), undefined)

fun:=foo

endif

13 变量的高级用法之一:替换变量中的共有部分

格式:$(var:a=b)或${var:a=b}

这种用法就是将变量var中以a结尾的字串替换为以b结尾。这里的结尾是指a后面是空格或结束符。对于不是以a结尾的则不改变。$(var:%.a=%.b)也起到同样的作用。这一般用于后缀文件的变换中。

譬如:

foo := a.c b.c c.d d.c

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

结果将是koo:=a.o b.o c.d d.o

而koo:=$(foo:%.c=%.o)也得到同样的结果。

14 变量的第二种高级用法就是“把变量的值再当成变量”

譬如:

x = y

y = z

foo = $($(x))

那么foo=$($(x))=$(y)=z。

15 += 可以用来追加变量值。譬如

a = foo.o

a += boo.o

那么a的值就是”foo.o boo.o”,相当于:

a = foo.o

a = $(a) boo.o

+=这种操作相对于上面这种定义来说就比较简洁。

另外,如果变量之前未有定义过,则“+=”就变成了“=”。如果变量已定义过,则“+=”会根据前面定义使用的是“=”或“:=”而相应变化,譬如:

a = foo.o

a += boo.o

相当于

a = foo.o

a = $(a) boo.o

a := foo.o

a += boo.o

相当于

a := foo.o

a :=$(a) boo.o

16 override指示符

在make命令行中定义的参数在makefile中是无法改变的。在makefile中也可以使用override指示符达到这种效果。

override a = foo.o

a+=boo.o

结果a的值还是foo.o,但执行的过程中make并不会报错,所以要当心。但如下的方式还是可以改变a的值的:

override a+=boo.o

结果a的值就变成了foo.o boo.o了。

17 多行变量

可以使用define关键字来定义多行变量,define后面跟变量名,然后换行定义变量值,最后以endef结束。

定义命令包就是用了这种技术。变量值可以是文本、函数或命令。但要记住的是因为makefile中命令以TAB键开始,所以当变量值是命令时也要以TAB键开始。

示例:

define display_your_name

        echo yu

        echo chuwei

endef

还可以在define前加override指示符。

18 环境变量

在嵌套make中,系统环境变量或make命令行中定义的参数会带入到makefile中,如果当前makefile定义了同名的变量则会覆盖这些上层定义的变量,除非make使用了-e参数,那么系统环境变量或命令行中定义的参数会覆盖下层makefile中同名的变量。

上层makefile中的变量要带入下层,需要使用export关键字导出。

19 目标变量

在makefile中,变量一般是整个makefile中都可以访问的,但我们也可以定义“局部变量”,即“目标变量”——Target-specific variable。

这种变量的定义形式如下:

<Target…>: 变量赋值

变量赋值可以使用=,:=,+=,?=等

如果是针对系统环境变量或命令行参数带入的变量则需要使用如下的形式:

<Target…>:override 变量赋值

使用这种形式定义的变量只在目标以及其连带规则中生效,还可以与系统环境变量或命令行参数带入的变量同名。下面举个例子:

prog: CFLAGS=-g

prog:foo.o boo.o 

        $(CC) $(CFLAGS) –o prog foo.o boo.o koo.o

foo.o:foo.c

        $(CC) $(CFLAGS) –c foo.c

koo.o:koo.c

        $(CC) $(CFLAGS) –c koo.c

不管全局的CFLAGS的值是什么,在这些规则中,CFLAGS的值就是-g。

20 模式变量

所谓模式变量,实际上就是针对某个模式定义的目标变量。这种只在GNU的make中有。

譬如:

%.o:CFLAGS=-O

那么所有匹配%.o的目标的规则中,CFLAGS的值都是-O。

同样,这种形式定义的变量也有两种:

<parttern…>: 变量赋值

<parttern…>:override 变量赋值

21 使用条件判断

语法:

条件指令

执行语句

endif

条件指令

执行语句

else

执行语句

endif

 

条件指令可以是ifeq,ifneq,ifdef,ifndef

ifeq和ifneq的条件可以是:

(变量1,变量2)

“变量1”,”变量2”

‘变量1’,’变量2’

“变量1”,’变量2’

‘变量1’,”变量2”

而ifdef和ifndef主要用来判断一个变量是否有定义并赋值,并不会对该变量值进行展开,譬如

a=

b=$(a)

那么ifdef $b的结果为TRUE,而ifdef $a的结果就为FALSE。ifndef与此相反。

22 make有一个环境变量叫做“MAKECMDGOALS” ,如果在make命令后有指定终极目标,那么该环境变量会存储所指定的终极目标,否则就会为空。

posted @ 2012-05-06 00:51  茫茫深海一条鱼  阅读(855)  评论(0编辑  收藏  举报