陈斌彬的技术博客

Stay foolish,stay hungry

Linux- Makefile 使用

Makefile 简介

读者经常看到一个 C 程序的项目常常由很多的文件组成,那么,多文件的好处到底在哪里呢?一个最简单也最直接有力的理由就是,这样可以将一个大项目分成多个小的部分,独立开来,利于结构化管理。在修改和维护的时候,优势就更明显了。例如,需要对代码做一点小的改动,如果这个项目所有的代码都在一个文件中,那么就要重新编译所有这些代码,这是很耗时的,不仅效率低,而且维护难度更大。但是,如果是多个不同的文件,那么只需要重新编译这些修改过的文件就行了,而且其他源文件的目标文件都已经存在,没有必要重复编译,这样就会快捷很多。

因此,通过合理有效的划分,将一个项目分解为多个易于处理的文件,是非常明智的做法。多文件的管理方式非常正确的选择。

一个工程中的源文件不计其数,按其类型、功能、模块分别放在若干个目录中。makefile 定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至进行更复杂的功能操作(因为 makefile 就像一个 shell 脚本一样,可以执行操作系统的命令)。

makefile 带来的好处就是——“自动化编译”,一但写好,只需要一个 make 命令,整个工程完全编译,极大的提高了软件的开发效率。make 是一个命令工具,是一个及时 makefile 中命令的工具程序。

make 工具最主要也是最基本的功能就是根据 makefile 文件中描述的源程序至今的相互关系来完成自动编译、维护多个源文件工程。而 makefile 文件需要按某种语法进行编写,文件中需要说明如何编译各个源文件并链接生成可执行文件,要求定义源文件之间的依赖关系。

Makefile 基本规则

下面从一个简单实例入手,介绍如何编写 Makefile。假设现在有一个简单的项目由几个文件组成:prog.c、 code.c、 code.h。这些文件的内容如下:

prog.c

#include <stdio.h>
#include "code.h"

int main(void)
{
    int i = 1;      
    printf ("myfun(i) = %d\n", myfun(i));
}

code.c

#include "code.h"

int myfun(int in)
{
    return in + 1;
}

code.h

extern int myfun(int);

这些程序都比较短,结构也很清晰,因此使用下面的命令进行编译:

$ gcc -c code.c -o code.o
$ gcc -c prog.c -o prog.o
$ gcc prog.o code.o -o test

如上所示,这样就能生成可执行文件 test,由于程序比较简单,而且数量也比较少,因此看不出来有多麻烦。但是,试想如果不只上面的3个文件,而是几十个或者是成百上千个甚至更多,那将是非常复杂的问题。

那么如何是好呢?这里就是 makefile 的绝佳舞台,下面是一个简单的 makefile 的例子。

首先 $ vim Makefile

test: prog.o code.o
        gcc prog.o code.o -o test
prog.o: prog.c code.h
        gcc -c prog.c -o prog.o
code.o: code.c code.h
        gcc -c code.c -o code.o
clean:
        rm -f *.o test

有了这个 Makefile,不论什么时候修改源文件,只要执行一下 make 命令,所有必要的重新编译将自动执行。make 程序利用 Makefile 中的数据,生成并遍历以 test 为根节点的树;现在我们以上面的实例,来学习一下 Makefile 的一般写法:

test(目标文件): prog.o code.o(依赖文件列表)
tab(至少一个tab的位置) gcc prog.o code.o -o test(命令)
.......

一个 Makefile 文件主要含有一系列的规则,每条规则包含一下内容:一个目标,即 make 最终需要创建的文件,如可执行文件和目标文件;目标也可以是要执行的动作,如‘clean’;一个或多个依赖文件的列表,通常是编译目标文件所需要的其他文件。之后的一系列命令,是 make 执行的动作,通常是把指定的相关文件编译成目标文件的编译命令,每个命令占一行,并以 tab 开头(初学者务必注意:是 tab,而不是空格) 执行以上 Makefile 后就会自动化编译:

$ make
gcc -c prog.c -o prog.o
gcc -c code.c -o code.o
gcc prog.o code.o -o test

最后就会多产生: porg.o code.o test 这三个文件,执行 ./test 查看结果

还记得 Makefile中 的 clean 吗? make clean 就会去执行 rm -f *.o test 这条命令,完成 clean 操作。

使用带宏的 Makefile

Makefile 还可以定义和使用宏(也称做变量),从而使其更加自动化,更加灵活,在 Makefile 中定义宏的格式为:

macroname = macrotext

使用宏的格式为:

$(macroname)

用 “宏” 的方式,来改写上面的 Makefile 例子

OBJS = prog.o code.o
CC = gcc
test: $(BOJS)
        $(CC) $(OBJS) -o test
prog.o: prog.c code.h
        $(CC) -c prog.c -o prog.o
code.o: code.c code.h
        $(CC) -c code.c -o code.o
clean:
        rm -f *.o test