Android编译系统的架构:
分析Android编译系统,你会发现,Android编译系统完成的并不仅仅是对目标(主机)系统二进制文件、java应用程序的编译、链接、打包等,而且还有包括生成各种依赖关系、确保某个模块的修改引起相依赖的文件的重新编译链接,甚至还包括目标文件系统的生成,配置文件的生成等,因此Android编译系统具有支持多架构(linux-x86、windows、arm等)、多语言(汇编、C、C++、Java等)、多目标、多编译方式。这些目标和结构决定其架构也很重要。
Android编译系统集中于build/core下,几个很重要的*.mk文件如下:
main.mk(主控Makefile)
base_rules.mk(对一些Makefile的变量规则化)
config.mk(关于编译参数、编译命令的一些配置)
definations.mk(定义了很多编译系统中用到的宏,相当于函数库)
Makefile(这个Makefile特指build/core下的Makefile,此文件主要控制生成system.img,ramdisk.img,userdata.img,以及recorvery image,sdk等)
Binary.mk(控制如何生成目标文件)
Clear_vars.mk(清除编译系统中用到的临时变量)
Combo/linux-arm.mk(控制如何生成linux-arm二进制文件,包括ARM相关的编译器,编译参数等的设置)
Copy_headers.mk(将头文件拷贝到指定目录)
分散于各个目录下的Android.mk(控制生成局部模块的源码,名称所需头文件路径,依赖库等特殊选项)
Build/envsetup.mk(编译环境初始化,定义一些实用的shell函数,方便编译使用)
以上几个主要的文件,可以按照社会分工打一个比方:
Main.mk是总统,是老大,承担了很多工作。
Makefile是副总统,辅佐老大Main.mk
Base_rules.mk是交警,让不规则的东西,变得规则。
Config.mk是省长,规定了各个人民群众该如何行事
Definations.mk是图书馆管理员
Binary.mk应该属于村长了,规定每个人该如何行事
Clear_vars.mk应该属于保洁公司的工人吧
Combo/linux-arm.mk应该属于社会公民了,他决定自己该如何去做
Main.mk分析
Main.mk主要包含如下几个部分的内容
1. SHELL设置
2. 编译环境配置
3. 编译环境检查
4. 包含必要的宏
5. 根据make参数设置编译时的变量
6. 包含需要编译的Android.mk
7. 设置编译系统Target:prerequisites 控制整个编译流程
下面对上面几点进行必要解释:
有了前面小节对Android编译系统架构的分析,如果需要修改Android编译系统,就可以不至于盲目找问题,修改代码了。今天分析下main.mk,看它能为我们Android编译提供什么有价值的信息,以及如何自己定制我们的Android编译系统。
Main.mk的第一句就根据ANDROID_BUILD_SHELL来包裹编译系统用到的Shell,如果我们不想使用bash,而想使用sh,那么就可以在它前面写上ANDROID_BUILD_SHELL := /bin/sh,或者在build/envsetup.sh中添加相关定义。
定义完SHELL之后,就是对MAKE_VERSION的检查,然后定义了默认的编译目标droid!
如果我们敲入make之后,不加任何参数,默认的目标就是droid。注意虽然后面的include $(BUILD_SYSTEM)/config.mk写在默认目标droid依赖之后,但其和之后的语句都是要执行的,这是Makefile的语法决定的。
后面会include config.mk cleanbuild.mk对编译系统进行必要的配置。后面就是对编译环境的检查,包括是否大小写敏感、路径检查、java版本检查、javac版本检查。Android对编译环境的检查如果符合条件,在下次编译的时候,不会再次进行检查。
检查完版本之后,会包含进definations.mk,如前所述,definations.mk中定义了很多编译系统中用到的宏,这些宏在编译时需要经常调用,因此在编译的很靠前的阶段,就将之包含了进来。
然后就是针对make时传入的编译类型(eng user userdebug showcommands等)进行编译配置,这些配置会影响到最终编译目标所包括的模块。对于eng user userdebug sdk win_sdk tests等编译目标的区别,读者可以通过查看main.mk的代码找出其中到底有什么不同。
此处略去部分部分不重要的内容,直接跳到
Ifeq($(SDK_ONLY),true)处,大概368行附近,这个判断语句一直到这个语句块结束,都是对subdirs变量的设置,subdirs变量决定了哪些子文件夹最终被编译。
在后面的subdir_makefiles变量的设置,决定了哪些Android.mk被编译。紧接着include $(subdir_makefiles)就会添加所有这些Android.mk文件的依赖。这其中包含了droid的依赖,后面我们会发现。
然后就会根据这些Makefile,找出所有需要编译的模块(module),以及进行必要的分类(eng_MODULES/debug_MODULES/tests_MODULES等、modules_to_check/modules_to_install等),用以区别对待。
紧接着是定义了一系列的隐含目标:prebuilt、all_copied_headers、files、checkbuild、ramdisk、systemtallball、userdataimage、userdatatarball、bootimg、droidcore等。最重要的一点,是会发现droid依赖于droidcore,而droidcore依赖于
droidcore: files \
systemimage \
$(INSTALLED_BOOTIMAGE_TARGET) \
$(INSTALLED_RECOVERYIMAGE_TARGET) \
$(INSTALLED_USERDATAIMAGE_TARGET) \
$(INSTALLED_FILES_FILE)
正是这几个依赖项,控制着整个android的编译。
图1 android 的makefile结构
android的编译文件主要依赖于mk文件,其源码编译名字是Android.mk,而不我们常见的Makefile文件。
android目录下的Makefile文件,include了build/core目录下的main.mk文件。
main.mk文件
main.mk要完成功能,主要如下:
包含build/core/config.mk:根据目标板的标准变量和主机信息,设置一些变量。确定输出目录和产品工程。
SHELL := /bin/bash, 表明用到系统bash,如果想用其他的sh,那么据此而改即可。
检测host的操作系统,编译环境。
包含definitions.mk,标准的编译系统设定参数。主要是一些宏定义,如在Android.mk常见到的all-subdir-makefiles、my-dir之类的宏。
主要内容还是在 config.mk文件。
1config.mk
首先会检测主目录下的buildspec.mk,目前此文件不存在,我们可在此设置一些参数。
这些参数,可用于envsettup.mk,如果没有buildspec.mk文件,envsettup.mk则默认一些变量,如TARGET_ARCH:= arm ,TARGET_OS := linux等。
1.1 envsettup.mk
应该说envsettup.mk是config.mk重要组成部分,设置一些主要编译工程的相关参数。
首先includeversion_defaults.mk,设置以下变量:
PLATFORM_VERSION
PLATFORM_SDK_VERSION
DEFAULT_APP_TARGET_SDK
BUILD_ID
BUILD_NUMBER
其次设定TARGET_PRODUCT,也就是在编译地时候如果没有用makePRODUCT-xxx-xxx,则会在此处设定。否则就要根据product_config.mk文件里的内容进行target和product的相关参数。
再次,设置一些输出文件路径变量,如TARGET_OUT_XXX之类的。
最后把一些简单信息显示终端上:
$(info============================================)
$(info PLATFORM_VERSION_CODENAME=$(PLATFORM_VERSION_CODENAME))
$(info PLATFORM_VERSION=$(PLATFORM_VERSION))
$(info TARGET_PRODUCT=$(TARGET_PRODUCT))
$(info TARGET_BUILD_VARIANT=$(TARGET_BUILD_VARIANT))
$(info TARGET_SIMULATOR=$(TARGET_SIMULATOR))
$(info TARGET_BUILD_TYPE=$(TARGET_BUILD_TYPE))
$(info TARGET_ARCH=$(TARGET_ARCH))
$(info HOST_ARCH=$(HOST_ARCH))
$(info HOST_OS=$(HOST_OS))
$(info HOST_BUILD_TYPE=$(HOST_BUILD_TYPE))
$(info BUILD_ID=$(BUILD_ID))
$(info============================================)
envsetup.mk里头最主要的就是product_config.mk
product_config.mk包含三个文件node_fns.mk、product.mk和device.mk。此三文件都是一些宏定义或变量定义,目的是根据make时传进来的参数,从vendor目录和build/target目录下,找到相应的product和devcie信息,即找BoardConfig.mk文件,从而设定TARGET_PRODUCT、TARGET_DEVICE等值。
1.2 pathmap.mk,一些头文件路径,及androidframework目录。
编译系统内部的一些文件:host_static_library.mk、host_shared_library.mk等
1.3 BoardConfig.mk
默认情况下是编译的board是generic。
查找build/target/board/generic目录上的内容,共有两个mk文件,AndroidBoard.mk和BoardConfig.mk。其中BoardConfig.mk定义一些硬件特性。
2 Makefile文件
build/core/Makefile文件是系统集成了,完成img,app,frameworks、模块的编译(可能有错)
3 对于framework开发,或者在系统移植时添加应用程序,每写一次代码就要进行编译,而每次用make相关参数命令时,编译的时间会比较长。这时可以考虑以下方式进行编译
cdanddroid_top_path
./build/envsetup.sh
此时就用mmm,mm和m命令进行编译:
m表示在顶层目录进行编译
mm 表示在当前目录下进行模块编译
mmm path, 编译path目录下的模块。
一般在写模块时,用mmm命令就可以了。
以下是转载内容,在framework新增加或修改代码,所谓的系统移植了,链接地址为http://xxw8393.blog.163.com/blog/static/3725683420107532137920/
“
如何向android的framework里添加新类
google对于所有的类和API,分为开放式和不开放式两种。所谓的开放式就是值javadoc所包含 的,并不是java中有public和private,而是跟javadoc有关系,代码没有关系。
在开放式的类中增加了一个变量,而又没隐藏,导致和原API的doc不一致造成的就会有错。
通过提示,有2个方法可以解决 该问题:
1、将新增加的变量或方法加上"@hide"的注释,注意一点,加"@hide"不是简简单单 的/*@hide */就行了,标准的javadoc要这样 /** */而且对于 format变量 应该加上 { }, 也就是/**{@hide}*/
2、如果想在生成的doc中增加该变量或方法的话,必须输入:
make update-api
这样的话,系统 自动 将新增加的API添加到current.xml中了。
所以如果要加方法就是按上面的方法加。
如果需要加进新的类 这时候又分2种 :一种是原有的包下面加类 这个最简单 加完之后直接make update-api就好了 还有一种是加在framework/base下面 这个时候你make update-api是不会在current。xml里生成你的类的。 看了Android。mk才知道 原来需要修改 android源码根目录下的build/core/pathmap.mk把你的目录加进去。然后就好了。
事实上对于famework的开发,可以通过mmm命令生成新jar包,然后手动的方式传到模块器对应目录,一般是/system/framework/即可,当然也可以自己写个脚本,敲一命令,一步到位亦可。