软件开发中,make通常被视为一种软件构建工具。它通过makefile/Makefile的文件来实现自动化构建。以target来查找相关文件之间的依赖关系(对比文件的修改时间来实现)。
我们假设要生成一个yan的执行程序,有一份main主程序,其次是123代码和一个头文件yan.h。
gcc -o yan main.c yan1.c yan2.c yan3.c
在开发和调试过程中,我们需要不断键入此命令。这样的话有两个缺点:一是耗时,需要不断重复这一个命令;二是如果我们修改了某个文件,此命令会重编译所有文件。
因此我们需要改进一下这些缺点。makefile就出场了。还是上面这个命令,我们来修改一下。
yan:main.c yan1.c yan2.c yan3.c
gcc -o yan main.c yan1.c yan2.c yan3.c
这里面可以分为三个部分:
target:file
command//前方是一个tab空格,必须是tab
首先是target,是我们这次处理的对象,要编译成的yan。冒号“:”后面的部分是依赖关系,也就是生成target所需要的文件。当后面的依赖文件有一个发生变化时,就会触发第三部分command,也就是编译命令。最后就是将代码放进名为makefile/Makefile的文件中。这样,编译的时候只需要输入make即可。
好像已经很不错了哎,只要make就行了。但是如果我们的文件比较多,火车临时添了不少文件。改make的时候貌似也是有点麻烦。
这时候,变量就出来了。变量就是用来简化修改操作的,我们将一起的文件定义为一个变量,然后在后续使用时用变量来代替,简化了后续编写和修改的操作。来看代码。
cc=gcc//make中喜欢这样用,具体查了下就当gcc用就可以了
des=yan
src=main.c yan1.c yan2.c yan3.c
$(des):$(src)
$(cc) -o $(des) $(src)
上述可以看到,我们用三个变量来替换了make中的文件。变量的表示方式就是“$(变量名)”。这样就能简化之后的编写了。
现在第一个问题已经解决掉了。怎么解决一个变化就会重新编译所有文件呢?
分开就好了。
要调整依赖关系的话,首先要了解编译过程。源文件首先编译成目标文件,然后链接成可执行文件。反应出来就是.c文件先转变为.o,然后链接成可执行文件。
另外,头文件在这里面没有办法反应出来。因此我们用此逻辑修改下makefile。
cc=gcc
des=yan
dep=yan.h
obj=main.o yan1.o yan2.o yan3.o
$(des):$(obj)
$(cc) -o $(des) $(obj)
main.o:main.c $(dep)
$(cc) -c main.c
yan1.o:yan1.c $(dep)
$(cc) -c yan1.c
yan2.o:yan2.c $(dep)
$(cc) -c yan2.c
yan3.o:yan3.c $(dep)
$(cc) -c yan3.c
这样修改之后,就可以改变文件的依赖关系。单个文件变化时,只需要编译自己的部分即可,而且将头文件包含了进来,头文件的修改也能体现其中。
上面的make虽然好用,但是貌似有点长,如果文件过多,一个个输入也会很麻烦。有没有更简单的办法?
我们看一下上面,其实下面的一堆就是把.c变成了.o,所以能不能直接把所有.c包含进去编译成.o。当然可以,通配符。
cc=gcc
des=yan
dep=yan.h
obj=main.o yan1.o yan2.o yan3.o
$(des):$(obj)
$(cc) -o $(des) $(obj)
%.o:%.c $(dep)
$(cc) -c $< -o $@
这里面有几个东西要解释一下。首先是“%”,可以理解为所有,也就是只要最后是.c,前面无所谓,能匹配上最后两位就行。所有的o文件依赖于相应的c文件。命令部分的“<”代表依赖关系表中的第一项(^表示依赖关系表中的所有项),也就是%.c。$@代表则是目标,也就是%.o文件。这样编译的话就是将所有的c文件编译成同名的o文件。不用我们一项项指派了。
中间会生成很多中间文件,所以我们也可以在里面借着变量一道完成这个功能。
cc=gcc
des=yan
dep=yan.h
obj=main.o yan1.o yan2.o yan3.o
$(des):$(obj)
$(cc) -o $(des) $(obj)
%.o:%.c $(dep)
$(cc) -c $< -o $@
clean:
rm -rf $(obj) $(des)
这样,我们可以直接执行make clean来进行清理工作。
我们的工程文件会经常发生变化,每次都要自己输进去不是很麻烦吗?makefile已经智能成这样了,能自己找吗?
当然可以。从command就可以看出,makefile中是可以执行shell命令的。而shell中可以进行文件搜寻。那我们把搜寻命令写进去不就行了。说干就干。
cc=gcc
des=yan
dep=$(shell find ./ name "*.h")//shell命令查找当前目录下的所有.h文件
src=$(shell find ./ name "*.c")
obj=$(src:%.c=%.o)
$(des):$(obj)
$(cc) -o $(des) $(obj)
%.o:%.c $(dep)
$(cc) -c $< -o $@
clean:
rm -rf $(obj) $(des)
里面的shell命令就不解释了。主要是有一个src的替换,会将src的c文件都替换成o文件,这一步的原因主要是o文件暂时目录没有,所以我们用同名c文件来进行名字替换。
好啦。
shell:make
编译完成。
shell:make clean
清理完成。
shell:再见啦!