Makefile初步-1
2014年03月31日 Linux

在Windows下有诸如vs这样儿“强大”的工具来组织与管理工程,文件层次组织,代码编写,包括选项的设置,编译,链接,都可以在一个窗口中完成(其实背后也是有一个make工具)。Linux下,Unix的哲学,多个不同功能的小工具,组合到一起组成强大的功能,一些小小的文件,我们可以gcc –XXFlags XXOO.c –o XXOO,这样儿来做,但是要是几十个文件,几百个文件呢?还去挨个的输入命令编译?这就是是马上要介绍的make工具,使用make工具,只需要一个命令,就可以构建你的工程。当然,代价就是编写Makefile,make工具就是靠解释Makefile文件来对整个工程进行编译和链接。

这里先给出一个简单的示例,只为说明用法,简单写了几个文件如下:

utils.c,utils.h,display.c,display.h,main.c内容如下(非常简单)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#ifndef _UTILS_H_
#define _UTILS_H_

#include <stdlib.h>

float compute_add(float a, float b);
int get_rand();

#endif
// [utils.c]
#include "utils.h"

float compute_add(float a, float b)
{
return a + b;
}
int get_rand()
{
return rand() % 10;
}
// [display.h]
#ifndef _DISPLAY_H_
#define _DISPLAY_H_

#include <stdio.h>

void print_rand(int rand);
void display_float(float res);

#endif
// [display.c]
#include "display.h"

void print_rand(int rand)
{
printf("rand number: %d\n", rand);
}
void display_float(float res)
{
printf("result : %f\n", res);
}
// [main.c]
#include "display.h"
#include "utils.h"

int main(int argc, char *argv[])
{
print_rand(get_rand());
display_float(compute_add(1.0f, 3.0f));
return 0;
}

如果按最平常的方式,怎么做呢?你肯定想到,先编译utils,display和main为目标文件,再链接三个目标文件,生成main。至少三个命令,有人说了,也不多,那再想,如果我改动了utils文件,那就要再编译utils,再链接main。所以,该Makefile上场了:

1
2
3
4
5
6
7
8
9
10
11
12
13
# makefile for sample
main: main.c utils.o display.o
gcc main.c utils.o display.o -o main

utils.o: utils.h utils.c
gcc -c utils.c -o utils.o

display.o: display.c display.h
gcc -c display.c -o display.o

.PHONY: clean
clean:
rm -f *.o main

这就算是一个小小的Makefile了,慢慢看一下它。

  1. 首先,#号开头,Makefile的注释,作用从行首到行尾,如果想换行,需要加上\转义换行。
  2. 接下来有三个名称然后跟冒号,这三个名称(或者叫文件名)叫做目标,就是要生成的东西,无论是生成的中间文件,还是最终文件,都叫做目标,通常放在第一个位置的都是最终要生成的目标,如本例是main。
  3. 之后冒号后跟了几个空格分隔的名称(文件名),这叫做依赖,看名字就是知道,意思就是目标文件要生成,那么就依赖这几个文件,也就是生成这个目标的必要文件,没有先后顺序。
  4. 然后,每个目标和依赖的下一行都有一行命令,这个命令就是上面的依赖,通过下面的命令生成目标,需要注意的是,命令需要以Tab开头,不然make不会识别,如果Makefile某个部分过长,可以通过\来转到下一行继续。

根据上面几个部分的具体规则,就可以在本目录下通过make命令构建出最终目标了,默认是生成第一个目标文件,想只生成其中的某个目标,可以使用make obj类似的命令,如本例,如只想生成utils.o文件,则可以用make utils.o命令。每次的构建生成,make工具都会检测每个对应的文件是否是最新生成的,如果某个文件不是最新生的,那么make将重新构建对应的目标,如果某个目标的依赖是这个重新构建的目标,那么同样儿的,这个目标也会被重建。make的工作方式就是解释Makefile,以现第一个目标,然后去寻找它的依赖,如果依赖存在,就寻找下一个依赖,如果这个依赖不存在,就去找对应的依赖的作为目标的构建规则,直到生成这个依赖,有点儿像C语言里面的递归。层层下去,然后最终文件都齐全了,生成最终目标。

再看这个小Makefile的最后三行,.PHONY:XXX指示其冒号后的这个目标是伪目标,也就是防止在make XXX的时候,当前目录下正好有一个叫做XXX的文件,那么make就会现错误或者停止工作了。声明这个伪目标,make XXX的时候,就不会出现上述状况了,而这个例子中clean下面的命令,就是清除生成的文件(包括是中间文件和最终目标,像一般IDE里的清理生成,清理解决方案之类的。),make clean之后,生成的是中间和最终文件就全被删除了,再次make,就是重新构建了。

感兴趣的也可以自己组织一个小“工程”,练习一下Makefile的初级使用。