使用keil、mdk、avr等工具开发时,点点鼠标就可以编译了,但是你是否理解它的内部机制呢?它是怎么组织管理程序的?怎么决定编译哪一个文件的?这些其实都是和Makefile息息相关
一、Makefile作用
说起作用的话,得看看我们平时怎么使用GCC进行编译,比如我们需要编译main.c和sub.c生成可执行程序test, 那么我们有两种方式进行编译:
- 方式一: 直接使用gcc -o test main.c sub.c进行编译
- 方式二:使用gcc -c -o main.o main.c 和gcc -c -o sub.o sub.c生成main.o和sub.o OBJ文件,然后使用gcc -o test main.o sub.o生成可执行文件test
方式一用于源文件比较少的程序,方式二用于源文件比较多的程序。但是如果源文件很多时,我们一条一条输入命令就变得很不高效。这时我们的Makefile就起作用了。
Makefile可以根据文件的更新时间选择性的进行执行相应的命令。
二、Makefile规则
Makefile的书写框架如下:
目标1:依赖1 依赖2 ...
【TAB】命令1
目标2:依赖3 依赖4 ...
【TAB】命令2
......
Makefile的执行情况如下:
- 当"目标文件"不存在或者依赖文件比目标文件更新时间更新时,则执行"命令", 注意如果依赖1和依赖2文件不存在,Makefile会先跳过相关的命令,在下方查找相关依赖文件,如果还是查找不到相关的依赖文件,那么编译报错。
三、Makefile实验
源程序如下:
main.c
#include <stdio.h>
#include "sub.h"
int main(int argc, char *argv[])
{
int i;
printf("Main fun!\n");
sub_fun();
return 0;
}
sub.c
void sub_fun(void)
{
printf("Sub fun!\n");
}
3.1 Makefile文件书写
Makefile文件我们一般是按照命令的执行顺序进行编写,首先第一行是最后执行的命令,如:
gcc -o test main.o sub.o 可见目标文件为test 依赖为 main.o sub.o
则第一部分如下:
test : main.o sub.o
gcc -o test main.o sub.o
随后部分就是依次生成目标文件
生成main.o如下:
main.o : main.c
gcc -c -o main.o main.c
生成sub.o如下:
sub.o : sub.c
gcc -c -o sub.o sub.c
合并之后的Makefile:
test : main.o sub.o
gcc -o test main.o sub.o
main.o : main.c
gcc -c -o main.o main.c
sub.o : sub.c
gcc -c -o sub.o sub.c
Makefile执行过程为,先判断test是否存在,不存在则想执行命令, 发现main.o和sub.o也不存在,,那么会往下查找这两个文件,发现main.o 依赖main.c 那么执行gcc -c -o main.o main.c, 发现sub.o 依赖sub.c 执行gcc -c -o sub.o sub.c, 再返回执行gcc -o test main.o sub.o
如下图为执行的顺序图
接着我们试图修改main.c,再次编译:
编译过程为判断main.o和sub.o有没有更新,发现main.o依赖main.c, main,c有更新,那么需要执行gcc -c -o main.o main.c, 发现sub.o依赖sub.c, 而sub.c没有更新,那么不执行,再发现main.o有更新了, 那么需要执行gcc -o test main.o sub.o 。
为了再次验证makefile是不是根据时间来更新的,我们这里使用ls -l查看文件的时间,和使用touch来更新文件时间,重新编译:
和我们想的一样