Android的编译系统

转自:http://www.360doc.com/content/11/0609/14/474846_122680003.shtml

本文主要从编译全局控制的角度描述。

一、Makefile的主要流程


以下主要流程都在build/core/main.mk里安排。

    初始化相关的参数设置(buildspec.mkenvsetup.mkconfig.mk)
    检测编译环境和目标环境
    读取product的配置信息及目标平台信息
    清除输出目录
    检查版本号
    读取Board的配置
    读取所有Module的配置
    根据配置产生必要的规则(build/core/Makefile)
    生成image

主要配置和实现文件:   

   build/core/config.mk         summary of config
   build/core/envsetup.mk    generate dir config and so on
   build/target/product         product config
    build/target/board            board config
    build/core/combo              build flags config

这里解释下这里的boardproduct目录。

board目录主要是设计到硬件芯片的配置,比如是否提供硬件的某些功能,比如说GPU等等,或者芯片支持浮点运算等等。

product目录是指针对当前的芯片配置定义你将要生产产品的个性配置,主要是指APK方面的配置,哪些APK会包含在哪个product中,哪些APK在当前product中是不提供的。
config.mk是一个总括性的东西,它里面定义了各种module编译所需要使用的HOST工具以及如何来编译各种模块,比如说 BUILT_PREBUILT就定义了如何来编译预编译模块。envsetup.mk主要会读取由envsetup.sh写入环境变量中的一些变量来配置编译过程中的输出目录,combo里面主要定义了各种HostTarget结合的编译器和编译选项。

配置部分主要完成以下几个工作:
a) 基于Android 产品的配置(product config):选择构建安装的运行程序(user package)
b) 设置 target 等相关变量TARGET_ARCH, TARGET_OS,TARGET_BUILD_TYPE,
TARGET_PREBUILT_TAG
c) 根据编译环境设置 host等相关变量HOST_OS, HOST_ARCH,HOST_BUILD_TYPE,
HOST_PREBUILT_TAG
d) 编译 target上运行程序所需的工具链及编译参数设置,如linux-arm-cccflaginclude目录等。
e) 编译 host上运行程序所需的工具链及编译参数设置。
下图简要介绍了Android build system的配置部分的主要构成及相互关系。

 

二、初始化参数设置

main.mk里,简单设置几个主要编译路径的变量后,来到config.mk

——————————————config.mk——————————————

其中设置了源文件的一系列路径,包括头文件、库文件、服务、API已经编译工具的路径(前36行)。


40行开始,定义一些编译模块的生成规则:

除了CLEAR_VARS是清楚本地变量之外,其他所有的都对应了一种模块的生成规则,每一个本地模块最后都会include其中的一种来生成目标模块。

 

回到config.mk,接着会尝试读取buildspec.mk的设置:

 

如同注释所说,会尝试查找buildspec.mk,如果文件不存在会自动使用环境变量的设置,如果仍然未定义,会按arm默认的设置去build

这里的buildspec.mk可以自己创建,也可以将原先build/下的buildspec.mk.default直接命名为buildspec.mk并移到根目录。

实际上,buildspec.mk配置都被屏蔽了,我们可以根据需要直接打开和修改一些变量。在这里我们可以加入自己的目标产品信息:

ifndef TARGET_PRODUCT
TARGET_PRODUCT:=generic_x86
endif

以及输出目录设置:
OUT_DIR:=$(TOPDIR)generic_x86

三、读取Product的设定


回到config.mk,接着进行全局变量设置,进入envsetup.mk

——————————————envsetup.mk——————————————

里面的大部分函数都在build/envsetup.sh中定义。

首先,设置版本信息,(11)build/core/version_defaults.mk中具体定义平台版本、SDK版本、Product版本,我们可以将BUILD_NUMBER作为我们产品generic_x86version信息,当然,也可以自定义一个版本变量。

回到envsetup.mk,接着设置默认目标产品(generic),这里由于我们在buildspec.mk里设置过TARGET_PRODUCT,事实上这个变量值为generic_x86

然后读取product的设置(41),具体实现在build/core/product_config.mk中,进而进入product.mk,从build/target/product/AndroidProducts.mk中读出PRODUCT_MAKEFILES,这些makefile各自独立定义product,而我们的产品generic_x86也应添加一个makefile文件generic_x86.mk。在generic_x86.mk中我们可以加入所需编译的PRODUCT_PACKAGES

下面为generic_x86.mk

四、读取BoardConfig


接着回到config.mk(114)这里会搜索所有的BoardConfig.mk,主要有以下几个地方:

这里的TARGET_DEVICE就是generic_x86,就是说为了定义我们自己的产品generic_x86,我们要在build/target/board下添加一个自己的目录generic_x86用来加载自己的board配置。

