在windows上面用VS等工具的时候,编译链接都是一键就成的,在linux下使用gcc编译链接感觉很麻烦,特别是文件多的时候。编译链接的过程,以C/C++为例,源文件(.c/.cpp)和头文件(.h)经过编译器,生成目标文件(.o文件),然后目标文件和库函数文件经过链接器,最后生成可执行文件。(dia画成这样了,左边被覆盖的地方分别是Makefile工程文件和make工具)
今天学习了陈浩的《跟我一起写Makefile》,感觉很强大。
一、make工具:
make [option] [目标]
在不指定目标文件时,则创建第一目标,即make在当前目录下搜索到的第一个makefile文件。make使用的默认的规则定义文件是:GUNmakefile(不建议使用)、makefile或Makefile。要指定目标文件时使用 -f选项。
在默认情况下也就是直接输入make命令的时候
1、make会在当前目录下寻找名字为“Makefile”或“makefile”的文件;
2、如果找到,它会找到第一目标文件(target),举个栗子如example文件,并把这个example文件作为最终的目标文件;
3、如果没有找到example文件或者它的依赖文件(如:example.o)的修改时间比example的新,则执行后面的定义的命令来生成example文件;
4、如果example依赖的.o文件也存在,那么make会在当前目录下寻找目标为.o文件的依赖性,如果找到则再根据哪一个规则生成.o文件,递归的运行下去;
5、在C文件和H文件都是存在的情况下,make生成.o文件,然后再用.o文件完成make的终极任务,也就是可执行文件example。
注:对于所定义的命令错误,或是编译不成功,make不会理会,make只管文件的依赖性。make会一层一层的去找文件的依赖性,直到最终编译出第一个目标文件。如果在搜寻过程中被依赖的文件不存在,则make会直接退出并报错!
二、Makefile
0、Makefile的规则语法
target : Prerequisites command .... ....
或是
target : Prerequisites ; command command .... ....
规则包含两部分:一个是依赖关系(Prerequisites),和生成目标的方法(command)。
target是一个目标文件,也就是Makefile要最终生成的文件,Prerequisites是target的依赖文件,command也就是make要执行的命令(任意的shell命令)。依赖文件列白哦中的对象可为文件,也可为另一执行规则的目标。
在makefile文件中规则顺序很重要,因为makefile文件中只应该有一个最终目标,其他目标都是被这个目标所连带进来的。
注:独自一行的命令都要用Tab键开头
例 1:
calculator : main.o add.o subtract.o multiply.o divide.o calculate.o cc -o main.o add.o subtract.o multiply.o divide.o calculate.o main.o : main.c calculate.h cc -c main.c calculate.o : calculate.c add.h subtract.h divide.h multiply.h cc -c calculate.c add.o : add.c cc -c add.c subtract.o : subtract.c cc -c subtract.c divide.o : divide.c cc -c divide.c clean: rm calculator *.o
1、Makefile中的变量
这个Makefile 通过make可生成可执行文件calculator(所有.c和.h文件都存在),感觉很方便哦,无论修改了那个文件,要重新生成时只要make一下就好了,而不用每次都gcc -o....都重新编译一遍。但是上面的Makefile有个缺点,当增加新的文件或者删除文件的时候都要修改好几处地方,非常的不方便,而且那么长一行,看得眼都花了。好在makefile是可以使用变量的,用变量来保存所有的文件,使用变量的时候就像shell的一样$(var)。而当要增加或者删除的时候只要修改一处地方就可以了,就像C语言里的宏一样。
make工具支持4种类型的变量:自定义变量、环境变量、预定义变量、自动变量。
1)自定义变量
变量的命名规则与shell中本地变量的一样,使用方法也差不多,变量名=字符串
对利用自定义变量上例进行改进
例 2:
objects = main.o calculate.o add.o subtract.o divide.o multiply.o calculator : $(objects) cc -o calculator $(objects) main.o : main.c calculate.h cc -c main.c calculate.o : calculate.c add.h subtract.h divide.h multiply.h cc -c calculate.c add.o : add.c cc -c add.c subtract.o : subtract.c cc -c subtract.c divide.o : divide.c cc -c divide.c multiply.o : multiply.c cc -c multiply.c clean: rm calculator $(objetcs)
2)环境变量
make在运行过程中,将环境变量转化为同名同值的make变量,用户也可在Makefile中对这些变量进行重新定义(不建议这样做,当make其他的文件的时候有可能出错)
3)预定义变量
GNU make预定义了一些变量,在Makefile文件中可以直接使用。
再次改进例子
例 3
objects = main.o calculate.o add.o subtract.o divide.o multiply.o calculator : $(objects) $(CC) -o calculator $(objects) main.o : main.c calculate.h $(CC) -c main.c calculate.o : calculate.c add.h subtract.h divide.h multiply.h $(CC) -c calculate.c add.o : add.c $(CC) -c add.c subtract.o : subtract.c $(CC) -c subtract.c divide.o : divide.c $(CC) -c divide.c multiply.o : multiply.c $(CC) -c multiply.c clean: rm calculator $(objetcs)
4)自动变量
由make工具预先定义,具有特定的含义,它的值与规则中的目标和依赖对象有关。
利用自定义变量改进例3
例 4
objects = main.o calculate.o add.o subtract.o divide.o multiply.o calculator : $(objects) $(CC) -o $@ $^ #用$@表示calculator $^表示所有依赖文件 main.o : main.c calculate.h $(CC) -c -o $@ $< #<第一个依赖文件的名称 这里是main.c calculate.o : calculate.c add.h subtract.h divide.h multiply.h $(CC) -c -o $@ $< add.o : add.c $(CC) -c -o $@ $< subtract.o : subtract.c $(CC) -c -o $@ $< divide.o : divide.c $(CC) -c -o $@ $< multiply.o : multiply.c $(CC) -c -o $@ $< clean: rm calculator $(objetcs)
这样子感觉比例2 还麻烦了,都要加个-o 选项和$@,其实这两个可以去掉的,指示为了练习才加了。
2、Makefile的潜规则
1)隐含规则:自动推导
make强大,它可以自动推导文件及依赖文件后面的命令,我们就没必要去在每一个[.o]文件后都写上类似的命令(cc example.c),因为make 会自动识别,并自己推导命令。所以再次修改上面的例子
例 5:
objects = main.o calculate.o add.o subtract.o divide.o multiply.o calculator : $(objects) $(CC) -o $@ $^ #用$@表示calculator $^表示所有依赖文件 main.o : main.c calculate.h calculate.o : calculate.c add.h subtract.h divide.h multiply.h add.o : add.c subtract.o : subtract.c divide.o : divide.c multiply.o : multiply.c .PHONY : clean clean: -rm calculator $(objetcs)
看下make的自动推导,好爽
lean@lean-Aspire:~/workspace/hello,world/src$ make cc -c -o main.o main.c cc -c -o calculate.o calculate.c cc -c -o add.o add.c cc -c -o subtract.o subtract.c cc -c -o divide.o divide.c cc -c -o multiply.o multiply.c cc -o calculator main.o calculate.o add.o subtract.o divide.o multiply.o
上面的.PHONY 表示clean是个伪目标,现在我也不懂伪目标是个什么东西,接着继续往后看吧!
2)后缀规则
后缀规则定义了将具有某后缀的文件(例如.c文件)转换为具有另外一后缀的文件(例如.o文件)的方法。每个后缀规则以两个成对出现的后缀名定义,例如将.c文件转换为.o文件的后缀规则可定义为:
.c.o: gcc -c $<
3、清空目标文件的规则
每个Makefile中都应该写一个清空目标文件(可执行文件和.o文件)的规则,这有利于重编译和保持文件的整洁
一般风格是:
clean : rm calculator $(objects)
更为稳健的做法是:
.PHONY : clean clean : -rm calculator $(objects)
在rm前加了“-”的意思是,可能某些文件有问题不用理会,继续做后面的事就好。
更加灵活的做法:
.PHONY : cleanall cleanall : cleanprogram cleanobj cleanprogram : -rm procgram cleanobj : -rm obj
想要清除不同文件的时候只需
make cleanall #清除所有文件 make cleanobj #清除目标文件 ...
4、文件指示
①文件包含:在一个Makefile中包含另一个Makefile,就像C语言里面的#include一样
②文件的有效性:根据某些情况指示文件是否有效,就像C语言里的预编译#if
③定义多行命令:
。。。
后面的用到了再补充