[转帖]android开发---makefile编写(一)_Android, Python及开发编程讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  Android, Python及开发编程讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 3156 | 回复: 0   主题: [转帖]android开发---makefile编写(一)        下一篇 
jie.liang
注册用户
等级:少校
经验:1003
发帖:77
精华:0
注册:2013-10-11
状态:离线
发送短消息息给jie.liang 加好友    发送短消息息给jie.liang 发消息
发表于: IP:您无权察看 2013-10-18 14:27:45 | [全部帖] [楼主帖] 楼主

忽然发觉应该对android 编译脚本Makefile进行下系统的学习,每天都在使用它对android源码进行编译,但是不知道这个编译的过程和配置是很痛苦的。好了,不多说了;

我跟着How to Write makefile这一本书进行学习;

makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个shell脚本一样,其中也可以执行操作系统的命令。

一、什么是编译?


把代码源文件编译成中间代码文件,这个动作就叫编译,如unix下的.o文件(object file)就是中间代码文件;然后把大量的object文件合成执行文件,这个动作是叫链接;

大多数时候源文件太多,编译生成的中间代码文件太多,而在链接时需明显指出中间目标文件名,很不方便,所以给中间目标文件打个包,windows下叫.lib文件,unix下是.a文件;

二、关于makefile文件


make命令在执行时,需要一个makefile文件告诉make命令怎样去编译和链接程序。

举个例子说明:

[html]

  1. edit : main.o kbd.o command.o display.o \  
  2. insert.o search.o files.o utils.o  
  3. cc -o edit main.o kbd.o command.o display.o \  
  4. insert.o search.o files.o utils.o  
  5. main.o : main.c defs.h  
  6. cc -c main.c  
  7. kbd.o : kbd.c defs.h command.h  
  8. cc -c kbd.c  
  9. command.o : command.c defs.h command.h  
  10. cc -c command.c  
  11. display.o : display.c defs.h buffer.h  
  12. cc -c display.c  
  13. insert.o : insert.c defs.h buffer.h  
  14. cc -c insert.c  
  15. search.o : search.c defs.h buffer.h  
  16. cc -c search.c  
  17. files.o : files.c defs.h buffer.h command.h  
  18. cc -c files.c  
  19. utils.o : utils.c defs.h  
  20. cc -c utils.c  
  21. clean :  
  22. rm edit main.o kbd.o command.o display.o \  
  23. insert.o search.o files.o utils.o  


makefile文件的格式是:

[html]

  1. target ... : prerequisites ...  
  2. command  



target代表目标文件

prerequisites代表生成目标文件所需要的文件或者目标文件,就是依赖文件

command代表执行的命令

当我们执行make命令时,make会找到当前目录下的makefile或者Makefile文件,然后它会找到第一个target,也就是上面例子的edit,把它作为最终文件。(另外clean后面没有依赖文件,所以make不会自动执行,需要make clean去执行),如果edit的依赖文件修改时间要比edit新,那么它就会执行后面的命令,一层一层下去;文件修改了才会从新编译,否则不会,比如我们改变了“command.h”,那么,kdb.o、command.o和files.o都会被重编译,并且,edit会被重链接。

三、makefile中变量的使用


上面的makefile可以看出来.o文件都是多次被使用,这个时候如果加新的.o文件,就需要在好几处加,容易出错。所以这时候就需要变量来。这个时候可以定义一个变量:

[html]

  1. OBJS =  main.o kbd.o command.o display.o \  
  2. insert.o search.o files.o utils.o  



使用的时候就可以直接用$(OBJS)来使用变量就可以了;

在makefile中预定义的变量有:

[html]

  1. $*  :不包含扩展名的目标文件名称。  
  2. $+  :所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。  
  3. $<  :第一个依赖文件的名称。  
  4. $?  :所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚。  
  5. $@  :目标的完整名称。  
  6. $^  :所有的依赖文件,以空格分开,不包含重复的依赖文件。  
  7. $%  :如果目标是归档成员,则该变量表示目标的归档成员名称。例如,如果目标名称为(image.o),则 $@ 为 ,而 $% 为 image.o。  
  8. AR  :归档维护程序的名称,默认值为 ar。  
  9. ARFLAGS  :归档维护程序的选项。  
  10. AS  :汇编程序的名称,默认值为 as。  
  11. ASFLAGS  :汇编程序的选项。  
  12. CC  :C编译器的名称,默认值为 cc。  
  13. CFLAGS  :C编译器的选项。  
  14. CPP  :C 预编译器的名称,默认值为 $(CC) -E。  
  15. CPPFLAGS  :C编译器的选项。  
  16. CXX  :C++编译器的名称,默认值为 g++。  
  17. CXXFLAGS  :C++编译器的选项。  
  18. FC  :FORTRAN编译器的名称,默认值为 f77。  
  19. FFLAGS  :FORTRAN编译器的选项。  


