在c语言学习的初级阶段,我们所写的代码量较少,分装的文件也很少,直接使用gcc编译便能满足我们的大部分需求,然而随着我们学习的深入,代码量越来越多,已经开始做一些工程项目了,项目中包含多个c文件,使用gcc应把所有的文件从头编译一遍,这时候再使用gcc编译就会显得十分麻烦了。这时,就需要一个我们之后做工程项目时要经常用到的一个make工具了。
make工具,我们又称它工程管理器,顾名思义,它可以帮助我们更好更方便的管理我们的工程文件。在我们项目中加入新内容或者我们编译出错时,使用gcc要将这些文件一个一个重复输入编译,对比一下make工具,它能够根据文件的时间戳自动发现更新的文件来减少编译的工作量,减少了编译时间,如果我们的Makefile文件写的好,只需要一个make指令就能解决,属实提高了我们的效率。
例如有一个编译main.c和add.c成可执行文件add,我们第一次make时:
当我修改了一下main.c之后再编译:
可以看见,它没有再编译add.c的那一部分。
Makefile的基本编写
接下来我们开始讲Makefile,Makefile是make读入的唯一配置文件,make通过解析Makefile里面的指令来对项目中的代码进行编译,进而生成可执行文件,但是Makefile文件编写有一定的规则,这也是我们接下来要着重讲的内容。
首先,Makefile的基本编写格式为:
目标:依赖
命令
定义了目标和依赖后在之后的命令行要以Tab键开头,必需严格依照这种格式。在下面我再举两个例子,例子中add为可执行文件,add.o为生成add的二进制文件,add.c为编译成add.o的源代码。
add:add.o
gcc add.o -o add
add.o:add.c
gcc -c add.c -o add.o
为防某些同学不知道-c、-o的作用,我这里再叨唠两句。-c是只编译,不连接生成可执行文件,编译器只将源代码生成.o的二进制文件,“-o filename”可以输出文件”filename“,除此之外对代码进行gdb调试就需要使用-g,-O可以优化编译链接。
我们可以看到生成add可执行文件依赖于add.o,下一行命令紧跟编译生成add倒序编译,先编写二进制.o文件生成可执行文件的语句,再写目标add.o和它的依赖add.c,下面写.c文件生成.o文件的语句。
注意,如果前面的目标文件和后边的目标文件没有依赖关系的话,那么系统会默认只生成第一个目标文件。如果我们输入make后没加其他指令,make会默认的执行第一条语句。所以,我们经常将我们需要生成的目标文件放到第一条去写,这样,我们只需要输入make就可以实现我们的编译工作。
以上讲的主要是有目标有依赖的情况,接下来我再讲一下有目标无依赖的例子。
指令名:
需要执行的指令
在使用这个指令时,我们只需要输入 make 指令名 便可以实现我们想要执行的指令。
若项目中有和指令名同名的可执行文件文件,执行 make 指令名 时,make会以为目标已实现,所以不会执行我们Makefile中的指令。要想使用这条指令的话,我们可以先声明一下。
.PHONY 的作用是声明一个伪指令,它的基本格式为:
.PHONY:指令名
指令名:
需要执行的指令
例如,我想做一个能清除垃圾文件或者其他不想要的文件时,我可以这样定义:
这样,我只需要执行 make clean 便可以实现清除当前项目下的所有 .o文件和生成的可执行文件add。
.PHONY:clean
clean:
rm add *.o
然而,这还是不够简洁,如果我们的工程项目中加入了其他新的文件或者是提出了其他新的要求,我们在Makefile文件中再进行修改还是输入新的指令。这时候变量的优势就体现出来了。
引入变量后的Makefile编写
首先我们可以先预定义一些变量,例如:
CC=gcc
CFLAGS=-c -g -O (gcc的选项)
OBJ=executable file (可执行文件)
OBJS=file1.o file2.o ... (一般为生成可执行文件的二进制文件)
除此之外我们还要知道一些自动变量
$< 第一个依赖文件的名称
$@ 目标文件的完整名称
$^ 所有不重复的目标依赖文件,以空格分开
$:取变量的值;%是所有的同名文件,类似于*
例如我们编译main.c和add.c生成可执行文件add的过程。
在使用变量前:
add:add.o main.o
gcc add.o main.o
add.o:add.c
gcc -c add.c -o add.o
main.o:main.c
gcc -c main.c -o main.o
在使用变量后:
CC=gcc
CFLAGS=-c -g -O
OBJS=add.o main.o
add:$(OBJS)
$(CC) $(OBJS) -o add
$(OBJS):%.o:%.c
$(CC) $(CFLAGS) $^ -o $@
这样我们在后面的工程中添加了新的文件,那么我们只需修改预定义OBJS的那一行就可以了。
可能相较于直接编写它的书写格式比较繁琐,而且文件和依赖不是很直观,在我们文件较多时他的优势他就体现出来了。当然,具体使用哪种方法还是见仁见智,喜欢那个就用那个就好了。
以上就是关于Makefile文件的简单编写,如有不足之处,还望指正。