Makefile基础篇(四)——依赖

前言:
   要生成目标,除了执行相关的指令还需要处理目标的依赖关系。

1 目标依赖

  假设工程结构如下,所有文件放于工程根目录中。在main.c中调用了alg.c的函数,也即main.c依赖与alg.c。*.o是由*.c编译得的对象文件,也即*.o依赖于*.c。据此依赖可以写出makefile。

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include "alg.h"

int main(void)
{
int Num1 = 13;
int Num2 = 33;

printf("%d + %d = %d\n", Num1, Num2, Add(Num1, Num2));

return 0;
}
1
2
3
4
5
6
#include "alg.h"

int Add(int Num1, int Num2)
{
return Num1 + Num2;
}
1
extern int Add(int Num1, int Num2);
1
2
3
4
5
6
7
8
9
10
11
12
main.out: main.o alg.o
gcc main.o alg.o -o main.out

main.o: main.c
gcc -c main.c -o main.o

alg.o: alg.c
gcc -c alg.c -o alg.o

clean:
rm -rf *.out
rm -rf *.o

  执行 make 指令即可生成main.out,执行 make clean 指令即可清除*.out和*.o文件。

2 编译效率

  更简单的makefile写法如下。

1
2
main.out: main.c alg.c
gcc main.c alg.c -o main.out

  这种写法虽然简单,但执行效率低下。按此写法,要生成main.out需要编译一次main.c和alg.c,如果还有其他模块依赖alg.c,按这种写法又会编译一次alg.c,因此将其单独作为一个目标可以保证只对其编译一次,生成*.o文件用于其他文件的依赖。另外,将编译过程拆分成多个目标也可以避免gcc指令写的太长,不便于阅读(特别是工程比较庞大,文件比较多时)。
  将编译过程拆分成多个目标,当文件有改动时只需重新编译有改动的文件,特别是对庞大的工程可以大大减少重新编译时间。如果两次执行 make 指令,make会检查文件修改,若没有修改则第二次make什么也不做,并提示。

1
make: `main.out` is up to date.

  如果我们对main.c进行如下修改,修改局部变量的初始值。

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include "alg.h"

int main(void)
{
int Num1 = 23; //Change value.
int Num2 = 33;

printf("%d + %d = %d\n", Num1, Num2, Add(Num1, Num2));

return 0;
}

  再次make一下,可以看到main.o被重新生成但alg.o没有更新,因为alg没有修改。

ChangeValue.png