linux 下 c++ makefile学习文档

Makefile

一:Makefile的规则:

target ... prerequisites ... 
            command 
            ... 
            ... 

target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label

prerequisites就是要生成那个target所需要的文件或是目标。

command也就是make需要执行的命令。(任意的Shell命令)

 

这是一个文件的依赖关系,也就是说target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。

 

 

二:一个示例:

edit : main.o kbd.o command.o display.o \ 
           insert.o search.o files.o utils.o 
            cc -o edit main.o kbd.o command.o display.o \ 
                       insert.o search.o files.o utils.o 

    main.o : main.c defs.h 
            cc -c main.c 
    kbd.o : kbd.c defs.h command.h 
            cc -c kbd.c 
    command.o : command.c defs.h command.h 
            cc -c command.c 
    display.o : display.c defs.h buffer.h 
            cc -c display.c 
    insert.o : insert.c defs.h buffer.h 
            cc -c insert.c 
    search.o : search.c defs.h buffer.h 
            cc -c search.c 
    files.o : files.c defs.h buffer.h command.h 
            cc -c files.c 
    utils.o : utils.c defs.h 
            cc -c utils.c 
    clean : 
            rm edit main.o kbd.o command.o display.o \ 
               insert.o search.o files.o utils.o 

 

三:make是如何工作的:

1make会在当前目录下找名字叫“Makefile”或“makefile”的文件。 
2
、如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文件,并把这个文件作为最终的目标文件。 
3
、如果edit文件不存在,或是edit所依赖的后面的 .o 文件的文件修改时间要比edit这个文件新,那么,他就会执行后面所定义的命令来生成edit这个文件。 
4
、如果edit所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程) 
5
、当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件生命make的终极任务,也就是执行文件edit了。

通过上述分析,我们知道,像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令——“make clean”,以此来清除所有的目标文件,以便重编译。 

 

四:makefile中使用变量:


    objects = main.o kbd.o command.o display.o \ 
              insert.o search.o files.o utils.o 

    edit : $(objects) 
            cc -o edit $(objects) 
    main.o : main.c defs.h 
            cc -c main.c 
    kbd.o : kbd.c defs.h command.h 
            cc -c kbd.c 
    command.o : command.c defs.h command.h 
            cc -c command.c 
    display.o : display.c defs.h buffer.h 
            cc -c display.c 
    insert.o : insert.c defs.h buffer.h 
            cc -c insert.c 
    search.o : search.c defs.h buffer.h 
            cc -c search.c 
    files.o : files.c defs.h buffer.h command.h 
            cc -c files.c 
    utils.o : utils.c defs.h 
            cc -c utils.c 
    clean : 
            rm edit $(objects) 

五:让make自动推导:

objects = main.o kbd.o command.o display.o \ 
              insert.o search.o files.o utils.o 

    edit : $(objects) 
            cc -o edit $(objects) 

    main.o : defs.h 
    kbd.o : defs.h command.h 
    command.o : defs.h command.h 
    display.o : defs.h buffer.h 
    insert.o : defs.h buffer.h 
    search.o : defs.h buffer.h 
    files.o : defs.h buffer.h command.h 
    utils.o : defs.h 

    .PHONY : clean 
    clean : 
            rm edit $(objects) 

 

Note:Makefile中的命令,必须要以[Tab]键开始。 

 

 

 

六:Makefile里有什么:

Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。

文件指示:其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。

 

七:Makefile的文件名:

默认的情况下,make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件,找到了解释这个文件。如果要指定特定的Makefile,你可以使用make的“-f”和“--file”参数,如:make -f Make.Linuxmake --file Make.AIX 

 

八:引用其他的Makefile

include <filename>; 

filename可以是当前操作系统Shell的文件模式(可以保含路径和通配符)

Note:include前面可以有一些空字符,但是绝不能是[Tab]键开始。

 

include foo.make *.mk $(bar) 
    
等价于: 

  include foo.make a.mk b.mk c.mk e.mk f.mk 

 

如果你想让make不理那些无法读取的文件,而继续执行,你可以在include前加一个减号“-”。如: 
    -include <filename>; 
    
其表示,无论include过程中出现什么错误,都不要报错继续执行。

 

九:环境变量MAKEFILES

在这里我还是建议不要使用这个环境变量,因为只要这个变量一被定义,那么当你使用make时,所有的Makefile都会受到它的影响,这绝不是你想看到的。在这里提这个事,只是为了告诉大家,也许有时候你的Makefile出现了怪事,那么你可以看看当前环境中有没有定义这个变量。 

 