四、make的自动推导


GNU的make可以自动推导文件以及文件依赖关系后面的命令;如:make看一一个what.o文件,就会自动将what.c加入到依赖文件中来,并且cc -c what.c也会推导出来;

结合变量和自动推导的使用,就可以更新一个全新的makefile文件了;

[html]

  1. OBJS =  main.o kbd.o command.o display.o \  
  2. insert.o search.o files.o utils.o  
  3. edit : $(OBJS)  
  4. cc -o edit $(OBJS)  
  5. main.o : defs.h  
  6. kbd.o : defs.h command.h  
  7. command.o : defs.h command.h  
  8. display.o : defs.h buffer.h  
  9. insert.o : defs.h buffer.h  
  10. search.o : defs.h buffer.h  
  11. files.o : defs.h buffer.h command.h  
  12. utils.o : defs.h  
  13. .PHONE : clean  
  14. clean :rm edit $(OBJS)  



这种方法也成为隐晦规则,上面的makefile中,.PHONE代表clean是个伪目标文件,后面会详细解释;

经过进一步的推导,再清新下makefile文件:

[html]

  1. OBJS =  main.o kbd.o command.o display.o \  
  2. insert.o search.o files.o utils.o  
  3. edit : $(OBJS)  
  4. cc -o edit $(OBJS)  
  5. $(OBJS) : defs.h  
  6. kbd.o command.o files.o : command.h  
  7. display.o insert.o search.o files.o : buffer.h  
  8. .PHONE : clean  
  9. clean :  
  10. rm edit $(OBJS)  



但是有个缺点就是依赖关系变得很凌乱,具体要看你的喜好来决定是否这样写了;

五、清空目标文件的规则


每个makefile文件都应该有一个清空目标文件(.o,执行文件)的规则,

[html]

  1. .PHONY : clean  
  2. clean :  
  3. -rm edit $(objects)  


.PHONY意思表示clean是一个“伪目标”,而在rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。

六、Makefile总述


1、makefile文件主要包含五个东西:显式规则,隐晦规则,变量定义,文件指示,注释;

显式规则说明如何生成一个或多个目标文件,是明显指出的要生产的文件,依赖文件,生成的命令;

隐晦规则说明因为自动推导的原因,它可以让makefile文件写的简洁,粗糙,这是由make支持的;

变量不多说;

文件指示包含三个部分,一个是在一个makefile文件中引用另外一个makefile;另一个是根据某些情况制定makefile中的有效部分;还有一种就是定义一个多行的命令(待续);

注释是以#开始,如果要使用#,\#这样转义;

最后还有一个重要的说明,makefile中的命令,必须以Tab键开始;

七、引用其他的makefile文件


makefile中使用include把别的makefile包含进来,make命令开始时,就会寻找include的mk文件,有“-I”或“--include-dir”参数,表示在指定的目录下寻找;

八、环境变量


定义环境变量MAKEFILES;

九、make的工作方式


GNU的make执行不步骤如下:

1、读入所有的mk文件

2、读入所有include的mk文件

3、初始化文件中的变量

4、推导隐晦规则,并分析所有规则

5、为所有目标文件创建依赖关系链

6、根据依赖关系,觉得哪些文件需要生成

7、执行生成命令

1-5步为第一个阶段,6-7为第二个阶段。第一个阶段中,如果定义的变量被使用了,那么,make会把其展开在使用的位置。但make并不会完全马上展开,make使用的是拖延战术,如果变量出现在依赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。

一般来说,make会以UNIX的标准Shell,也就是/bin/sh来执行命令。

十、makefile书写规则/文件搜索


通配符:make支持三个通配符:“*”,“?”和“[...]”;×.o并不代表多个.o文件,$(wildcard *.o)可以表示多个.o文件;

VPATH = src:../headers,也可以在make命令的关键字vpath

十一:伪目标


前面说道clean是一个伪目标。“伪目标”并不是一个文件,只是一个标签,由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它是否要执行。我们只有通过显示地指明这个“目标”才能让其生效。当然,“伪目标”的取名不能和文件名重名,不然其就失去了“伪目标”的意义了。当然,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”。

伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件:

[html]

  1. all : prog1 prog2 prog3  
  2. .PHONY : all  
  3. prog1 : prog1.o utils.o  
  4. cc -o prog1 prog1.o utils.o  
  5. prog2 : prog2.o  
  6. cc -o prog2 prog2.o  
  7. prog3 : prog3.o sort.o utils.o  
  8. cc -o prog3 prog3.o sort.o utils.o  


顺便说下:伪目标同样也是可以被作为依赖文件的;

十二:多目标


Makefile的规则中的目标可以不止一个,其支持多目标,有可能我们的多个目标同时依赖于一个文件,并且其生成的命令大体类似。于是我们就能把其合并起来。当然,多个目标的生成规则的执行命令是同一个,这可能会可我们带来麻烦,不过好在我们的可以使用一个自动化变量“$@”(关于自动化变量,将在后面讲述),这个变量表示着目前规则中所有的目标的集合,这样说可能很抽象,还是看一个例子吧。

