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

接着 android开发---makefile编写(一)来学习makefile文件的编写;

十四、书写命令


make命令会按顺序的一条一条的执行命令,每条命令以Tab开始,除非命令是紧跟在依赖规则后面的分号后的,make的命令默认是被/bin/sh---unix标准shell解释执行的;

1、显示命令

通常make要把要执行的命令先显示在屏幕上,当我们用“@”字符在命令行前,就不会显示到屏幕上;

2、命令执行

当依赖目标新于目标时,也就是当规则的目标需要被更新时,make会一条一条的执行其后的命令。需要注意的是,如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令。

3、命令出错

每当命令运行完后,make会检测每个命令的返回码,如果命令返回成功,那么make会执行下一条命令,当规则中所有的命令成功返回后,这个规则就算是成功完成了。如果一个规则中的某个命令出错了(命令退出码非零),那么make就会终止执行当前规则,这将有可能终止所有规则的执行。如果想要忽略错误,则需要在命令前面加 “-” 。还有就是make后面的选项-i和-k。

4、嵌套执行make

在一些大的工程中,如android源码工程,我们会把不同模块的源码放在不同的目录下,我们可以在每一个目录下写一个makefile文件;

比如当前目录下有个子目录subDir,其下面有个makefile文件,那么我们总控的makefile文件就可以这样写:

[html]

  1. subsystem  
  2. cd subDir && $(MAKE)  


其等价于:

[html]

  1. subsystem  
  2. $(MAKE) -C subDir  



定义$(MAKE)宏变量的意思是,也许我们的make需要一些参数,所以定义成一个变量比较利于维护。这两个例子的意思都是先进入“subdir”目录,然后执行make命令。

如果总控makefile文件想传递/取消变量到下层makefile文件,可以使用export variable = value/unexport variable。

5、定义命令包

如果makefile中出现一些相同的命令序列,我们可以将它们定义成一个变量;

[html]

  1. define run-yacc  
  2. yacc $(firstword $^)  
  3. mv y.tab.c $@  
  4. endef  


这里,“run-yacc”是这个命令包的名字,其不要和Makefile中的变量重名。在“define”和“endef”中的两行就是命令序列。这个命令包中的第一个命令是运行Yacc程序,因为Yacc程序总是生成“y.tab.c”的文件,所以第二行的命令就是把这个文件改改名字。还是把这个命令包放到一个示例中来看看吧。

[html]

  1. foo.c : foo.y  
  2. $(run-yacc)  


我们可以看见,要使用这个命令包,我们就好像使用变量一样。在这个命令包的使用中,命令包“run-yacc”中的“$^”就是“foo.y”,“$@”就是“foo.c”(有关这种以“$”开头的特殊变量,我们会在后面介绍),make在执行命令包时,命令包中的每个命令会被依次独立执行。

十五、使用变量



变量在声明时需要给予初值,而在使用时,需要给在变量名前加上“$”符号,但最好用小括号“()”或是大括号“{}”把变量给包括起来。如果你要使用真实的“$”字符,那么你需要用“$$”来表示。

变量也可以使用后面语句定义的变量。

[html]

  1. foo = $(bar)  
  2. bar = $(ugh)  
  3. ugh = Huh?  
  4. all:  
  5. echo $(foo)  


这个时候会打印出Huh?,但是这样的语句有一个坏处就是可能导致无限循环,为了避免这种情况出现,可以使用另外一个用变量定义变量的方法,使用“:=”;

[html]

  1. x := foo  
  2. y := $(x) bar  
  3. x := later  


其等价于

[html]

  1. y := foo bar  
  2. x := later  


还有一个比较有用的操作符是“?=”,先看示例:

[html]

  1. FOO ?= bar  


其含义是,如果FOO没有被定义过,那么变量FOO的值就是“bar”,如果FOO先前被定义过,那么这条语将什么也不做。

接下来介绍两种变量的高级用法:

1、这里介绍两种变量的高级使用方法,第一种是变量值的替换。我们可以替换变量中的共有的部分,其格式是“$(var:a=b)”或是“${var:a=b}”,其意思是,把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符”。

[html]

  1. foo := a.o b.o c.o  
  2. bar := $(foo:.o=.c)  



2、第二种高级用法是——“把变量的值再当成变量”。先看一个例子:

[html]

  1. first_second = Hello  
  2. a = first  
  3. b = second  
  4. all = $($a_$b)  