BoardConfig.mk中会决定是否编译bootloaderkernel等信息。

五、读取所有Module


结束全局变量配置后,回到main.mk,对编译工具及版本进行检查,错误便中断编译。
line142,包含文件definitions.mk,这里面定义了许多变量和函数供main.mk使用。
line 446这里会去读取所有的Android.mk文件:

其中include $(ONE_SHOT_MAKEFILE)
这个ONE_SHOT_MAKEFILE是在前面提到的mm(envsetup.mk)函数中赋值的:
ONE_SHOT_MAKEFILE=$M make -C $T files $@

回到main.mk,最终将遍历查找到的所有子目录下的Android.mk的路径保存到subdir_makefiles变量里(main.mk里的470)

我们在package/apps下每个模块根目录都能看到Android.mk,里面会去定义当前本地模块的TagLOCAL_MODULE_TAGSAndroid会通过这个Tag来决定哪些本地模块会编译进系统,通过PRODUCTLOCAL_MODULE_TAGS来决定哪些应用包会编译进系统。(前面说过,你也能通过buildspec.mk来制定你要编译进系统的模块)

这个过程在mian.mk445行开始,最后需要编译的模块路径打包到ALL_DEFAULT_INSTALLED_MODULES(602)

关于Main.mk更多的话

user_PACKAGES := PRODUCT_PACKAGES PRODUCT_PACKAGEScore.mk, generic_no_telephony.mksdk.mk中定义,是APK名称列表。

[user/eng/debug/tests]_MODULES += user_PACKAGES

modules_to_install是根据规则从[user/eng/debug/tests]_MODULES检出,然后除去overriden_packages,然后除去target_gnu_MODULES,对于剩下的modules_to_install,不是检查modules_to_install的存在性,而是检查所有PRODUCT_PACKAGES的存在性。

 

envsetup.sh通过遍历特定的目录下的AndroidProduct.mk文件,这个文件中的会有如下语句:

<LOCAL_DIR>/msm7627a.mk

envsetup.sh会去掉.mkmsm7627a作为平台名称,供配置选择,而该平台对应的编译文件就是msm7627a.mk,其中会根据情况包含一些其他的.mk文件,如../<VENDOR>/common.mk../<VENDOR>/product_name.mk.

 

Prebuild的模块可以直接拷贝到相关输出目录,也可以使用BUILD_PREBUILTBUILD_MULTI_PREBUILT进行拷贝,模块可以是applib等。

Resources overlay可以使用PRODUCT_PACKAGE_OVERLAYS由编译系统进行目录遍历的归并。

 

一些要需要编译的模块可以直接加到PRODUCT_PACKAGES中进行编译。

如果想用Vendor自定义的不同名Prebuild包替换掉原生的apk,可以直接将原生apk编译模块名从PRODUCT_PACKAGES中去掉,添加上自己的apk名。当然最好是在定制包目录下使用Android.mkLOCAL_OVERRIDES_PACKAGES BUILT_PREBUILD更正规一些。

ALL_MODULES 是用来根据MODULE_TAG控制拷贝(安装)。所以Prebuild的包可以根据其输出方式决定是否加入到ALL_MODULESPRODUCT_PACKAGES中。

ALL_MODULES.$(m).INSTALLED := $path/$package_name.apk

 

ALL_MODULESPRODUCT_PACKAGES的关系?

ALL_MODULESbase_rules.mk中定义,是所有LOCAL_MODULE的集合;PRODUCT_PACKAGESproduct.mk中定义,会以$PRODUCT.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES体现在main,mk中使用,在common.mk/sdk.mk中初始化赋值。

 

编译系统会扫描源码目录中所有的Android.mk,形成subdir_makefiles,解析所有Android.mk,每个Android.mk会包含base_rules.mk中从而引入ALL_MODULES和其代码,从而会将LOCAL_MODULE添加到ALL_MODULES中,LOCAL_MODULE的详细信息会添加到ALL_MODULES.$(LOCAL_MODULE)的各信息字段中,如INSTALLED字段就包含完整的模块安装路径名, dir/file_name.ext。这用于控制特定产品Productuserdebugengtests)的模块的安装

大致是PRODUCT_PACKAGES中的模块会被编译,而ALL_MODULES用来控制输出安装。

 

main.mk中,编译sdk时有如下代码片段用于模块检查,

# Ensure every module listed in PRODUCT_PACKAGES gets something installed

$(foreach m, $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES), \

    $(if $(strip $(ALL_MODULES.$(m).INSTALLED)),,\

        $(error Module '$(m)' in PRODUCT_PACKAGES has nothing to install!)))

