Makefile学习

2017-10-11  本文已影响0人  fred290

makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译, 甚至进行更复杂的功能操作,因为makefile 像一个Shell 本一样,其中也可以执行操作系统的命令。

makefile规则

makefile的规则,也是makefile最核心的内容:

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

prerequisites中如果有一个以上的文件比 target文件要新的话,command 定义的命令就会被执行。

在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个Tab 键作为开头

Makefile组成

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

最后还值得一提的是,在Makefile中的命令, 必须要以Tab键开始 。

Makefile工作方式

GNU的make工作时的执行步骤如下:

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

1-5 为第一个阶段,6-7为第二个阶段 。 第一个阶段中, 如果定义的变量被使用了,那么make会把其展开在使用的位置。但make并不会完全马上展开 ,make使用的是拖延战术,如果变量出现在依赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。

伪目标

正如下面例子中的“clean”一样,既然我们生成了许多编译文件, 也应该提供一个以清除它们为“目标”的target,以备以后完整地再次编译(如以“make clean”来使用该目标)

clean:
    rm *.o temp

因为, 我们并不生成 “clean”这个文件。“伪目标”并不是一个文件,只是一个标签, 由于“伪目标”不是文件, 所以make无法生成它的依赖关系和决定它是否要执行。 我们只有通过显式地指明这个“目标” 能让其生效。当然,“伪目标”的取名不能和文件名重名,不然其就去了“伪目标”的意义了。
为了避免和文件重名的这种情况, 我们可以使 一个特殊的标记 “.PHONY” 来显式地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”。

.PHONY : clean
clean:
    rm *.o temp

变量

变量在声明时需要给予初值,而在使用时,需要在变量名前加上 $ 号,但是最好用小括号() 或者大括号 {} 将边看给包括起来。 如果你要使用真实的 $ 字符,那么你需要使用 $$ 来表示。

objects = program.o foo.o
program : $(objects)
    cc -o program $(objects)

自动化变量

自动化变量会把模式中所定义的一系列的文件自动地挨个取出,直到所有的符合模式的文件都取完了。这种自动化变量只应出现在规则的命令中。
下面是所有的自动化变量及其说明:

在上述所列出来的自动化变量中。四个变量( $@ 、 $< 、 $% 、 $* )在扩展时只会有一个文件,而另三个的值是一个文件列表。这七个自动化变量还可以取得文件的目录名或者是在当前目录下符合模式的文件名,只需要加上 D 或者 F 字样。。这是GNU make中老版本的特性,在新版本中使 用函数 dir 或者 notdir 就可以做到了。

make的运行

make命令执行后有三个退出码:
0 表示成功执行。
1 如果make运行时出现任何错误,其返回1。
2 如果你使用了make的“-q”选 ,并且make使 一些目标不需要更新,那么返回2。


make的-debug[=<options>]选项, 输出make的调试信息。它有几种不同的级别可供选 , 如果没有参数,
那 是输出最简单的调试信息。下面是<options>的取值:
• a: 也就是all,输出所有的调试信息。(会非常的 )
• b: 也就是basic,只输出简单的调试信息。即输出不需要重编译的目标。
• v: 也就是verbose,在b选项的级别之上。输出的信息包含哪个makefile被解析,不需要被重编译的依赖文件(就是依赖目标)等。
• i: 也就是implicit,输出所有的隐含规则。
• j: 也就是jobs,输出执行规则中命令的详细信息,如命令的PID、返回码等。
• m: 也就是makefile,输出make读取makefile,更新makefile,执行makefile的信息。

TIPS

objects = *.o

但这并不是说 *.o 会展开 ,不!objects的值是 *.o 。Makefile中的变 其实 是C/C++中的宏。 如果你要让通配符在变量中展开 ,也就是让objects的值是所有 .o的文件名的集合,那么可以这样:

objects := $(wildcard *.o)

gcc -M sha1.c
sha1.o: sha1.c /usr/include/stdio.h /usr/include/features.h
/usr/include/sys/cdefs.h /usr/include/bits/wordsize.h
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h
/usr/lib/gcc/x86_64-redhat-linux/4.4.7/include/stddef.h
/usr/include/bits/types.h /usr/include/bits/typesizes.h
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h
/usr/lib/gcc/x86_64-redhat-linux/4.4.7/include/stdarg.h
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h
/usr/include/string.h /usr/include/xlocale.h /usr/include/stdint.h
/usr/include/bits/wchar.h sha1.h

如果使用 -MM 参数,则不会包含标准库的头文件,大多数其它c/c++编译器使用-M参数即可:

gcc -MM sha1.c
sha1.o: sha1.c sha1.h

于是由编译器自动生成的依赖关系,这样一来,不必再手动书写若干文件的依赖关系,而 编译器自动生成了。

exec:
    cd /home/temp
    pwd

示例2:

exec:
    cd /home/temp; pwd

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

clean:
    -rm *.o temp

例外给make加上 -i 或者 --ignore-errors 参数,那么Makefile中所有命令都会忽略错误。而如果一个规则是以 .IGNORE 作为目标的,那么这个规则中的所有命令都会忽略错误。这些是不同级别的防止命令出错的方法,你可以根据你的不同喜好设置。
还有一个要提一下的make的参数的是 -k 或者 --keep-going ,这个参数的意思是:如果某个规则中的命令出错了,那么终止该规则的执行,但继续执行其它规则。

参考网址:http://seisman.info/how-to-write-makefile.html

上一篇 下一篇

猜你喜欢

热点阅读