十:make的工作方式:

make工作时的执行步骤入下:

   1、读入所有的Makefile 
    2
、读入被include的其它Makefile 
    3
、初始化文件中的变量。 
    4
、推导隐晦规则,并分析所有规则。 
    5
、为所有的目标文件创建依赖关系链。 
    6
、根据依赖关系,决定哪些目标要重新生成。 
    7
、执行生成命令。 

十一:书写规则:

规则包含两个部分,一个是依赖关系,一个是生成目标的方法。

在规则中可以使用通配符 :

clean: 
         rm  -f  *.o 

这是在命令中的通配符。

print: *.c 
         lpr -p $? 
         touch print 

这个例子说明了通配符也可以在我们的规则中,目标print依赖于所有的[.c]文件。

objects = *.o 
    
上面这个例子,表示了,通符同样可以用在变量中。并不是说[*.o]会展开,不!objects的值就是“*.o”

如果你要让通配符在变量中展开,也就是让objects的值是所有[.o]的文件名的集合,那么,你可以这样: 
    objects := $(wildcard *.o) 

十二:文件搜索

Makefile文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。

VPATH = src:../headers 
上面的的定义指定两个目录,“src”和“../headers”make会按照这个顺序进行搜索。目录由“冒号”分隔。

 

设置文件搜索路径的方法是使用make的“vpath”关键字(注意,它是全小写的),这不是变量,这是一个make的关键字。

1vpath <pattern>; <directories>; 

    
为符合模式<pattern>;的文件指定搜索目录<directories>; 

 2
vpath <pattern>; 

    
清除符合模式<pattern>;的文件的搜索目录。 

  3
vpath 

    
清除所有已被设置好了的文件搜索目录。

 

vapth使用方法中的<pattern>;需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”结尾的文件。<pattern>;指定了要搜索的文件集,而<directories>;则指定了<pattern>;的文件集的搜索的目录。例如: 

    vpath %.h ../headers 

该语句表示,要求make在“../headers”目录下搜索所有以“.h”结尾的文件。

 

 

十三:伪目标

“伪目标”并不是一个文件,只是一个标签,由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它是否要执行。我们只有通过显示地指明这个“目标”才能让其生效。当然,“伪目标”的取名不能和文件名重名,不然其就失去了“伪目标”的意义了。

 

当然,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”。 
    .PHONY : clean 
只要有这个声明,不管是否有“clean”文件,要运行“clean”这个目标,只有“make clean”这样。于是整个过程可以这样写: 
     .PHONY: clean 
    clean: 
            rm *.o temp 

 

伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为“默认目标”,只要将其放在第一个。一个示例就是,如果你的Makefile需要一口气生成若干个可执行文件,但你只想简单地敲一个make完事,并且,所有的目标文件都写在一个Makefile中,那么你可以使用“伪目标”这个特性: 

    all : prog1 prog2 prog3 
    .PHONY : all 

    prog1 : prog1.o utils.o 
            cc -o prog1 prog1.o utils.o 

    prog2 : prog2.o 
            cc -o prog2 prog2.o 

    prog3 : prog3.o sort.o utils.o 
            cc -o prog3 prog3.o sort.o utils.o 

我们知道,Makefile中的第一个目标会被作为其默认目标。我们声明了一个“all”的伪目标,其依赖于其它三个目标。由于伪目标的特性是,总是被执行的,所以其依赖的那三个目标就总是不如“all”这个目标新。所以,其它三个目标的规则总是会被决议。也就达到了我们一口气生成多个目标的目的。“.PHONY : all”声明了“all”这个目标为“伪目标”。

 

 

十四:多目标

十五:静态模式

十六:自动生成依赖性

 

十七:书写命令

1:显示命令

通常,make会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在命令行前,那么,这个命令将不被make显示出来,最具代表性的例子是,我们用这个功能来像屏幕显示一些信息。如: 

    @echo 
正在编译XXX模块...... 

make执行时,会输出“正在编译XXX模块......”字串,但不会输出命令,如果没有“@”,那么,make将输出: 

    echo 
正在编译XXX模块...... 
    
正在编译XXX模块...... 

如果make执行时,带入make参数“-n”或“--just-print”,那么其只是显示命令,但不会执行命令,这个功能很有利于我们调试我们的Makefile,看看我们书写的命令是执行起来是什么样子的或是什么顺序的。 

