Who are you?

Makefile学习笔记

这篇文档主要是用作《跟我一起写Makefile》教程的笔记,不会面面俱到,只记录关键点。

<!—more—>

[TOC]

概述

  • makefile的最大优点——自动化编译
  • make是一个命令工具,是一个解释makefile中指令的命令工具

程序的编译与链接

  1. 编译(compile):由源文件生成目标文件(.o),只要求
    • 语法正确
    • 函数和变量的声明正确(需要告诉编译器头文件的位置)
  2. 链接(link):使用中间目标文件生成应用程序,也就是可执行文件
    • 主要链接函数和全局变量,也就是要在目标文件中寻找函数的实现
    • 一般情况下,我们都会对中间目标文件进行打包,在windows下形成库文件(library file),也就是.lib文件,而在linux下是Archive File,后缀为.a文件。

Makefile简介

  • make命令执行,需要有一个makefile文件

makefile规则

1
2
target ... : prerequisites ...
command ... ...
  • target :可以是一个object file(目标文件), 也可以是一个执行文件, 还可以是一个标签 (label)。
  • prerequisites :生成该target所依赖的文件和/或target
  • command:该target要执行的命令(任意的shell命令)

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

makefile示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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

几个注意点:

  • 反斜杠(\)是换行符
  • 在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个 Tab 键作为开头
  • clean只是一个label,make不会自动执行其后的命令,这样的方法非常有用,我们可以在一个makefile中定义不用的编译或是和编译无关的命令,如打包备份等
  • 工作流程
    1. make会在当前目录下找名字叫“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 了。
  • make对于所定义的命令的错误,或是编译不成功,不会报错,只有依赖文件找不到时才会报错

Makefile中使用变量

对于重复的字符串,我们可以使用变量避免重复书写。makefile的变量也就是一个字符串,理解成C语言中的宏可能会更好。

  1. 变量定义:

    1
    2
    objects = main.o kbd.o command.o display.o \
    insert.o search.o files.o utils.o
  2. 变量使用:$(objects)

Makefile自动推导

GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令。

只要make看到一个 .o 文件, 它就会自动的把 .c 文件加在依赖关系中, 如果make找到 一个 whatever.o , 那么 whatever.c 就会是 whatever.o 的依赖文件,并且 cc -c whatever.c 也会被推导出来,因而上面命令可以写为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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)

也就是:对应于目标文件的源文件以及相应的编译命令cc -c都不需要显式写出。这就是所谓的隐晦规则

清空目标文件的规则

1
2
3
.PHONY : clean
clean :
-rm edit $(objects)
  • .PHONY表示clean是一个伪目标文件
  • 在 rm 命令前面加了一个小减号的意思 就是, 也许某些文件出现问题, 但不要管, 继续做后面的事。
  • “clean从来都是放在文件的最后”

makefile包含内容

  1. 显式规则。显式规则说明了如何生成一个或多个目标文件。这是由Makefile的书写者明显 指出要生成的文件、文件的依赖文件和生成的命令。
  2. 隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较简略地 书写Makefile,这是由make所支持的。
  3. 变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点像 你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
  4. 文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言 中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的 预编译#if一样;还有就是定义一个多行的命令。
  5. 注释。Makefile中只有行注释, 和UNIX的Shell脚本一样, 其注释是用 # 字符, 这个就 像C/C++中的 // 一样。 如果你要在你的Makefile中使用 # 字符, 可以用反斜框进行转 义,如: # 。

Makefile文件名

  • 建议使用Makefile这个作为文件名
  • 可 以 使 用 别 的 文 件 名 来 书 写Makefile, 比如:“Make.Linux”,等 , 如 果 要 指 定 特定的Makefile, 你可以使用make的-f—file 参数,如: make -f Make.Linuxmake --file Make.AIX

引用其他Makefile

  1. 语法:include filename
  2. 注意点:
    • filename 可以是当前操作系统Shell的文件模式(可以包含路径和通配符)
    • 如果文件都没有指定绝对路径或是相对路径的话,make会 在当前目录下首先寻找,如果当前目录下没有找到,那么,make还会在下面的几个目录下找:
      • 如果make执行时,有 -I 或 –include-dir 参数,那么make就会在这个参数所指定的 目录下去寻找。
      • 如果目录 \/include (一般是: /usr/local/bin 或 /usr/include )存 在的话,make也会去找。
    • -include <filename>:无论include过程中出现什么错误,都不要报错继续执行。

Make工作方式

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

书写规则

参考:跟我一起写Makefile