前面我们所讲的在Makefile中定义的变量都是“全局变量”,在整个文件,我们都可以访问这些变量。当然,“自动化变量”除外,如“$<”等这种类量的自动化变量就属于“规则型变量”,这种变量的值依赖于规则的目标和依赖目标的定义。当然,我样同样可以为某个目标设置局部变量,这种变量被称为“Target-specific Variable”,它可以和“全局变量”同名,因为它的作用范围只在这条规则以及连带规则中,所以其值也只在作用范围内有效。而不会影响规则链以外的全局变量的值。成为目标变量。

其语法是:

[html]

  1. <target ...> : <variable-assignment>  
  2. <target ...> : overide <variable-assignment>  


<variable-assignment>可以是前面讲过的各种赋值表达式,如“=”、“:=”、“+=”或是“?=”。第二个语法是针对于make命令行带入的变量,或是系统环境变量。这个特性非常的有用,当我们设置了这样一个变量,这个变量会作用到由这个目标所引发的所有的规则中去。如:

[html]

  1. prog : CFLAGS = -g  
  2. prog : prog.o foo.o bar.o  
  3. $(CC) $(CFLAGS) prog.o foo.o bar.o  
  4. prog.o : prog.c  
  5. $(CC) $(CFLAGS) prog.c  
  6. foo.o : foo.c  
  7. $(CC) $(CFLAGS) foo.c  
  8. bar.o : bar.c  
  9. $(CC) $(CFLAGS) bar.c  


在这个示例中,不管全局的$(CFLAGS)的值是什么,在prog目标,以及其所引发的所有规则中(prog.o foo.o bar.o的规则),$(CFLAGS)的值都是“-g”。

十六:使用函数



函数调用,很像变量的使用,也是以“$”来标识的,其语法如下:

[html]

  1. $(<function> <arguments>)  


或是

[html]

  1. ${<function> <arguments>} 



对make中的函数进行分类:

字符串处理函数:subst,patsubst,strip,findstring,filter,filter-out,sort,word,wordlist,words,firstword;

文件名操作函数:dir,notdir,suffix,basename,addsuffix,addprefix,join

foreach,if,call函数

orgin函数:undefine,default,file,command line,override,automatic