make参数“-s”或“--slient”则是全面禁止命令的显示 

 

2.命令执行 
当依赖目标新于目标时,也就是当规则的目标需要被更新时,make会一条一条的执行其后的命令。需要注意的是,如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令。比如你的第一条命令是cd命令,你希望第二条命令得在cd之后的基础上运行,那么你就不能把这两条命令写在两行上,而应该把这两条命令写在一行上,用分号分隔。如: 

    
示例一: 
        exec: 
                cd /home/hchen 
                pwd 

    
示例二: 
        exec: 
                cd /home/hchen; pwd 

当我们执行“make exec”时,第一个例子中的cd没有作用,pwd会打印出当前的Makefile目录,而第二个例子中,cd就起作用了,pwd会打印出“/home/hchen” 

 

3命令出错 
每当命令运行完后,make会检测每个命令的返回码,如果命令返回成功,那么make会执行下一条命令,当规则中所有的命令成功返回后,这个规则就算是成功完成了。如果一个规则中的某个命令出错了(命令退出码非零),那么make就会终止执行当前规则,这将有可能终止所有规则的执行。 

有些时候,命令的出错并不表示就是错误的。例如mkdir命令,我们一定需要建立一个目录,如果目录不存在,那么mkdir就成功执行,万事大吉,如果目录存在,那么就出错了。我们之所以使用mkdir的意思就是一定要有这样的一个目录,于是我们就不希望mkdir出错而终止规则的运行。 

为了做到这一点,忽略命令的出错,我们可以在Makefile的命令行前加一个减号“-”(在Tab键之后),标记为不管命令出不出错都认为是成功的。如: 

   clean: 
            -rm -f *.o 

还有一个全局的办法是,给make加上“-i”或是“--ignore-errors”参数,那么,Makefile中所有命令都会忽略错误。

还有一个要提一下的make的参数的是“-k”或是“--keep-going”,这个参数的意思是,如果某规则中的命令出错了,那么就终目该规则的执行,但继续执行其它规则。

 

十八:使用变量

Makefile中,变量可以使用在“目标”,“依赖目标”,“命令”或是Makefile的其它部分中。 

变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有“:”、“#”、“=”或是空字符(空格、回车等)。变量是大小写敏感的.

1, 变量的基础

变量在声明时需要给予初值,而在使用时,需要给在变量名前加上“$”符号

2,变量中的变量

foo = $(bar) 
    bar = $(ugh) 
    ugh = Huh? 

    all: 
            echo $(foo) 

我们执行“make all”将会打出变量$(foo)的值是“Huh?”( $(foo)的值是$(bar)$(bar)的值是$(ugh)$(ugh)的值是“Huh?”)可见,变量是可以使用后面的变量来定义的。 

 

我们可以使用make中的另一种用变量来定义变量的方法。这种方法使用的是“:=”操作符,如: 

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

其等价于: 

    y := foo bar 
    x := later 

值得一提的是,这种方法,前面的变量不能使用后面的变量,只能使用前面已定义好了的变量。如果是这样: 

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

那么,y的值是“bar”,而不是“foo bar” 

下面再介绍两个定义变量时我们需要知道的,请先看一个例子,如果我们要定义一个变量,其值是一个空格,那么我们可以这样来: 

    nullstring := 
    space := $(nullstring) # end of the line 

nullstring
是一个Empty变量,其中什么也没有,而我们的space的值是一个空格。因为在操作符的右边是很难描述一个空格的,这里采用的技术很管用,先用一个Empty变量来标明变量的值开始了,而后面采用“#”注释符来表示变量定义的终止,这样,我们可以定义出其值是一个空格的变量。请注意这里关于“#”的使用,注释符“#”的这种特性值得我们注意,如果我们这样定义一个变量: 

    dir := /foo/bar    # directory to put the frobs in 

dir
这个变量的值是“/foo/bar”,后面还跟了4个空格,如果我们这样使用这样变量来指定别的目录——“$(dir)/file”那么就完蛋了。

 

还有一个比较有用的操作符是?=”,先看示例: 

    FOO ?= bar 

其含义是,如果FOO没有被定义过,那么变量FOO的值就是“bar”,如果FOO先前被定义过,那么这条语将什么也不做,其等价于: 

    ifeq ($(origin FOO), undefined) 
      FOO = bar 
    endif 

 

posted @ 2012-02-29 19:52  lcyang  阅读(760)  评论(0)    收藏  举报