编译时可能会报某些模块无从安装。

这主要是因为不知道模块如何输出安装。

说明模块名在PRODUCT_PACKAGES中,但是ALL_MODULES中对应模块的INSTALLED目录为空,或者ALL_MODULES中不存在该项。

这通常是自己在PRODUCT_PACKAGES中直接添加需要编译的模块,然后自己拷贝;

或者其INSTALLED因为没有默认输出目录从而为空,或者没有ALL_MODULES += $(LOCAL_MODULE)将其添加到ALL_MODULES中,或者根本就不从在对应MODULE目标的Android.mk

 

这可以自己根据情况要么filter_out,要么添加ALL_MODULES.$(m)各子字段即可。

如下为编译SDK时,Filter out掉的包,因为这些源码目录已经被删掉(用Vendor PrebuildVendorNamePackageName.apk模块代替),从而Android.mk也不复存在,但是MOUDLE Name没有PRODUCT_PACKAGES中去掉。

ifdef is_sdk_build

  # same name packages in core.mk replaced by vendor prebuild.

  CORE_PACKAGES_REPLACED_BY_PREBUILD := \

  Contacts \

  ContactsProvider \

  Home \

  TelephonyProvider \

  libdrmframeworkcommon

 

  # same name packages in sdk.mk replaced by vendor prebuild.

  SDK_PACKAGES_REPLACED_BY_PREBUILD := \

       Calculator \

       Camera \

       DeskClock \

       Email \

       Exchange \

       Gallery \

       Music \

       Mms \

       OpenWnn \

       libWnnEngDic \

       libWnnJpnDic \

       libwnndict \

       Phone \

       PinyinIME \

       Launcher2 \

       SdkSetup \

       LatinIME \

       CalendarProvider \

       Calendar

 

  PACKAGES_REPLACED_BY_PREBUILD := \

              $(CORE_PACKAGES_REPLACED_BY_PREBUILD) \

              $(SDK_PACKAGES_REPLACED_BY_PREBUILD)

 

  # filter out deleted packages, these modules will be copy to destination dirs directly.

  PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES := \

              $(filter-out $(PACKAGES_REPLACED_BY_PREBUILD),$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES))

endif

 

六、产生相应的Rules,生成image


所有需要配置的准备工作都已完成,下面该决定如何生成image输出文件了,这一过程实际上在build/core/Makefile中处理的。

这里定义各种img的生成方式,包括ramdisk.imguserdata.imgsystem.imgupdate.ziprecover.img等。

Make include所有的文件,完成对所有make文件的解析以后就会寻找生成对应目标的规则,依次生成它的依赖,直到所有满足的模块被编译好,然后使用相应的工具打包成相应的img

具体make操作:

    完整编译

我们在根目录下输入make命令即可开始完全编译。这个命令实际编译生成的默认目标是droid

也就是说,大家敲入make实际上执行的make droid。而接下来大家看看main.mk文件里最后面的部分,会有很多伪目标,如sdkcleanclobber等,这些在默认的make droid的命令下是不会执行的。我们可以在make后加上这些标签来单独实现一些操作。如:输入make sdk将会生成该版本对应的SDK,输入make clean会清除上次编译的输出。

    模块编译

有时候我们只修改了某一个模块,希望能单独编译这个模块而不是重新完整编译一次,这时候我们要用到build/envsetup.sh中提供的几个bash的帮助函数。

在源代码根目录下执行:

. build/envsetup.sh(.后面有空格)

这样大家相当于多了几个可用的命令。
这时可以用help命令查看帮助信息:
其中对模块编译有帮助的是tapasmmmmmm这几个命令。

1tapas——以交互方式设置build环境变量。
   输入:tapas
第一步,选择目标设备:
第二步,选择代码格式:
第三步,选择产品平台:

 注意:这里,Google源代码里默认是generic,而我们针对自己的产品应修改成generic_x86具体在build/envsetup.sh里的函数chooseproduct()中对相应代码进行修改。

2mmmmmm使用独立模块的make命令。

几个命令的功能使用help命令查看。
举个例子,我们修改了Camera模块的代码,现在需要重新单独编译这一块,这时可以使用mmm命令,后面跟指定模块的路径(注意是模块的根目录)

具体如下:
mmm packages/apps/Camera/

为了可以直接测试改动,编译好后需要重新生成system.img可以执行:make snod

    单独编译image文件

一般我们完整编译后,会生成三个重要的image文件:ramdisk.imgsystem.imguserdata.img。当然我们可以分开单独去编译这三个目标:

make ramdisk —— ramdisk.img
make userdataimage —— userdata.img
make systemimage  —— system.img

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值