shell函数:不像其它的函数。顾名思义,它的参数应该就是操作系统Shell的命令。它和反引号“`”是相同的功能。这就是说,shell函数把执行操作系统命令后的输出作为函数返回。于是,我们可以用操作系统命令以及字符串处理命令awk,sed等等命令来生成一个变量,如:

[html]

  1. contents := $(shell cat foo)  
  2. files := $(shell echo *.c)  


注意,这个函数会新生成一个Shell程序来执行命令,所以你要注意其运行性能,如果你的Makefile中有一些比较复杂的规则,并大量使用了这个函数,那么对于你的系统性能是有害的。特别是Makefile的隐晦的规则可能会让你的shell函数执行的次数比你想像的多得多。

控制make的函数:error,warning。

十七:make的运行



make命令执行后有三个退出码:

0 - 表示成功执行。

1 - 如果make运行时出现任何错误,其返回1。

2 - 如果你使用了make的“-q”选项,并且make使得一些目标不需要更新,那么返回2。

十八、隐含规则


如果要使用隐含规则生成你需要的目标,你所需要做的就是不要写出这个目标的规则。那么,make会试图去自动推导产生这个目标的规则和命令,如果make可以自动推导生成这个目标的规则和命令,那么这个行为就是隐含规则的自动推导。当然,隐含规则是make事先约定好的一些东西。例如:

[html]

  1. foo : foo.o bar.o  
  2. cc –o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)  


我们可以注意到,这个Makefile中并没有写下如何生成foo.o和bar.o这两目标的规则和命令。因为make的“隐含规则”功能会自动为我们自动去推导这两个目标的依赖目标和生成命令。

还有,在make的“隐含规则库”中,每一条隐含规则都在库中有其顺序,越靠前的则是越被经常使用的,所以,这会导致我们有些时候即使我们显示地指定了目标依赖,make也不会管。如下面这条规则(没有命令):

[html]

  1. foo.o : foo.p  


依赖文件“foo.p”(Pascal程序的源文件)有可能变得没有意义。如果目录下存在了“foo.c”文件,那么我们的隐含规则一样会生效,并会通过“foo.c”调用C的编译器生成foo.o文件。因为,在隐含规则中,Pascal的规则出现在C的规则之后,所以,make找到可以生成foo.o的C的规则就不再寻找下一条规则了。如果你确实不希望任何隐含规则推导,那么,你就不要只写出“依赖规则”,而不写命令。

那么make都有哪些常用的隐含规则?

1、编译C程序的隐含规则

“<n>.o”的目标的依赖目标会自动推导为“<n>.c”,并且其生成命令是“$(CC) –c $(CPPFLAGS) $(CFLAGS)”。

2、编译C++程序的隐含规则

“<n>.o”的目标的依赖目标会自动推导为“<n>.cc”或是“<n>.C”,并且其生成命令是“$(CXX) –c $(CPPFLAGS) $(CFLAGS)”。(建议使用“.cc”作为C++源文件的后缀,而不是“.C”)。

3、编译Pascal程序的隐含规则

“<n>.o”的目标的依赖目标会自动推导为“<n>.p”,并且其生成命令是“$(PC) –c $ (PFLAGS)”。

4、编译Fortran/Ratfor程序的隐含规则

“<n>.o”的目标的依赖目标会自动推导为“<n>.r”或“<n>.F”或“<n>.f”,并且其生成命令是:

[html]

  1. “.f” “$(FC) –c $(FFLAGS)”  
  2. “.F” “$(FC) –c $(FFLAGS) $(CPPFLAGS)”  
  3. “.f” “$(FC) –c $(FFLAGS) $(RFLAGS)”  


5、预处理Fortran/Ratfor程序的隐含规则

“<n>.f”的目标的依赖目标会自动推导为“<n>.r”或“<n>.F”。这个规则只是转换Ratfor或有预处理的Fortran程序到一个标准的Fortran程序。其使用的命令是:

[html]

  1. “.F” “$(FC) –F $(CPPFLAGS) $(FFLAGS)”  
  2. “.r” “$(FC) –F $(FFLAGS) $(RFLAGS)”  


6、编译Modula-2程序的隐含规则

“<n>.sym”的目标的依赖目标会自动推导为“<n>.def”,并且其生成命令是:“$(M2C)$(M2FLAGS) $(DEFFLAGS)”。“<n.o>” 的目标的依赖目标会自动推导为“<n>.mod”, 并且其生成命令是:

[html]

  1. “$(M2C) $(M2FLAGS) $(MODFLAGS)”  


7、汇编和汇编预处理的隐含规则

“<n>.o” 的目标的依赖目标会自动推导为“<n>.s”,默认使用编译品“as”,并且其生成命令是:“$(AS) $(ASFLAGS)”。“<n>.s” 的目标的依赖目标会自动推导为“<n>.S”,默认使用C预编译器“cpp”,并且其生成命令是:

[html]

  1. “$(AS) $(ASFLAGS)”  


8、链接Object文件的隐含规则

“<n>”目标依赖于“<n>.o”,通过运行C的编译器来运行链接程序生成(一般是“ld”),其生成命令是:“$(CC) $(LDFLAGS) <n>.o $(LOADLIBES) $(LDLIBS)”。这个规则对于只有一个源文件的工程有效,同时也对多个Object文件(由不同的源文件生成)的也有效。例如如下规则:

x : y.o z.o


并且“x.c”、“y.c”和“z.c”都存在时,隐含规则将执行如下命令:

[html]

  1. cc -c x.c -o x.o  
  2. cc -c y.c -o y.o  
  3. cc -c z.c -o z.o  
  4. cc x.o y.o z.o -o x  
  5. rm -f x.o  
  6. rm -f y.o  
  7. rm -f z.o  


9、Yacc C程序时的隐含规则

“<n>.c”的依赖文件被自动推导为“n.y”(Yacc生成的文件),其生成命令是:“$(YACC) $(YFALGS)”。(“Yacc”是一个语法分析器,关于其细节请查看相关资料)

10、Lex C程序时的隐含规则

“<n>.c”的依赖文件被自动推导为“n.l”(Lex生成的文件),其生成命令是:“$(LEX) $(LFALGS)”。(关于“Lex”的细节请查看相关资料)。

11、Lex Ratfor程序时的隐含规则

“<n>.r”的依赖文件被自动推导为“n.l”(Lex生成的文件),其生成命令是:“$(LEX) $(LFALGS)”。

12、从C程序、Yacc文件或Lex文件创建Lint库的隐含规则

“<n>.ln” (lint生成的文件)的依赖文件被自动推导为“n.c”,其生成命令是:“$(LINT) $(LINTFALGS) $(CPPFLAGS) -i”。对于“<n>.y”和“<n>.l”也是同样的规则。

另外,在隐含规则中的命令中,基本上都是使用了一些预先设置的变量。你可以在你的makefile中改变这些变量的值,或是在make的命令行中传入这些值,或是在你的环境变量中设置这些值,无论怎么样,只要设置了这些特定的变量,那么其就会对隐含规则起作用。例如,第一条隐含规则——编译C程序的隐含规则的命令是“$(CC) –c $(CFLAGS) $(CPPFLAGS)”。Make默认的编译命令是“cc”,如果你把变量“$(CC)”重定义成“gcc”,把变量“$(CFLAGS)”重定义成“-g”,那么,隐含规则中的命令全部会以“gcc –c -g $(CPPFLAGS)”的样子来执行了。

隐含规则的搜索算法:

比如我们有一个目标叫 T。下面是搜索目标T的规则的算法。请注意,在下面,我们没有提到后缀规则,原因是,所有的后缀规则在Makefile被载入内存时,会被转换成模式规则。如果目标是"archive(member)"的函数库文件模式,那么这个算法会被运行两次,第一次是找目标T,如果没有找到的话,那么进入第二次,第二次会把"member"当作T来搜索。

1、把T的目录部分分离出来。叫D,而剩余部分叫N。(如:如果T是"src/foo.o",那么,D就是"src/",N就是"foo.o")

2、创建所有匹配于T或是N的模式规则列表。

3、如果在模式规则列表中有匹配所有文件的模式,如"%",那么从列表中移除其它的模式。

4、移除列表中没有命令的规则。

5、对于第一个在列表中的模式规则:

1)推导其"茎"S,S应该是T或是N匹配于模式中"%"非空的部分。

2)计算依赖文件。把依赖文件中的"%"都替换成"茎"S。如果目标模式中没有包含斜框字符,而把D加在第一个依赖文件的开头。

3)测试是否所有的依赖文件都存在或是理当存在。(如果有一个文件被定义成另外一个规则的目标文件,或者是一个显式规则的依赖文件,那么这个文件就叫"理当存在")

4)如果所有的依赖文件存在或是理当存在,或是就没有依赖文件。那么这条规则将被采用,退出该算法。

6、如果经过第5步,没有模式规则被找到,那么就做更进一步的搜索。对于存在于列表中的第一个模式规则:

1)如果规则是终止规则,那就忽略它,继续下一条模式规则。

2)计算依赖文件。(同第5步)

3)测试所有的依赖文件是否存在或是理当存在。

4)对于不存在的依赖文件,递归调用这个算法查找他是否可以被隐含规则找到。比如我们有一个目标叫 T。下面是搜索目标T的规则的算法。请注意,在下面,我们没有提到后缀规则,原因是,所有的后缀规则在Makefile被载入内存时,会被转换成模式规则。如果目标是"archive(member)"的函数库文件模式,那么这个算法会被运行两次,第一次是找目标T,如果没有找到的话,那么进入第二次,第二次会把"member"当作T来搜索。

1、把T的目录部分分离出来。叫D,而剩余部分叫N。(如:如果T是"src/foo.o",那么,D就是"src/",N就是"foo.o")

2、创建所有匹配于T或是N的模式规则列表。

3、如果在模式规则列表中有匹配所有文件的模式,如"%",那么从列表中移除其它的模式。

4、移除列表中没有命令的规则。

5、对于第一个在列表中的模式规则:

1)推导其"茎"S,S应该是T或是N匹配于模式中"%"非空的部分。

2)计算依赖文件。把依赖文件中的"%"都替换成"茎"S。如果目标模式中没有包含斜框字符,而把D加在第一个依赖文件的开头。

3)测试是否所有的依赖文件都存在或是理当存在。(如果有一个文件被定义成另外一个规则的目标文件,或者是一个显式规则的依赖文件,那么这个文件就叫"理当存在")

4)如果所有的依赖文件存在或是理当存在,或是就没有依赖文件。那么这条规则将被采用,退出该算法。

6、如果经过第5步,没有模式规则被找到,那么就做更进一步的搜索。对于存在于列表中的第一个模式规则:

1)如果规则是终止规则,那就忽略它,继续下一条模式规则。

2)计算依赖文件。(同第5步)

3)测试所有的依赖文件是否存在或是理当存在。

4)对于不存在的依赖文件,递归调用这个算法查找他是否可以被隐含规则找到。

十九、使用make更新函数库文件

函数库文件也就是对Object文件(程序编译的中间文件)的打包文件。在Unix下,一般是由命令"ar"来完成打包工作。

至此,这本书也就大体的过一遍了,不过最好的学习就是实践,android源码中很多的makefile文件可以起到很好的进行巩固学习的效果;




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