SuperIC社区_

标题: Android源代码编译命令m/mm/mmm/make分析(2) [打印本页]

作者: liuwei    时间: 2016-9-28 13:17
标题: Android源代码编译命令m/mm/mmm/make分析(2)
   接下来我们就先对build/core/main.mk文件的核心逻辑进行分析,然后再进一步对其中涉及到的关键点进行分析。

        build/core/main.mk文件的执行过程如下所示:

        1. 定义默认make目标为droid。目标droid根据不同的情形有不同的依赖关系。如果在初始化编译环境时,指定了TARGET_BUILD_APPS环境变量,那么就表示当前只编译特定的模块,这些特定的模块保存在变量unbundled_build_modules中,这时候目标droid就透过另外一个伪目标app_only依赖它们。如果在初始化编译环境时没有指定TARGET_BUILD_APPS环境变量,那么目标droid就依赖于另外两个文件droidcore和dist_files。droidcore是一个make伪目标,它依赖于各种预编译文件,以及system.img、boot.img、recovery.img和userdata.img等镜像文件。dist_files也是一个make伪目标,用来指定一些需要在编译后拷贝到out/dist目录的文件。也就是说,当我们在Android源码目录中执行不带目标的make命令时,默认就会对目标droid进行编译,也就是会将整个Android系统编译出来。

        2. 加载build/core/config.mk文件。从前面Android编译系统环境初始化过程分析一文可以知道,在加载build/core/config.mk文件的过程中,会在执行make命令的进程中完成对Android编译环境的初始化过程,也就是会指定好目标设备以及编译类型。

        3. 加载build/croe/definiti**.mk文件。该文件定义了很多在编译过程中要用到的宏,相当于就是定义了很多通用函数,供编译过程调用。

        4. 如果在执行make命令时,指定的不是清理文件相关的目标,也就是不是clean、clobber、dataclean和installclean等目标,那么就会将变量dont_bother的值设置为true,表示接下来要执行的是编译命令。

        5. 在变量dont_bother的值等于true的情况下,如果环境变量ONE_SHOT_MAKEFILE的值不等于空,也就是我们执行的是mm或者mmm命令,那么就表示要编译的是特定的模块。这些指定要编译的模块的Android.mk文件路径就保存在环境变量ONE_SHOT_MAKEFILE中,因此直接将这些Android,mk文件加载进来就获得相应的编译规则。另一方面,如果环境变量ONE_SHOT_MAKEFILE的值等于空,那么就说明我们执行的是m或者make命令,那么就表示要对Android源代码中的所有模块进行编译,这时候就通过build/tools/findleaves.py脚本获得Android源代码工程下的所有Android.mk文件的路径列表,并且将这些Android.mk文件加载进行获得相应的编译规则。

        6. 上一步指定的Android.mk文件加载完成之后,变量ALL_MODULES就包含了所有要编译的模块的名称,这些模块名称以空格来分隔形成成一个列表。

        7. 生成模块依赖规则。每一个模块都可以通过LOCAL_REQUIRED_MODULES来指定它所依赖的其它模块,也就是说当一个模块被安装时,它所依赖的其它模块也同样会被安装。每一个模块m依赖的所有模块都会被保存在ALL_MODULES.$(m).REQUIRED变量中。对于每一个被依赖模块r,我们需要获得它的安装文件,也就是最终生成的模块文件的文件路径,以便可以生成相应的编译规则。获得一个模块m的安装文件是通过调用函数module-installed-files来实现的,实质上就是保存在$(ALL_MODULES.$(m).INSTALLED变量中。知道了一个模块m的所依赖的模块的安装文件路径之后,我们就可以通过函数add-required-deps来指定它们之间的依赖关系了。注意,这里实际上指定的是模块m的安装文件与它所依赖的模块r的安装文件的依赖关系。

        8. 将所有要安装的模块都保存在变量ALL_DEFAULT_INSTALLED_MODULES中,并且将build/core/Makefie文件加载进来。 build/core/Makefie文件会根据要安装的模块产成system.img、boot.img和recovery.img等镜像文件的生成规则。

        9. 前面提到,当执行mm命令时,make目标指定为all_moudles。另外,当执行mmm命令时,默认的make目标也指定为all_moudles。因此,我们需要指定目标all_modules的编译规则,实际上只要将它依赖于当前要编译的所有模块就行了,也就是依赖于由变量ALL_MODULES所描述的模块。

       在上述过程中,最核心的就是第5步和第8步。由于本文只关心Android源码的编译过程,因此我们只分析第5步的执行过程。在接下来一篇文章中分析Android镜像文件的生成过程时,我们再分析第8步的执行过程。

       第5步实际上就是将指定模块的Android.mk文件加载进来。一个典型的Android.mk文件如下所示:


[plain] view plain copy



       以LOCAL开头的变量都是属于模块局部变量,也就是说,一个模块在开始编译之前,必须要先对它们进行清理,然后再进行初始化。Android编译系统定义了非常多的模块局部变量,因此我们不可能手动地一个一个清理,需要加载一个由变量CLEAR_VARS指定的Makefile脚本来帮我们自动清理。变量CLEAR_VARS的值定义在build/core/config.mk文件,它的值等于build/core/clear_vars.mk。

       Android.mk文件中还有一个重要的变量LOCAL_PATH,用来指定当前正在编译的模块的目录,我们可以通过调用宏my-dir来获得。宏my-dir定义在build/core/definiti**.mk文件,它实际上就是将当前正在加载的Android.mk文件路径的目录名提取出来。

       Android.mk文件接下来就是通过其它的LOCAL变量定义模块名称、源文件,以及所要依赖的各种库文件等等。例如,在我们这个例子,模块名称定义为libdis,参与编译的源文件为dispatcher.cpp和common.cpp、依赖的库文件为liblog和libdl。

       最后,Android文件通过加载一个模板文件来告诉编译系统它所要编译的模块的类型。例如,在我们这个例子中,就是通过加载由变量BUILD_SHARED_LIBRARY指定的模板文件来告诉编译系统我们要编译的模块是一个动态链接库。变量BUILD_SHARED_LIBRARY的值定义在build/core/config.mk文件,它的值等于build/core/shared_library.mk。

       Android编译系统定义了非常多的模板文件,每一个模板文件都对应一种类型的模块,例如除了我们上面的动态链接库模板文件之外,还有:

       BUILD_PACKAGE:指向build/core/package.mk,用来编译APK文件。

       BUILD_JAVA_LIBRARY:指向build/core/java_library.mk,用来编译Java库文件。

       BUILD_STATIC_JAVA_LIBRARY:指向build/core/tatic_java_library.mk,用来编译Java静态库文件。

       BUILD_STATIC_LIBRARY:指向build/core/static_library.mk,用来编译静态库文件。也就是.a文件。

       BUILD_EXECUTABLE:指向build/core/executable.mk,用来编译可执行文件。

       BUILD_PREBUILT:指向build/core/prebuilt.mk。用来编译已经预编译好的第三方库文件,实际上是将这些预编译好的第三方库文件拷贝到合适的位置去,以便可以让其它模块引用。

       不管编译何种类型的模块,都是主要完成以下的工作:

       1. 制定好相应的依赖规则

       2. 调用合适的命令进行编译

       为了简单起见,接下来我们就以动态链接库(即.so文件)的编译过程为例来说明Android编译命令mmm的执行过程。

       在分析动态链接库的编译过程之前,我们首先看一看使用mmm命令来编译上述的Android.mk文件时得到的输出,如下所示:


[plain] view plain copy



       从这些输出我们大体推断出一些文件之间的依赖关系及其生成过程:

       1. out/target/product/generic/obj/SHARED_LIBRARIES/libdis_intermediates/LINKED/libdis.so文件依赖于external/si/dispatcher/dispatcher.cpp和external/si/dispatcher/../common/common.cpp文件,并且由它们生成。

       2. out/target/product/generic/symbols/system/lib/libdis.so依赖于out/target/product/generic/obj/SHARED_LIBRARIES/libdis_intermediates/LINKED/libdis.so文件,并且由它生成。

       3. out/target/product/generic/obj/lib/libdis.so依赖于out/target/product/generic/symbols/system/lib/libdis.so文件,并且由它生成。

       4. out/target/product/generic/system/lib/libdis.so依赖于out/target/product/generic/obj/lib/libdis.so文件,并且由它生成。

       回忆前面的分析,我们提到,当执行mmm命令时,默认的make目标是all_modules,并且它依赖于变量ALL_MODULES指向的文件或者目标,因此,我们可以继续推断出,变量ALL_MODULES指向的文件或者目标一定会与文件out/target/product/generic/system/lib/libdis.so有依赖关系,这样才能够从make目标all_modules开始链式地生成上述文件。在接下来的分析中,我们就按照抓住上述文件的依赖关系进行逆向分析。

      从上面的分析可以知道,在编译动态链接库文件的过程中,文件build/core/shared_library.mk会被加载,它的核心内容如下所示:


[plain] view plain copy



       LOCAL_MODULE_CLASS用来描述模块文件的类型。对于动态链接库文件来说,如果我们没有对它进行设置的话,它的默认值就等于SHARED_LIBRARIES。

       LOCAL_MODULE_SUFFIX用来描述生成的模块文件的后缀名。对于动态链接库文件来说,如果我们没有对它进行设置的话,它的默认值就等于TARGET_SHLIB_SUFFIX。即.so。

       上述两个变量限定了生成的动态链接库文件的完整文件名以及保存位置。

       接下来,build/core/shared_library.mk文件加载了另外一个文件build/core/dynamic_binary.mk文件,并且为变量linked_module指向的文件制定了一个依赖规则,这个依赖规则由函数transform-o-to-shared-lib来执行。从函数transform-o-to-shared-lib就可以知道,它是根据一系列的中间编译文件(object文件)以及依赖库文件生成指定的动态链库文件的,主要就是由变量all_objects和all_libraries所描述的文件。现在,变量linked_module、all_objects和all_libraries所指向的文件是我们所要关心的。

       我们接着分析文件build/core/dynamic_binary.mk文件的加载过程,它的内容如下所示:


[plain] view plain copy



       LOCAL_UNSTRIPPED_PATH描述的是带符号的模块文件的输出目录。如果我们没有设置它,并且也没有设置变量LOCAL_MODULE_PATH的值,那么它的默认值就会与当前要编译的产品以及当前要编译的模块文件类型有关。例如,如果我们在执行lunch命令时,选择的是目标产品是模拟器,并且当前要编译的是动态链接库文件,那么得到的LOCAL_UNSTRIPPED_PATH值就为TARGET_OUT_$(LOCAL_MODULE_CLASS)_UNSTRIPPED。将$(LOCAL_MODULE_CLASS)替换为SHARED_LIBRARIES,就得到LOCAL_UNSTRIPPED_PATH的值为TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED,它值就等于out/target/product/generic/symbols/system/lib。也就是说,我们在为模拟器编译动态链接库模块时,生成的带符号文件都保存在目录out/target/product/generic/symbols/system/lib中。

       如果我们没有设置 LOCAL_MODULE_STEM的值的话,那么它的默认值就等在我们在Android.mk文件中设置的LOCAL_MODULE的值。在我们的例子中,LOCAL_MODULE_STEM的值就等于LOCAL_MODULE的值,即libdis。

       LOCAL_INSTALLED_MODULE_STEM和LOCAL_BUILT_MODULE_STEM的值等于LOCAL_MODULE_STEM的值再加上后缀名LOCAL_MODULE_SUFFIX。在我们的例子中,LOCAL_INSTALLED_MODULE_STEM和LOCAL_BUILT_MODULE_STEM的值就等于libdis.so。

       这里调用函数local-intermediates-dir得到的是动态链接文件的中间输出目录,默认就是out/target/product/generic/obj/SHARED_LIBRARIES/libdis_intermediates了,因此,我们就可以得到变量linked_module的值为out/target/product/generic/obj/SHARED_LIBRARIES/libdis_intermediates/LINKED/libdis.so,这是编译过程要生成的文件之一。

       LOCAL_INTERMEDIATE_TARGETS的值被设置为linked_module的值,接下来在加载build/core/binary.mk文件时需要用到。

       接下来会根据out/target/product/generic/obj/SHARED_LIBRARIES/libdis_intermediates/LINKED/libdis.so文件生成另外三个文件:

       1. 生成带符号压缩的模块文件,前提是LOCAL_COMPRESS_MODULE_SYMBOLS的值等于true。输入是compress_input,即linked_module,输出是compress_output,即$(intermediates)/COMPRESSED-$(LOCAL_BUILT_MODULE_STEM)。注意。目前还不支持此类型的模块文件。因此,当变量LOCAL_COMPRESS_MODULE_SYMBOLS的值等于true时,就会报错。

       2. 拷贝一份带符号的模块文件到LOCAL_UNSTRIPPED_PATH描述的目录中去,即out/target/product/generic/symbols/system/lib目录。在我们这个情景中,得到的文件即为out/target/product/generic/symbols/system/lib/libdis.so。

       3. 生成不带符号的模块文件,前提是LOCAL_STRIP_MODULE的值等于true。输入是前面拷贝到out/target/product/generic/symbols/system/lib目录的文件,输出由变量LOCAL_BUILT_MODULE指定。变量LOCAL_BUILT_MODULE的值是在加载文件build/core/binary.mk的过程中指定的。

       到目前为止,我们就解决前面提到的文件out/target/product/generic/obj/SHARED_LIBRARIES/libdis_intermediates/LINKED/libdis.so和out/target/product/generic/symbols/system/lib/libdis.so的生成过程,还剩下out/target/product/generic/obj/lib/libdis.so和out/target/product/generic/system/lib/libdis.so文件的生成过程未搞清楚。这就需要继续分析文件build/core/binary.mk的加载过程。

       文件build/core/binary.mk的核心内容如下所示:


[plain] view plain copy










欢迎光临 SuperIC社区_ (/) Powered by Discuz! X3.3