C/C++ 的项目,通常需要使用 Makefile 来定义编译依赖,这样在有某文件发生变动时,可以依据依赖关系部分编译代码。因此,现在简单的总结下MakeFile 的基本用法。

Makefile 核心写法

  不管 makefile 最后写出来如何的花哨,它最核心的规则仍是三个东西:目标依赖命令。它们的排布方式如下:

目标 ... :   依赖文件   依赖文件 ...

    命令...

    命令...

  这个的意思是,生成目标依赖于 " : " 后面的文件,生成目标的方法是执行下面的命令(命令一个一行)。而这个规则被触发的条件是:

1、目标文件不存在;

2、后面依赖文件的修改日期比目标文件新(make 会自动检查文件日期)。makefile中一定有一个最终目标,一般最终目标就是我们 makefile 文件中的第一个目标.

  假如我们有三个文件,main.c,tools.h,tools.c,其中main.c 中 include 了 tools.h,我们最终要生成可执行文件main,那么这个简单的makefile如下:

main : main.o
    gcc -o main main.o
main.o : main.c tools.h
    gcc -c main.c
tools.o : tools.c tools.h
    gcc -c tools.c

Makefile 中使用变量

  在写makefile中会有很多重复的地方,如果我们需要修改一处,那么重复的地方就都需要修改,这自然是极其愚蠢的,因此我们可以使用变量来帮忙。
  makefile中的变量像C 语言中的宏,我们可以理解为变量在makefile被解析时自动展开成了相应的字符串。
  假如我们有五个文件,main.c,play.c,play.h,tools.c,tools.h。main.c中包含了play.h,play.h中包含了tools.h,最终我们要生成main可执行文件:

OBJ = main.o \
    play.o \
    tools.o
main : $(OBJ)
    gcc -o main $(OBJ)
main.o : main.c play.h
    gcc -c main.c
play.o : play.c tools.h
    gcc -c play.c
tools.o : tools.c tools.h
    gcc -c tools.c
clean :
    rm -rf main $(OBJ)

用MakeFile的自动推导简化脚本

  make是个很强大的工具,它可以自动推导语法,这也就意味着我们在写脚本的时候不用写的那么详细。最明显的自动推导是它知道xxx.o 是通过命令 gcc -c xxx.c -o xxx.o得来,因此我们可以把上面写的那个脚本简化为:

OBJ = main.o \
    play.o \
    tools.o
main : $(OBJ)
    gcc -o main $(OBJ)
main.o : main.c play.h
play.o : play.c tools.h
tools.o : tools.c tools.h
clean :
    rm -rf main $(OBJ)

MakeFile设置搜索的目录

  在通常情况下,我们的 .h头文件 和 .c源文件 以及makefile都不在一个目录下,make只会在当前根路径和系统环境变量路径下搜寻文件,那么如果找不到自然也就报错了,因此我们可以用makefile中的 VPATH和vpath设置make需要搜索的目录。
  其中,VPATH 用 "VPATH = 目录一 : 目录二" 的方式指定,vpath 用 "vpath : %.h 目录" 的方式指定。
  假设还是那五个文件,但是play.h放在play文件夹中,tools.h放在tools文件夹中,.c文件都在根目录,那么makefile写为:

vpath : %.h play
vpath : %.h tools
OBJ = main.o \
    play.o \
    tools.o
main : $(OBJ)
    gcc -o main $(OBJ)
main.o : main.c play.h
play.o : play.c tools.h
tools.o : tools.c tools.h
clean :
    rm -rf main $(OBJ)

MakeFile的伪目标文件

   我们有时会用make执行一些显示的命令,如:make clean,这个clean就属于声明在makefile中的伪目标文件,通常用.PHONY指明,比如上面的我们改为:

vpath : %.h play
vpath : %.h tools
OBJ = main.o \
    play.o \
    tools.o
main : $(OBJ)
    gcc -o main $(OBJ)
main.o : main.c play.h
play.o : play.c tools.h
tools.o : tools.c tools.h
.PHONY : clean
clean :
    rm -rf main $(OBJ)