实战Makefile

1. 没有makefile的日子

1.1 手动编译

1.2 文件编译依赖树

2. 快速体验makefile

3. makefile 变量

4. makefile条件编译

5. makefile函数

6. 参考资料

<1>. 没有makefile的日子

1.1 程序是如何编译的?

如果存在这么一个工程目录结构:

.
|-- foo.c
|-- foo.h
|-- main.c

如果想要编译上面的工程,在没有使用makefile的情况下,可能需要使用如下的编译命令:

[email protected]:~/makefile/program$ cc -c foo.c foo.h # 编译foo.o
[email protected]:~/makefile/program$ cc -c main.c foo.h # 编译main.o
[email protected]:~/makefile/program$ cc foo.o main.o -o nomakefile # 生成工程,需要依赖foo.o和main.o
[email protected]:~/makefile/program$ ls
foo.c foo.h foo.h.gch foo.o main.c main.o makefile nomakefile

通过上面的命令生成nomakefile工程编译完成的文件。那么可能比较懒的程序员就在想这个很类似于一个批处理的过程,那么能不能将整个过程编写成脚本的形式,减少命令的输入,makefile就是用来将手动编译自动化的一个工具。

1.2 文件编译依赖树

查看上面的手动编译流程的话,为了生成nomakefile文件,需要存在foo.o文件和main.o文件,如果想要生成foo.o文件,需要编译foo.c,为了生成main.o,那么需要编译main.c文件,如此即生成一个所谓的文件编译依赖树:

实战Makefile

通过手动编译文件的话,显然是将文件全部重新编译,在makefile中如果说依赖的文件没有更新的话,那么目标文件是不会重新编译的。

<2>. 快速体验makefile

还是上面例子如果使用makefile来管理,事情会变得如此简单,首先编写makefile文件:

# makefile comment

OBJECTS=foo.o main.o
hellomakefile:${OBJECTS}
cc ${OBJECTS} -o hellomakefile
foo.o:foo.c foo.h
cc -c foo.c
@echo "compile foo.c foo.h"
main.o:main.c foo.h
cc -c main.c
clean:
rm *.o
完成之后目录:

.

|-- foo.c
|-- foo.h
|-- main.c
`-- makefile
在该目录下执行make命令,make将在当前目录中查找名为makefile或者是Makefile的文件,如果使用的不是这两个名字的话,需要使用make -f your-make-file-name,解析该文件,并生成二进制文件hellomakefile。

查看上面的makefile,可以看出:

1. makefile中可以使用#添加行注释

2. makefile文件笼统的将是一个个规则的集合,每个规则是由目标开始,然后是该目标依赖的文件,下一行以tab键开始,然后是生成目标所需的命令。

目标:依赖文件,以空格隔开

生成目标文件的命令

3. makefile在执行默认会将第一个目标做为最终目标,在这个文件就是hellomakefile,make在执行时将自动生成“最终目标”所依赖的文件,也就是main.o和foo.o文件。

4. makefile中可以定义变量(或者称之为宏),在程序的其他部分中使用,这里也就是变量(宏)OBJECTS。

5. makefile中可以使用内建函数,例如上面中的@echo函数。

<3>. makefile变量

makefile中用户可以自定义变量,例如:OBJECTS=foo.o main.o,那么在下面的makefile中可以使用${OBJECTS}代替该变量定义的值(foo.o,main.o)。另外可以使用默认变量:

[email protected]:表明的是目标文件名,例如在foo.o:foo.c foo.h中[email protected]表示的就是foo.o

$<:表明的是目标所依赖的文件的第一个文件,例如在foo.o:foo.c foo.h表明的是就是foo.c文件。

$?:表明的是所有比目标文件更新的文件的集合,其中使用空格个风格。对于foo.o:foo.c foo.h,如果更新了foo.c文件,那么重新make时,将$?将显示foo.c。这里需要注意的是如果更新了foo.h文件,那么$?将显示foo.c和foo.h文件,主要原因是在foo.c文件中包含了foo.h文件。

<4>. 条件编译

makefile中可以使用类似于编程语言中的跳转命令,如果满足一定的条件,将执行一定的动作。常见的条件编译指令:

ifeq(arg1, arg2) .. endif:如果arg1和arg2相等的话,将执行该区域中的内容。

ifneq(arg1, arg2) .. endif:如果arg1和arg2不相等的话,将执行该区域内的内容。

ifdef ARG .. endif:判定ARG变量是否已经定义

ifndef ARG .. endif:和ifdef相反

上面的语句可以和else一起来使用:

ifneq ("same", "diff")

@echo "same != diff"
else
@echo "same == diff"
endif

<5>. makefile函数

makefile中可以使用内建函数,makefile中的内建函数大多是和字符串相关和目录相关函数。makefile中函数使用$(function-name function-arg),例如$(strip($(STR))),makefile中内建函数大多是和字符串相关的函数,常用函数:strip,finstring等,这些函数和高级语言中的string类的方法是极为类似的,具体可以参考[这里]。

<6>. 参考资料

gun make



1. 本博客中的文章均是个人在学习和项目开发中总结。其中难免存在不足之处 ,欢迎留言指正。 2. 本文版权归作者和博客园共有,转载时,请保留本文链接。