[html]

  1. bigoutput littleoutput : text.g  
  2. generate text.g -$(subst output,,$@) > $@  



上述规则等价于:

[html]

  1. bigoutput : text.g  
  2. generate text.g -big > bigoutput  
  3. littleoutput : text.g  
  4. generate text.g -little > littleoutput  


其中,-$(subst output,,$@)中的“$”表示执行一个Makefile的函数,函数名为subst,后面的为参数。关于函数,将在后面讲述。这里的这个函数是截取字符串的意思,“$@”表示目标的集合,就像一个数组,“$@”依次取出目标,并执于命令。

十三、静态目标


静态模式可以更加容易地定义多目标的规则,可以让我们的规则变得更加的有弹性和灵活
。我们还是先来看一下语法:

[html]

  1. <targets ...>: <target-pattern>: <prereq-patterns ...>  
  2. <commands>  
  3. ....  


targets定义了一系列的目标文件,可以有通配符。是目标的一个集合。
target-parrtern是指明了targets的模式,也就是的目标集模式。
prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义。

举个例子说明下:

[html]

  1. objects = foo.o bar.o  
  2. all: $(objects)  
  3. $(objects): %.o: %.c  
  4. $(CC) -c $(CFLAGS) $< -o $@  


target-pattern用%.o表示target都是.o结尾的;<prereq-parrterns>定义成“%.c”,意思是对<target-parrtern>所形成的目标集进行二次定义,其计算方法是,取<target-parrtern>模式中的“%”(也就是去掉了[.o]这个结尾),并为其加上[.c]这个结尾,形成的新集合。

而命令中的“$<”和“$@”则是代表自动化变量,“$<”表示所有的依赖目标集(也就是“foo.c bar.c”),“$@”表示目标集(也就是“foo.o bar.o”)。

再看一个例子:

[html]

  1. files = foo.elc bar.o lose.o  
  2. $(filter %.o,$(files)): %.o: %.c  
  3. $(CC) -c $(CFLAGS) $< -o $@  
  4. $(filter %.elc,$(files)): %.elc: %.el  
  5. emacs -f batch-byte-compile $<  


$(filter %.o, $(files))表示调用makefile的filter函数;这个例子展示了makefile中更大的弹性。

八、自动生成依赖性

在makefile中,我们的依赖关系可以需要一系列的头文件,但是一个大的工程必须知道c文件包含了哪些头文件,并且加入或删除头文件的时候也要小心翼翼的修改makefile文件,很难维护;大多数的c/c++编译器都支持一个-M/-MM命令选项,可以自动寻找头文件,并生成一个依赖关系。如cc -M main.c,会输出main.o : main.c defs.h。

如何把它应用到makefile文件呢,就是为每个.c文件都生成一个.d的makefile文件用于存放依赖关系,这样我们就可以在makefile文件中更新使用这种依赖关系了;

这里给出一个规则来产生.d文件:

[html]

  1. %.d: %.c  
  2. @set -e; rm -f $@; \  
  3. $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \  
  4. sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \  
  5. rm -f $@.$$$$  


这个规则的意思是,所有的[.d]文件依赖于[.c]文件,“rm -f $@”的意思是删除所有的目标,也就是[.d]文件,第二行的意思是,为每个依赖文件“$<”,也就是[.c]文件生成依赖文件,“$@”表示模式“%.d”文件,如果有一个C文件是name.c,那么“%”就是“name”,“$$$$”意为一个随机编号,第二行生成的文件有可能是“name.d.12345”,第三行使用sed命令做了一个替换,关于sed命令的用法请参看相关的使用文档。第四行就是删除临时文件。

总而言之,这个模式要做的事就是在编译器生成的依赖关系中加入[.d]文件的依赖,即把依赖关系:

main.o : main.c defs.h


转成:

main.o main.d : main.c defs.h


于是,我们的[.d]文件也会自动更新了,并会自动生成了,当然,你还可以在这个[.d]文件中加入的不只是依赖关系,包括生成的命令也可一并加入,让每个[.d]文件都包含一个完赖的规则。一旦我们完成这个工作,接下来,我们就要把这些自动生成的规则放进我们的主Makefile中。我们可以使用Makefile的“include”命令,来引入别的Makefile文件(前面讲过),例如:

[html]

  1. sources = foo.c bar.c  
  2. include $(sources:.c=.d)  


上述语句中的“$(sources:.c=.d)”中的“.c=.d”的意思是做一个替换,把变量$(sources)所有[.c]的字串都替换成[.d],关于这个“替换”的内容,在后面我会有更为详细的讲述。当然,你得注意次序,因为include是按次来载入文件,最先载入的[.d]文件中的目标会成为默认目标。




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论