android.mk android源码编译

http://www.cnblogs.com/chenbin7/archive/2013/01/05/2846863.html

Android.mk简单分析

2013-01-05 22:51 by ...平..淡..., 884 阅读, 0 评论, 收藏, 编辑

复制代码
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_PACKAGE_NAME := Settings
LOCAL_CERTIFICATE := platform

include $(BUILD_PACKAGE)

# Use the folloing include to make our test apk.
include $(call all-makefiles-under,$(LOCAL_PATH))
复制代码

该Android.mk文件路径是package/app/Settings/Android.mk,来分析该文件

GNU Make‘功能’宏,必须通过使用'$(call  )'来调用,调用他们将返回文本化的信息。

------------------------------------------------------------------------------------------------------------------------------

(1) LOCAL_PATH:= $(call my-dir)

一个Android.mk file首先必须定义好LOCAL_PATH变量。它用于在开发树中查找源文件。

宏函数’my-dir’,由编译系统提供,用于返回当前路径(即包含Android.mk file文件的目录)。

------------------------------------------------------------------------------------------------------------------------------

(2) Android.mk中可以定义多个编译模块,每个编译模块都是以include $(CLEAR_VARS)开始,以include $(BUILD_XXX)结束。

(2.1) include $(CLEAR_VARS)

CLEAR_VARS指的是clear_vars.mk,由编译系统提供,它会让GNU MAKEFILE为你清除除LOCAL_PATH以外的所有LOCAL_XXX变量,如 LOCAL_MODULE,LOCAL_SRC_FILES,LOCAL_SHARED_LIBRARIES,LOCAL_STATIC_LIBRARIES 等。

这是必要的,因为所有的编译控制文件都在同一个GNU MAKE执行环境中,所有的变量都是全局的。

 

(2.2) include $(BUILD_PACKAGE)      # Tell it to build an APK

$(BUILD_PACKAGE)是用来编译生成package/app/下的apk。

还有其他几种编译情况:

include $(BUILD_STATIC_LIBRARY)   表示编译成静态库

include $(BUILD_SHARED_LIBRARY)  表示编译成动态库

include $(BUILD_EXECUTABLE)      表示编译成可执行程序         

至于例子的话,跳转到下面的"扩展"

------------------------------------------------------------------------------------------------------------------------------

(3) LOCAL_MODULE_TAGS := optional

解析:

LOCAL_MODULE_TAGS :=user eng tests optional

user:  指该模块只在user版本下才编译

eng:  指该模块只在eng版本下才编译

tests: 指该模块只在tests版本下才编译

optional:指该模块在所有版本下都编译

取值范围debug eng tests optional samples shell_ash shell_mksh。注意不能取值user,如果要预装,则应定义core.mk。

------------------------------------------------------------------------------------------------------------------------------

(4) LOCAL_SRC_FILES := $(call all-java-files-under, src)

(4.1) 如果要包含的是java源码的话,可以调用all-java-files-under得到。(这种形式来包含local_path目录下的所有java文件)

(4.2) 当涉及到C/C++时,LOCAL_SRC_FILES变量就必须包含将要编译打包进模块中的C或C++源代码文件。注意,在这里你可以不用列出头文件和包含文件,因为编译系统将会自动为你找出依赖型的文件;仅仅列出直接传递给编译器的源代码文件就好。

all-java-files-under宏的定义是在build/core/definitions.mk中。

------------------------------------------------------------------------------------------------------------------------------

(5) LOCAL_PACKAGE_NAME := Settings

package的名字,这个名字在脚本中将标识这个app或package。

------------------------------------------------------------------------------------------------------------------------------

(6) LOCAL_CERTIFICATE := platform

LOCAL_CERTIFICATE 后面是签名文件的文件名,说明Settings.apk是一个需要platform key签名的APK

  用于指定签名时使用的KEY,如果不指定,默认使用testkey,LOCAL_CERTIFICATE可设置的值如下:

    LOCAL_CERTIFICATE := platform
    LOCAL_CERTIFICATE := shared
    LOCAL_CERTIFICATE := media

 而在Android.mk中的这些配置,需要在APK源码的AndroidManifest.xml文件中的manifest节点添加如下内容:

    android:sharedUserId="android.uid.system"
    android:sharedUserId="android.uid.shared"
    android:sharedUserId="android.media"
 这些刚好与上面的mk文件里的配置对应上。
 在Android源码的build/target/product/security/目录下有如下的4对KEY:
    1、media.pk8与media.x509.pem;
    2、platform.pk8与platform.x509.pem;
    3、shared.pk8与shared.x509.pem;
    4、testkey.pk8与testkey.x509.pem;
其中,“*.pk8”文件为私钥,“*.x509.pem”文件为公钥,这需要去了解非对称加密方式。

------------------------------------------------------------------------------------------------------------------------------

(7) include $(BUILD_PACKAGE)

在(2.2)中已作解释

------------------------------------------------------------------------------------------------------------------------------

(8) include $(call all-makefiles-under,$(LOCAL_PATH))

加载当前目录下的所有makefile文件,all-makefiles-under会返回一个位于当前'my-dir'路径的子目录中的所有Android.mk的列表。

all-makefiles-under宏的定义是在build/core/definitions.mk中。

------------------------------------------------------------------------------------------------------------------------------

 

这个Android.mk文件最后就生成了Settings.apk。分析完上面的Android.mk文件后,来总结下各种LOCAL_XXX。

三种情况说明:

    必须定义, 在app或package的Android.mk中必须给定值。

    可选定义,在app或package的Android.mk中可以也可以不给定值。

    不用定义,在app或package的Android.mk中不要给定值,脚本自动指定值。

 

复制代码
LOCAL_PATH,          当前路径,必须定义。
LOCAL_PACKAGE_NAME,  必须定义,package的名字,这个名字在脚本中将标识app或package。
LOCAL_MODULE_SUFFIX, 不用定义,module的后缀,=.apk。
LOCAL_MODULE,        不用定义,=$(LOCAL_PACKAGE_NAME)。
LOCAL_JAVA_RESOURCE_DIRS,     不用定义。
LOCAL_JAVA_RESOURCE_FILES,    不用定义。
LOCAL_MODULE_CLASS,  不用定义。
LOCAL_MODULE_TAGS,   可选定义。默认optional。取值范围user debug eng tests optional samples shell_ash shell_mksh。
LOCAL_ASSET_DIR,     可选定义,推荐不定义。默认$(LOCAL_PATH)/assets
LOCAL_RESOURCE_DIR,  可选定义,推荐不定义。默认product package和device package相应的res路径和$(LOCAL_PATH)/res。
LOCAL_PROGUARD_ENABLED,       可选定义,默认为full,如果是user或userdebug。取值full, disabled, custom。
full_android_manifest,        不用定义,=$(LOCAL_PATH)/AndroidManifest.xml。
LOCAL_EXPORT_PACKAGE_RESOURCES,    可选定义,默认null。如果允许app的资源被其它模块使用,则设置true。
LOCAL_CERTIFICATE,   可选定义,默认为testkey。最终
        private_key := $(LOCAL_CERTIFICATE).pk8
        certificate := $(LOCAL_CERTIFICATE).x509.pem
复制代码

 

 

扩展:在一个Android.mk中可以生成多个可执行程序、动态库和静态库。

(1)编译APK应用程序模板。
关于编译APK应用程序的模板请参照《Android.mk编译APK范例
 
(2)编译JAVA库模板
复制代码
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Build all java files in the java subdirectory
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Any libraries that this library depends on
LOCAL_JAVA_LIBRARIES := android.test.runner # The name of the jar file to create LOCAL_MODULE := sample # Build a static jar file. include $(BUILD_STATIC_JAVA_LIBRARY)
复制代码
   :LOCAL_JAVA_LIBRARIES := android.test.runner表示生成的JAVA库的jar文件名
 
(3)编译C/C++应用程序模板如下
复制代码
LOCAL_PATH := $(call my-dir)
#include $(CLEAR_VARS)
LOCAL_SRC_FILES := main.c
LOCAL_MODULE := test_exe
#LOCAL_C_INCLUDES :=
#LOCAL_STATIC_LIBRARIES := #LOCAL_SHARED_LIBRARIES := include $(BUILD_EXECUTABLE)
复制代码

 :‘:=’是赋值的意思,'+='是追加的意思,‘$’表示引用某变量的值

LOCAL_SRC_FILES中加入源文件路径,LOCAL_C_INCLUDES中加入需要的头文件搜索路径
LOCAL_STATIC_LIBRARIES 加入所需要链接的静态库(*.a)的名称,
LOCAL_SHARED_LIBRARIES 中加入所需要链接的动态库(*.so)的名称,
LOCAL_MODULE表示模块最终的名称,BUILD_EXECUTABLE 表示以一个可执行程序的方式进行编译。

 

(4)编译C\C++静态库
复制代码
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
 helloworld.c
LOCAL_MODULE:= libtest_static
 #LOCAL_C_INCLUDES :=
#LOCAL_STATIC_LIBRARIES := #LOCAL_SHARED_LIBRARIES := include $(BUILD_STATIC_LIBRARY)
复制代码

 和上面相似,BUILD_STATIC_LIBRARY 表示编译一个静态库。

(5)编译C/C++动态库的模板
复制代码
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := helloworld.c
LOCAL_MODULE := libtest_shared
TARGET_PRELINK_MODULES := false #LOCAL_C_INCLUDES := #LOCAL_STATIC_LIBRARIES := #LOCAL_SHARED_LIBRARIES := include $(BUILD_SHARED_LIBRARY)
复制代码

和上面相似,BUILD_SHARED_LIBRARY 表示编译一个共享库。

以上三者的生成结果分别在如下目录中,generic 依具体 target 会变(可能是dkb~~):
out/target/product/generic/obj/APPS
out/target/product/generic/obj/JAVA_LIBRARIES
out/target/product/generic/obj/EXECUTABLE
out/target/product/generic/obj/STATIC_LIBRARY
out/target/product/generic/obj/SHARED_LIBRARY

每个模块的目标文件夹分别为:

1)APK程序:XXX_intermediates
2)JAVA库程序:XXX_intermediates
3)C\C++可执行程序:XXX_intermediates
4)C\C++静态库: XXX_static_intermediates
5)C\C++动态库: XXX_shared_intermediates

 

 

http://hubingforever.blog.163.com/blog/static/171040579201152185542166/

Android.mk文件是GNU Makefile的一小部分,它用来对Android程序进行编译。
因为所有的编译文件都在同一个 GNU MAKE 执行环境中进行执行,而Android.mk中所有的变量都是全局的。因此,您应尽量少声明变量,不要认为某些变量在解析过程中不会被定义。
一个Android.mk文件可以编译多个模块, 每个模块属下列类型之一
  1)APK程序
  一般的Android程序,编译打包生成apk文件
  2)JAVA库
  java类库,编译打包生成jar文件
  3)C\C++应用程序
 可执行的C\C++应用程序
  4)C\C++静态库 
编译生成C\C++静态库,并打包成.a文件
  5)C\C++共享库
编译生成共享库(动态链接库),并打包成.so文, 有且只有共享库才能被安装/复制到您的应用软件(APK)包中。
  可以在每一个Android.mk file 中定义一个或多个模块,你也可以在几个模块中使用同一个
源代码文件。  编译系统为你处理许多细节问题。例如,你不需要在你的 Android.mk 中列出头文件和依
赖文件。编译系统将会为你自动处理这些问题。这也意味着,在升级 NDK 后,你应该
得到新的toolchain/platform支持,而且不需要改变你的 Android.mk 文件。
  注意,NDK的Anroid.mk语法同公开发布的Android平台开源代码的Anroid.mk语法很接近,然而编译系统实现他们的
方式却是不同的,这是故意这样设计的,可以让程序开发人员重用外部库的源代码更容易。
  在描述语法细节之前,咱们来看一个简单的"hello world"的例子,比如,下面的文件:
 sources/helloworld/helloworld.c
 sources/helloworld/Android.mk
 'helloworld.c'是一个 JNI 共享库 ,实现返回"hello world"字符串的原生方法。相应的
Android.mk 文件会象下面这样:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= helloworld
LOCAL_SRC_FILES := helloworld.c
include $(BUILD_SHARED_LIBRARY)
  解释一下几行代码:
  LOCAL_PATH := $(call my-dir)
  一个Android.mk file首先必须定义好LOCAL_PATH变量。它表示是当前文件的路径。
在这个例子中, 宏函数‘my-dir’,  由编译系统提供, 用于返回当前路径(即包含Android.mk file
文件的目录)
  include $(CLEAR_VARS)
  CLEAR_VARS 由编译系统提供(可以在 android 安装目录下的 /build/core/config.mk 文件看 到其定义,为 CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk),指定让GNU MAKEFILE该脚本为你清除许多 LOCAL_XXX 变量 ( 例如 LOCAL_MODULE , LOCAL_SRC_FILES ,LOCAL_STATIC_LIBRARIES,等等…),除 LOCAL_PATH。这是必要的,因为所有的编译文件都在同一个 GNU MAKE 执行环境中,所有的变量都是全局的。所以我们需要先清空这些变量(LOCAL_PATH除外)。又因为LOCAL_PATH总是要求在每个模块中都要进行设置,所以并需要清空它。
另外 注意 ,该语句的意思就是把CLEAR_VARS变量所指向的脚本文件包含进来。
  LOCAL_MODULE := helloworld
  LOCAL_MODULE 变量必须定义,以标识你在 Android.mk 文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。注意编译系统会自动产生合适的前缀和后缀,换句话说,一个被命名为'foo'的共享库模 块,将会生成'libfoo.so'文件。
注意 :如果把库命名为‘libhelloworld’,编译系统将不会添加任何的 lib 前缀,也会生成 libhelloworld.so。
  LOCAL_SRC_FILES := helloworld.c
  LOCAL_SRC_FILES 变量必须包含将要编译打包进模块中的 C 或 C++源代码文件。不用
在这里列出头文件和包含文件,编译系统将会自动找出依赖型的文件,当然对于包含文件,你包含时指定的路径应该正确。
注意 ,默认的 C++源码文件的扩展名是‘.cpp’ 。指定一个不同的扩展名也是可能的,只要定义 LOCAL_DEFAULT_CPP_EXTENSION 变量,不要忘记开始的小圆点(也就是定义为  ‘.cxx’,而不是‘cxx’)
  include $(BUILD_SHARED_LIBRARY)
  BUILD_SHARED_LIBRARY 是编译系统提供的变量,指向一个 GNU Makefile 脚本(应该
就是在 build/core  目录下的 shared_library.mk) ,将根据LOCAL_XXX系列变量中的值,来编译生成共享库(动态链接库)。
如果想生成静态库,则用BUILD_STATIC_LIBRARY
  在NDK的sources/samples目录下有更复杂一点的例子,写有注释的 Android.mk 文件。
二、自定义变量
 以下是在 Android.mk中依赖或定义的变量列表, 可以定义其他变量为自己使用,但是NDK编译系统保留下列变量名:
 -以 LOCAL_开头的名字(例如 LOCAL_MODULE)
 -以 PRIVATE_, NDK_ 或 APP_开头的名字(内部使用)
 -小写名字(内部使用,例如‘my-dir’)
  如果为了方便在 Android.mk 中定义自己的变量,建议使用 MY_前缀,一个小例子
MY_SOURCES := foo.c
ifneq ($(MY_CONFIG_BAR),)
 MY_SOURCES += bar.c
endif
LOCAL_SRC_FILES += $(MY_SOURCES)
注意:‘:=’是赋值的意思;'+='是追加的意思;‘$’表示引用某变量的值。
三、GNU Make系统变量
  这些 GNU Make变量在你的 Android.mk 文件解析之前,就由编译系统定义好了。注意在
某些情况下,NDK可能分析 Android.mk 几次,每一次某些变量的定义会有不同。
  (1)CLEAR_VARS:  指 向一个编译脚本,几乎所有未定义的 LOCAL_XXX 变量都在"Module-description"节中列出。必须在开始一个新模块之前包含这个脚本:include$(CLEAR_VARS),用于重 置除LOCAL_PATH变量外的,所有LOCAL_XXX系列变量。
  (2)BUILD_SHARED_LIBRARY:   指向编译脚本,根据所有的在 LOCAL_XXX 变量把列出的源代码文件编译成一个共享库。
  注意,必须至少在包含这个文件之前定义 LOCAL_MODULE 和 LOCAL_SRC_FILES。
(3) BUILD_STATIC_LIBRARY:  一个 BUILD_SHARED_LIBRARY 变量用于编译一个静态库。静态库不会复制到的APK包中,但是能够用于编译共享库。
示例 include $( BUILD_STATIC_LIBRARY) 
注意,这将会生成一个名为 lib$(LOCAL_MODULE).a 的文件
  (4)TARGET_ARCH: 目标 CPU平台的名字,  和 android 开放源码中指定的那样。如果是
arm,表示要生成 ARM 兼容的指令,与 CPU架构的修订版无关。
  (5)TARGET_PLATFORM: Android.mk 解析的时候,目标 Android 平台的名字. 详情可参考/development/ndk/docs/stable- apis.txt.
 android-3 -> Official Android 1.5 system images
 android-4 -> Official Android 1.6 system images
 android-5 -> Official Android 2.0 system images
  (6)TARGET_ARCH_ABI:   暂 时只支持两个 value,armeabi 和 armeabi-v7a。在现在的版本中一般把这两个值简单的定义为 arm, 通过 android  平台内部对它重定义来获得更好的匹配。其他的 ABI 将在以后的 NDK 版本中介绍,它们会有不同的名字。注意虽然所有基于
ARM的ABI都会把 'TARGET_ARCH'定义成‘arm’, 但是会有不同的‘TARGET_ARCH_ABI’。 
( 7 ) TARGET_ABI:   目标平台和 ABI 的组合,它事实上被定义成$(TARGET_PLATFORM)-$(TARGET_ARCH_ABI)  ,在想要在真实的设备中针对一个特别的目标系统进行测试时,会有用。在默认的情况下,它会是'android-3-arm'。
五、模块描述变量
  下面的变量用于向编译系统描述你的模块。你应该定义在'include  $(CLEAR_VARS)'和'include $(BUILD_XXXXX)'之间。正如前面描写的那样,$(CLEAR_VARS)是一个脚本,清除所有这些变量
  (1) LOCAL_PATH:   这 个变量用于给出当前文件的路径。必须在 Android.mk 的开头定义,可以这样使用:LOCAL_PATH := $(call my-dir)  这个变量不会被$(CLEAR_VARS)清除,因此每个 Android.mk 只需要定义一次(即使在一个文件中定义了几个模块的情况下)。
  (2)LOCAL_MODULE: 这是模块的名字,它必须是唯一的,而且不能包含空格。必须在包含任一的$(BUILD_XXXX)脚本之前定义它。模块的名字决定了生成文件的名字。例如,如果一个一个共享库模块的名字是,那么生成文件的名字就是 lib.so。但是,在的 NDK 生成文
件中(或者 Android.mk 或者 Application.mk),应该只涉及(引用)有正常名字的其他模块。
  (3)LOCAL_SRC_FILES:   这是要编译的源代码文件列表。只要列出要传递给编译器的文件,因为编译系统自动计算依赖。注意源代码文件名称都是相对于 LOCAL_PATH的,你可以使用路径部分,例如:
LOCAL_SRC_FILES := foo.c toto/bar.c\
Hello.c
 文件之间可以用空格或Tab键进行分割,换行请用"\".如果是追加源代码文件的话,请用LOCAL_SRC_FILES +=
注意在生成文件中都要使用UNIX风格的斜杠(/).windows风格的反斜杠不会被正确的处理。
注意: 可以LOCAL_SRC_FILES := $(call all-subdir-java-files)这种形式来包含local_path目录下的所有java文件。
  (4) LOCAL_CPP_EXTENSION:   这是一个可选变量, 用来指定C++代码文件的扩展名,默认是'.cpp',但是可以改变它,比如:
LOCAL_CPP_EXTENSION := .cxx
  (5) LOCAL_C_INCLUDES:   可选变量,表示头文件的搜索路径。默认的头文件的搜索路径是LOCAL_PATH目录。
  示例: LOCAL_C_INCLUDES := sources/foo或LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo
  LOCAL_C_INCLUDES需要在任何包含LOCAL_CFLAGS/LOCAL_CPPFLAGS标志之前进行设置。
  (6)LOCAL_CFLAGS:   可选的编译器选项,在编译 C 代码文件的时候使用。这可能是有
用的,指定一个附加的包含路径(相对于NDK的顶层目录),宏定义,或者编译选项。
  注意不要在 Android.mk 中改变 optimization/debugging 级别,只要在 Application.mk 中指定合适的信息,就会自动地为你处理这个问题,在调试期间,会让 NDK自动生成有用的数据文件
  (7)LOCAL_CXXFLAGS:  与 LOCAL_CFLAGS同理,针对 C++源文件。
  (8)LOCAL_CPPFLAGS:   与 LOCAL_CFLAGS同理,但是对 C 和 C++ source files都适 用。
  (9)LOCAL_STATIC_LIBRARIES: 表示该模块需要使用哪些静态库,以便在编译时进行链接。
  (10)LOCAL_SHARED_LIBRARIES:  表示模块在运行时要依赖的共享库(动态库),在链接时就需要,以便在生成文件时嵌入其相应的信息。注意:它不会附加列出的模块到编译图,也就是仍然需要在Application.mk 中把它们添加到程序要求的模块中。
  (11)LOCAL_LDLIBS:  编译模块时要使用的附加的链接器选项。这对于使用‘ -l’ 前缀传递指定库的名字是有用的。
例如, LOCAL_LDLIBS := -lz 表示 告诉链接器生成的模块要在加载时刻链接到/system/lib/libz.so
  可查看 docs/STABLE-APIS.TXT 获取使用 NDK发行版能链接到的开放的系统库列表。
(注意:LOCAL_LDLIBS与LOCAL_STATIC_LIBRARIES及LOCAL_SHARED_LIBRARIES的区别是,LOCAL_LDLIBS是没有源代码或代码未被包含到Android.mk的库,如库不存在,则会报错找不到; 而其余两个是有代码且被包含到Android.mk的库里了,当她们的代码发生变化时候或当库不存在时,会自动重新编译他们的代码)
  (12) LOCAL_ALLOW_UNDEFINED_SYMBOLS:  默认情况下, 在试图编译一个共享库时,任何未定义的引用将导致一个“未定义的符号”错误。这对于在源代码文件中捕捉错误会有很大的帮助。然而,如果因为某些原因,需要不启动这项检查,可把这个变量设为‘true’。
注意相应的共享库可能在运行时加载失败。(这个一般尽量不要去设为 true)。
  (13) LOCAL_ARM_MODE: 默认情况下, arm目标二进制会以 thumb 的形式生成(16 位),你可以通过设置这个变量为 arm如果你希望你的 module 是以 32 位指令的形式。
'arm' (32-bit instructions) mode. E.g.:
LOCAL_ARM_MODE := arm
注意 :可以在编译的时候告诉系统针对某个源码文件进行特定的类型的编译
比如,LOCAL_SRC_FILES := foo.c bar.c.arm  这样就告诉系统总是将 bar.c 以 arm的模式编译。
(14)LOCAL_MODULE_PATH 和 LOCAL_UNSTRIPPED_PATH
在 Android.mk 文件中, 还可以用 LOCAL_MODULE_PATH LOCAL_UNSTRIPPED_PATH 指定最后的目标安装路径.
不同的文件系统路径用以下的宏进行选择:
  TARGET_ROOT_OUT:表示根文件系统。
    TARGET_OUT:表示 system文件系统。
   TARGET_OUT_DATA:表示 data文件系统。
用法如: LOCAL_MODULE_PATH :=$(TARGET_ROOT_OUT) 
至于LOCAL_MODULE_PATH 和LOCAL_UNSTRIPPED_PATH的区别,暂时还不清楚。
七、GNU Make‘功能’宏
GNU Make‘功能’宏,必须通过使用'$(call  )'来调用,调用他们将返回文本化的信息。
(1)my-dir: 返回当前 Android.mk 所在的目录的路径,相对于 NDK 编译系统的顶层。这是有用的,在 Android.mk 文件的开头如此定义:
LOCAL_PATH := $(call my-dir)
(2)all-subdir-makefiles: 返回一个位于当前'my-dir'路径的子目录中的所有Android.mk的列表
例如,看下面的目录层次:
sources/foo/Android.mk
sources/foo/lib1/Android.mk
sources/foo/lib2/Android.mk
 如果 sources/foo/Android.mk 包含一行:
include $(call all-subdir-makefiles)
那么它就会自动包含 sources/foo/lib1/Android.mk 和 sources/foo/lib2/Android.mk。
这项功 能用于向编译系统提供深层次嵌套的代码目录层次。
注意在默认情况下,NDK 将会只搜索在 sources/*/Android.mk 中的文件。
(3)this-makefile:   返回当前Makefile 的路径(即这个函数调用的地方)
(4)parent-makefile:   返回调用树中父 Makefile 路径。即包含当前Makefile的Makefile 路径。
(5)grand-parent-makefile返回调用树中父Makefile的父Makefile的路径
八、 Android.mk 使用模板
  在一个 Android.mk 中可以生成多个APK应用程序,JAVA库,C\C++可执行程序,C\C++动态库和C\C++静态库。
1)编译APK应用程序模板。
关于编译APK应用程序的模板请参照Android.mk编译APK范例
(2)编译JAVA库模板
  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)
  # Build all java files in the java subdirectory
  LOCAL_SRC_FILES := $(call all-subdir-java-files)
  # Any libraries that this library depends on
  LOCAL_JAVA_LIBRARIES := android.test.runner
  # The name of the jar file to create
  LOCAL_MODULE := sample
  # Build a static jar file.
  include $( BUILD_STATIC_JAVA_LIBRARY )
  :LOCAL_JAVA_LIBRARIES := android.test.runner表示生成的JAVA库的jar文件名
(3)编译C/C++应用程序模板如下
LOCAL_PATH := $(call my-dir)
#include $(CLEAR_VARS)
LOCAL_SRC_FILES := main.c
LOCAL_MODULE := test_exe
#LOCAL_C_INCLUDES :=
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
include $( BUILD_EXECUTABLE )
:‘:=’是赋值的意思,'+='是追加的意思,‘$’表示引用某变量的值
LOCAL_SRC_FILES中加入源文件路径,LOCAL_C_INCLUDES中加入需要的头文件搜索路径
LOCAL_STATIC_LIBRARIES 加入所需要链接的 静态库(*.a) 的名称,
LOCAL_SHARED_LIBRARIES 中加入所需要链接的 动态库(*.so) 的名称,
LOCAL_MODULE 表示模块最终的名称, BUILD_EXECUTABLE 表示以一个可执行程序的方式进行编译。
(4)编译C\C++静态库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
 helloworld.c
LOCAL_MODULE:= libtest_static
 #LOCAL_C_INCLUDES :=
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
include $( BUILD_STATIC_LIBRARY )
和上面相似,BUILD_STATIC_LIBRARY 表示编译一个静态库。
(5)编译C\C++动态库的模板
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := helloworld.c
LOCAL_MODULE := libtest_shared
TARGET_PRELINK_MODULES := false
#LOCAL_C_INCLUDES :=
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
include $( BUILD_SHARED_LIBRARY )
和上面相似,BUILD_SHARED_LIBRARY 表示编译一个共享库。
以上三者的生成结果分别在如下目录中,generic 依具体 target 会变
out/target/product/generic/obj/APPS
out/target/product/generic/obj/JAVA_LIBRARIES
out/target/product/generic/obj/EXECUTABLE
out/target/product/generic/obj/STATIC_LIBRARY
out/target/product/generic/obj/SHARED_LIBRARY
每个模块的目标文件夹分别为
1) APK程序 :XXX_intermediates
2) JAVA库程序 :XXX_intermediates
这里的XXX
 3) C\C++可执行程序 :XXX_intermediates
 4) C\C++静态库 : XXX_static_intermediates
 5) C\C++动态库 : XXX_shared_intermediates
 
 
 

Android Android.mk 文件一点感悟

Android.mk文件时android 中的一个非常重要的概念。我们有以下几个方便的时候需要使用它。

1:添加新的apk源码文件编译。这里我们先参考下AlarmClock的Android.mk文件

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES := $(call all-subdir-java-files)

LOCAL_PACKAGE_NAME := AlarmClock

include $(BUILD_PACKAGE)



简单的解释下:
第一行,赋予LOCAL_PATH一个新的值,表示当前的路径。
第二行,清楚所有的系统自带的标准变量值。我们通过查找CLEAR_VARS变量定义,可以发现它实际上代表的是
source\build\core\config.mk中有定义如下:
CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk
所以查看clear_vars.mk文件可以得到知道:


###########################################################
## Clear out values of all variables used by rule templates.
###########################################################

LOCAL_MODULE:=
LOCAL_MODULE_PATH:=
LOCAL_MODULE_STEM:=
LOCAL_DONT_CHECK_MODULE:=
LOCAL_CHECKED_MODULE:=
LOCAL_BUILT_MODULE:=
LOCAL_BUILT_MODULE_STEM:=
OVERRIDE_BUILT_MODULE_PATH:=
....



它把这些变量都清空了。
注意它这里没有去清空LOCAL_PATH变量。所以这个步骤放在第二行,是没有关系的。它的这个写法有点误导人哈。

第三、四行,给变量赋予新值。这里赋予的是LOCAL_SRC_FILES,LOCAL_PACKAGE_NAME
第五行,执行BUILD_PACKAGE。它的定义也是在config.mk中定义如下:
BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.mk,更多更加具体的,自己去看看吧。

这里其实有很多的,
# ###############################################################
# Build system internal files
# ###############################################################

BUILD_COMBOS:= $(BUILD_SYSTEM)/combo

CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk
BUILD_HOST_STATIC_LIBRARY:= $(BUILD_SYSTEM)/host_static_library.mk
BUILD_HOST_SHARED_LIBRARY:= $(BUILD_SYSTEM)/host_shared_library.mk
BUILD_STATIC_LIBRARY:= $(BUILD_SYSTEM)/static_library.mk
BUILD_RAW_STATIC_LIBRARY := $(BUILD_SYSTEM)/raw_static_library.mk
BUILD_SHARED_LIBRARY:= $(BUILD_SYSTEM)/shared_library.mk
BUILD_EXECUTABLE:= $(BUILD_SYSTEM)/executable.mk
BUILD_RAW_EXECUTABLE:= $(BUILD_SYSTEM)/raw_executable.mk
BUILD_HOST_EXECUTABLE:= $(BUILD_SYSTEM)/host_executable.mk
BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.mk
BUILD_HOST_PREBUILT:= $(BUILD_SYSTEM)/host_prebuilt.mk
BUILD_PREBUILT:= $(BUILD_SYSTEM)/prebuilt.mk
BUILD_MULTI_PREBUILT:= $(BUILD_SYSTEM)/multi_prebuilt.mk
BUILD_JAVA_LIBRARY:= $(BUILD_SYSTEM)/java_library.mk
BUILD_STATIC_JAVA_LIBRARY:= $(BUILD_SYSTEM)/static_java_library.mk
BUILD_HOST_JAVA_LIBRARY:= $(BUILD_SYSTEM)/host_java_library.mk
BUILD_DROIDDOC:= $(BUILD_SYSTEM)/droiddoc.mk
BUILD_COPY_HEADERS := $(BUILD_SYSTEM)/copy_headers.mk
BUILD_KEY_CHAR_MAP := $(BUILD_SYSTEM)/key_char_map.mk

基本上你有用到的就已经提前给你设计好了。你要做的很简单了。


这里的思想其实和makefile的是一样的。

目标:信赖文件
执行命令

只是android在这里进行了高度的封装和简化。
前面四行相当于设置目标文件。
最后一行相当于执行命令。

所以,如果我们要添加自己的apk,就按照这种思想添加就可以了。


2:变量的使用
makefile文件有一个基础的东西就是变量。大家可以自己了解下。这里我们说说android.mk这个变量的不同的地方。主要是用它来做代码管理。这个部分是我同事小强总结出来的,我就cp过来。
替它总结下。

首先在执行make文件的时候这些变量是全局有效的,一些公共的变量会通过include $(CLEAR_VARS)给清除掉。我们这里主要是添加自己的变量。
我们在使用自己定义变量的时候有两种情况,一种是在mk中使用,一种是在cpp中使用。注意java不支持的。
这两种情况有点不同,请注意。

首先我们得定义这个变量,一般来讲都是在产品的BoardConfig.mk中定义,例如:


TARGET_MEMORY_STYLE := TARGET_MEM_256MB
#TARGET_MEMORY_STYLE := TARGET_MEM_256MB_ST
#TARGET_MEMORY_STYLE := TARGET_MEM_512MB
#TARGET_MEMORY_STYLE := TARGET_MEM_512MB_ST

# board-specific configuration
BOARD_OPENCORE_FLAGS := -DTELECHIPS_ALSA

ifeq ($(BOARD_KERNEL_BASE),0x40000000)
BOARD_MEM_FLAGS := -DTCC_MEM_256MB
else
BOARD_MEM_FLAGS := -DTCC_MEM_512MB
endif


这里总共有两个我们经常用的东西。

第一:
   
    如果是mk总使用,定义如前四行就可以了。
    变量 := 字符串
   
    在mk中的使用方法就像后面例子一样。
    ifeq($(变量),对比值)
   
    else
   
    endif
   
   
第二:如果你想在cpp中使用,请定义方式如下
   
    变量 := -D+变量名
 
  如后面几行示例一样的。
 
    如果想在cpp中应用,首先必须在cpp对于的mk文件中,声明如下
    LOCAL_CFLAGS += $(BOARD_MEM_FLAGS)
       
    格式:   
    LOCAL_CFLAGS += $(变量)
       
   
    然后它的cpp中就可以引用如下:
    此时去掉了-D前缀。
   
    ifdef TCC_MEM_512MB
    ......
    endif
   
    特此感谢小强同学的代码控制.doc。

 

 

http://www.ibm.com/developerworks/cn/opensource/os-cn-android-build/

理解 Android Build 系统

Android Build 系统是用来编译 Android 系统,Android SDK 以及相关文档的一套框架。众所周知,Android 是一个开源的操作系统。Android 的源码中包含了许许多多的模块。 不同产商的不同设备对于 Android 系统的定制都是不一样的。如何将这些模块统一管理起来,如何能够在不同的操作系统上进行编译,如何在编译时能够支持面向不同的硬件设备,不同的编译类型, 且还要提供面向各个产商的定制扩展,是非常有难度的。 但 Android Build 系统很好的解决了这些问题,这里面有很多值得我们开发人员学习的地方。对于 Android 平台开发人员来说,本文可以帮助你熟悉你每天接触到的构建环境。对于其他开发人员来说,本文可以作为一个 GNU Make 的使用案例,学习这些成功案例,可以提升我们的开发经验。

前言

Android Build 系统是 Android 源码的一部分。关于如何获取 Android 源码,请参照 Android Source 官方网站:

http://source.android.com/source/downloading.html

Android Build 系统用来编译 Android 系统,Android SDK 以及相关文档。该系统主要由 Make 文件,Shell 脚本以及 Python 脚本组成,其中最主要的是 Make 文件。

众所周知,Android 是一个开源的操作系统。Android 的源码中包含了大量的开源项目以及许多的模块。不同产商的不同设备对于 Android 系统的定制都是不一样的。

如何将这些项目和模块的编译统一管理起来,如何能够在不同的操作系统上进行编译,如何在编译时能够支持面向不同的硬件设备,不同的编译类型,且还要提供面向各个产商的定制扩展,是非常有难度的。

但 Android Build 系统很好的解决了这些问题,这里面有很多值得我们开发人员学习的地方。

对于 Android 平台开发人员来说,本文可以帮助你熟悉你每天接触到的构建环境。

对于其他开发人员来说,本文可以作为一个 GNU Make 的使用案例,学习这些成功案例,可以提升我们的开发经验。

概述

Build 系统中最主要的处理逻辑都在 Make 文件中,而其他的脚本文件只是起到一些辅助作用,由于篇幅所限,本文只探讨 Make 文件中的内容。

整个 Build 系统中的 Make 文件可以分为三类:

第一类是 Build 系统核心文件,此类文件定义了整个 Build 系统的框架,而其他所有 Make 文件都是在这个框架的基础上编写出来的。

图 1 是 Android 源码树的目录结构,Build 系统核心文件全部位于 /build/core(本文所提到的所有路径都是以 Android 源码树作为背景的,“/”指的是源码树的根目录,与文件系统无关)目录下。

图 1. Android 源码树的目录结构

图 1. Android 源码树的目录结构

第 二类是针对某个产品(一个产品可能是某个型号的手机或者平板电脑)的 Make 文件,这些文件通常位于 device 目录下,该目录下又以公司名以及产品名分为两级目录,图 2 是 device 目录下子目录的结构。对于一个产品的定义通常需要一组文件,这些文件共同构成了对于这个产品的定义。例如,/device/sony/it26 目录下的文件共同构成了对于 Sony LT26 型号手机的定义。

图 2. device 目录下子目录的结构

图 2. device 目录下子目录的结构

第 三类是针对某个模块(关于模块后文会详细讨论)的 Make 文件。整个系统中,包含了大量的模块,每个模块都有一个专门的 Make 文件,这类文件的名称统一为“Android.mk”,该文件中定义了如何编译当前模块。Build 系统会在整个源码树中扫描名称为“Android.mk”的文件并根据其中的内容执行模块的编译。

编译 Android 系统

执行编译

Android 系统的编译环境目前只支持 Ubuntu 以及 Mac OS 两种操作系统。关于编译环境的构建方法请参见以下路径:http://source.android.com/source/initializing.html

在完成编译环境的准备工作以及获取到完整的 Android 源码之后,想要编译出整个 Android 系统非常的容易:

打开控制台之后转到 Android 源码的根目录,然后执行如清单 1 所示的三条命令即可("$"是命令提示符,不是命令的一部分。):

完整的编译时间依赖于编译主机的配置,在笔者的 Macbook Pro(OS X 10.8.2, i7 2G CPU,8G RAM, 120G SSD)上使用 8 个 Job 同时编译共需要一个半小时左右的时间。

清单 1. 编译 Android 系统
 $ source build/envsetup.sh 
 $ lunch full-eng 
 $ make -j8

这三行命令的说明如下:

第一行命令“source build/envsetup.sh”引入了 build/envsetup.sh脚本。该脚本的作用是初始化编译环境,并引入一些辅助的 Shell 函数,这其中就包括第二步使用 lunch 函数。

除此之外,该文件中还定义了其他一些常用的函数,它们如表 1 所示:

表 1. build/envsetup.sh 中定义的常用函数
名称说明
croot切换到源码树的根目录
m在源码树的根目录执行 make
mmBuild 当前目录下的模块
mmmBuild 指定目录下的模块
cgrep在所有 C/C++ 文件上执行 grep
jgrep在所有 Java 文件上执行 grep
resgrep在所有 res/*.xml 文件上执行 grep
godir转到包含某个文件的目录路径
printconfig显示当前 Build 的配置信息
add_lunch_combo在 lunch 函数的菜单中添加一个条目

 

第二行命令“lunch full-eng”是调用 lunch 函数,并指定参数为“full-eng”。lunch 函数的参数用来指定此次编译的目标设备以及编译类型。在这里,这两个值分别是“full”和“eng”。“full”是 Android 源码中已经定义好的一种产品,是为模拟器而设置的。而编译类型会影响最终系统中包含的模块,关于编译类型将在表 7 中详细讲解。

如果调用 lunch 函数的时候没有指定参数,那么该函数将输出列表以供选择,该列表类似图 3 中的内容(列表的内容会根据当前 Build 系统中包含的产品配置而不同,具体参见后文“添加新的产品”),此时可以通过输入编号或者名称进行选择。

图 3. lunch 函数的输出

图 3. lunch 函数的输出

第三行命令“make -j8”才真正开始执行编译。make 的参数“-j”指定了同时编译的 Job 数量,这是个整数,该值通常是编译主机 CPU 支持的并发线程总数的 1 倍或 2 倍(例如:在一个 4 核,每个核支持两个线程的 CPU 上,可以使用 make -j8 或 make -j16)。在调用 make 命令时,如果没有指定任何目标,则将使用默认的名称为“droid”目标,该目标会编译出完整的 Android 系统镜像。

Build 结果的目录结构

所有的编译产物都将位于 /out 目录下,该目录下主要有以下几个子目录:

  • /out/host/:该目录下包含了针对主机的 Android 开发工具的产物。即 SDK 中的各种工具,例如:emulator,adb,aapt 等。
  • /out/target/common/:该目录下包含了针对设备的共通的编译产物,主要是 Java 应用代码和 Java 库。
  • /out/target/product/<product_name>/:包含了针对特定设备的编译结果以及平台相关的 C/C++ 库和二进制文件。其中,<product_name>是具体目标设备的名称。
  • /out/dist/:包含了为多种分发而准备的包,通过“make disttarget”将文件拷贝到该目录,默认的编译目标不会产生该目录。

Build 生成的镜像文件

Build 的产物中最重要的是三个镜像文件,它们都位于 /out/target/product/<product_name>/ 目录下。

这三个文件是:

  • system.img:包含了 Android OS 的系统文件,库,可执行文件以及预置的应用程序,将被挂载为根分区。
  • ramdisk.img:在启动时将被 Linux 内核挂载为只读分区,它包含了 /init 文件和一些配置文件。它用来挂载其他系统镜像并启动 init 进程。
  • userdata.img:将被挂载为 /data,包含了应用程序相关的数据以及和用户相关的数据。

Make 文件说明

整个 Build 系统的入口文件是源码树根目录下名称为“Makefile”的文件,当在源代码根目录上调用 make 命令时,make 命令首先将读取该文件。

Makefile 文件的内容只有一行:“include build/core/main.mk”。该行代码的作用很明显:包含 build/core/main.mk 文件。在 main.mk 文件中又会包含其他的文件,其他文件中又会包含更多的文件,这样就引入了整个 Build 系统。

这些 Make 文件间的包含关系是相当复杂的,图 3 描述了这种关系,该图中黄色标记的文件(且除了 $开头的文件)都位于 build/core/ 目录下。

图 4. 主要的 Make 文件及其包含关系

图 4. 主要的 Make 文件及其包含关系

表 2 总结了图 4 中提到的这些文件的作用:

表 2. 主要的 Make 文件的说明
文件名说明
main.mk最主要的 Make 文件,该文件中首先将对编译环境进行检查,同时引入其他的 Make 文件。另外,该文件中还定义了几个最主要的 Make 目标,例如 droid,sdk,等(参见后文“Make 目标说明”)。
help.mk包含了名称为 help 的 Make 目标的定义,该目标将列出主要的 Make 目标及其说明。
pathmap.mk将许多头文件的路径通过名值对的方式定义为映射表,并提供 include-path-for 函数来获取。例如,通过 $(call include-path-for, frameworks-native)便可以获取到 framework 本地代码需要的头文件路径。
envsetup.mk 配置 Build 系统需要的环境变量,例如:TARGET_PRODUCT,TARGET_BUILD_VARIANT,HOST_OS,HOST_ARCH 等。
当前编译的主机平台信息(例如操作系统,CPU 类型等信息)就是在这个文件中确定的。
另外,该文件中还指定了各种编译结果的输出路径。
combo/select.mk 根据当前编译器的平台选择平台相关的 Make 文件。
dumpvar.mk 在 Build 开始之前,显示此次 Build 的配置信息。
config.mk 整个 Build 系统的配置文件,最重要的 Make 文件之一。该文件中主要包含以下内容:
  • 定义了许多的常量来负责不同类型模块的编译。
  • 定义编译器参数以及常见文件后缀,例如 .zip,.jar.apk。
  • 根据 BoardConfig.mk 文件,配置产品相关的参数。
  • 设置一些常用工具的路径,例如 flex,e2fsck,dx。
definitions.mk 最重要的 Make 文件之一,在其中定义了大量的函数。这些函数都是 Build 系统的其他文件将用到的。例如:my-dir,all-subdir-makefiles,find-subdir-files,sign- package 等,关于这些函数的说明请参见每个函数的代码注释。
distdir.mk 针对 dist 目标的定义。dist 目标用来拷贝文件到指定路径。
dex_preopt.mk 针对启动 jar 包的预先优化。
pdk_config.mk 顾名思义,针对 pdk(Platform Developement Kit)的配置文件。
${ONE_SHOT_MAKEFILE} ONE_SHOT_MAKEFILE 是一个变量,当使用“mm”编译某个目录下的模块时,此变量的值即为当前指定路径下的 Make 文件的路径。
${subdir_makefiles} 各个模块的 Android.mk 文件的集合,这个集合是通过 Python 脚本扫描得到的。
post_clean.mk 在前一次 Build 的基础上检查当前 Build 的配置,并执行必要清理工作。
legacy_prebuilts.mk 该文件中只定义了 GRANDFATHERED_ALL_PREBUILT 变量。
Makefile 被 main.mk 包含,该文件中的内容是辅助 main.mk 的一些额外内容。

 

Android 源码中包含了许多的模块,模块的类型有很多种,例如:Java 库,C/C++ 库,APK 应用,以及可执行文件等 。并且,Java 或者 C/C++ 库还可以分为静态的或者动态的,库或可执行文件既可能是针对设备(本文的“设备”指的是 Android 系统将被安装的设备,例如某个型号的手机或平板)的也可能是针对主机(本文的“主机”指的是开发 Android 系统的机器,例如装有 Ubuntu 操作系统的 PC 机或装有 MacOS 的 iMac 或 Macbook)的。不同类型的模块的编译步骤和方法是不一样,为了能够一致且方便的执行各种类型模块的编译,在 config.mk 中定义了许多的常量,这其中的每个常量描述了一种类型模块的编译方式,这些常量有:

  • BUILD_HOST_STATIC_LIBRARY
  • BUILD_HOST_SHARED_LIBRARY
  • BUILD_STATIC_LIBRARY
  • BUILD_SHARED_LIBRARY
  • BUILD_EXECUTABLE
  • BUILD_HOST_EXECUTABLE
  • BUILD_PACKAGE
  • BUILD_PREBUILT
  • BUILD_MULTI_PREBUILT
  • BUILD_HOST_PREBUILT
  • BUILD_JAVA_LIBRARY
  • BUILD_STATIC_JAVA_LIBRARY
  • BUILD_HOST_JAVA_LIBRARY

通过名称大概就可以猜出每个变量所对应的模块类型。(在模块的 Android.mk 文件中,只要包含进这里对应的常量便可以执行相应类型模块的编译。对于 Android.mk 文件的编写请参见后文:“添加新的模块”。)

这 些常量的值都是另外一个 Make 文件的路径,详细的编译方式都是在对应的 Make 文件中定义的。这些常量和 Make 文件的是一一对应的,对应规则也很简单:常量的名称是 Make 文件的文件名除去后缀全部改为大写然后加上“BUILD_”作为前缀。例如常量 BUILD_HOST_PREBUILT 的值对应的文件就是 host_prebuilt.mk。

这些 Make 文件的说明如表 3 所示:

表 3. 各种模块的编译方式的定义文件
文件名 说明
host_static_library.mk 定义了如何编译主机上的静态库。
host_shared_library.mk 定义了如何编译主机上的共享库。
static_library.mk 定义了如何编译设备上的静态库。
shared_library.mk 定义了如何编译设备上的共享库。
executable.mk 定义了如何编译设备上的可执行文件。
host_executable.mk 定义了如何编译主机上的可执行文件。
package.mk 定义了如何编译 APK 文件。
prebuilt.mk 定义了如何处理一个已经编译好的文件 ( 例如 Jar 包 )。
multi_prebuilt.mk 定义了如何处理一个或多个已编译文件,该文件的实现依赖 prebuilt.mk。
host_prebuilt.mk 处理一个或多个主机上使用的已编译文件,该文件的实现依赖 multi_prebuilt.mk。
java_library.mk 定义了如何编译设备上的共享 Java 库。
static_java_library.mk 定义了如何编译设备上的静态 Java 库。
host_java_library.mk 定义了如何编译主机上的共享 Java 库。

 

不同类型的模块的编译过程会有一些相同的步骤,例如:编译一个 Java 库和编译一个 APK 文件都需要定义如何编译 Java 文件。因此,表 3 中的这些 Make 文件的定义中会包含一些共同的代码逻辑。为了减少代码冗余,需要将共同的代码复用起来,复用的方式是将共同代码放到专门的文件中,然后在其他文件中包含这 些文件的方式来实现的。这些包含关系如图 5 所示。由于篇幅关系,这里就不再对其他文件做详细描述(其实这些文件从文件名称中就可以大致猜出其作用)。

图 5. 模块的编译方式定义文件的包含关系

图 5. 模块的编译方式定义文件的包含关系

Make 目标说明

make /make droid

如果在源码树的根目录直接调用“make”命令而不指定任何目标,则会选择默认目标:“droid”(在 main.mk 中定义)。因此,这和执行“make droid”效果是一样的。

droid 目标将编译出整个系统的镜像。从源代码到编译出系统镜像,整个编译过程非常复杂。这个过程并不是在 droid 一个目标中定义的,而是 droid 目标会依赖许多其他的目标,这些目标的互相配合导致了整个系统的编译。

图 6 描述了 droid 目标所依赖的其他目标:

图 6. droid 目标所依赖的其他 Make 目标

图 6. droid 目标所依赖的其他 Make 目标

图 6 中这些目标的说明如表 4 所示:

表 4. droid 所依赖的其他 Make 目标的说明
名称 说明
apps_only 该目标将编译出当前配置下不包含 user,userdebug,eng 标签(关于标签,请参见后文“添加新的模块”)的应用程序。
droidcore 该目标仅仅是所依赖的几个目标的组合,其本身不做更多的处理。
dist_files 该目标用来拷贝文件到 /out/dist 目录。
files 该目标仅仅是所依赖的几个目标的组合,其本身不做更多的处理。
prebuilt 该目标依赖于 $(ALL_PREBUILT)$(ALL_PREBUILT)的作用就是处理所有已编译好的文件。
$(modules_to_install) modules_to_install 变量包含了当前配置下所有会被安装的模块(一个模块是否会被安装依赖于该产品的配置文件,模块的标签等信息),因此该目标将导致所有会被安装的模块的编译。
$(modules_to_check) 该目标用来确保我们定义的构建模块是没有冗余的。
$(INSTALLED_ANDROID_INFO_TXT_TARGET) 该目标会生成一个关于当前 Build 配置的设备信息的文件,该文件的生成路径是:out/target/product/<product_name>/android-info.txt
systemimage 生成 system.img。
$(INSTALLED_BOOTIMAGE_TARGET) 生成 boot.img。
$(INSTALLED_RECOVERYIMAGE_TARGET) 生成 recovery.img。
$(INSTALLED_USERDATAIMAGE_TARGET) 生成 userdata.img。
$(INSTALLED_CACHEIMAGE_TARGET) 生成 cache.img。
$(INSTALLED_FILES_FILE) 该目标会生成 out/target/product/<product_name>/ installed-files.txt 文件,该文件中内容是当前系统镜像中已经安装的文件列表。

 

其他目标

Build 系统中包含的其他一些 Make 目标说明如表 5 所示:

表 5. 其他主要 Make 目标
Make 目标 说明
make clean 执行清理,等同于:rm -rf out/。
make sdk 编译出 Android 的 SDK。
make clean-sdk 清理 SDK 的编译产物。
make update-api 更新 API。在 framework API 改动之后,需要首先执行该命令来更新 API,公开的 API 记录在 frameworks/base/api 目录下。
make dist 执行 Build,并将 MAKECMDGOALS 变量定义的输出文件拷贝到 /out/dist 目录。
make all 编译所有内容,不管当前产品的定义中是否会包含。
make help 帮助信息,显示主要的 make 目标。
make snod 从已经编译出的包快速重建系统镜像。
make libandroid_runtime 编译所有 JNI framework 内容。
makeframework 编译所有 Java framework 内容。
makeservices 编译系统服务和相关内容。
make <local_target> 编译一个指定的模块,local_target 为模块的名称。
make clean-<local_target> 清理一个指定模块的编译结果。
makedump-products 显示所有产品的编译配置信息,例如:产品名,产品支持的地区语言,产品中会包含的模块等信息。
makePRODUCT-xxx-yyy 编译某个指定的产品。
makebootimage 生成 boot.img
makerecoveryimage 生成 recovery.img
makeuserdataimage 生成 userdata.img
makecacheimage 生成 cache.img

 

在 Build 系统中添加新的内容

添加新的产品

当我们要开发一款新的 Android 产品的时候,我们首先就需要在 Build 系统中添加对于该产品的定义。

在 Android Build 系统中对产品定义的文件通常位于 device 目录下(另外还有一个可以定义产品的目录是 vender 目录,这是个历史遗留目录,Google 已经建议不要在该目录中进行定义,而应当选择 device 目录)。device 目录下根据公司名以及产品名分为二级目录,这一点我们在概述中已经提到过。

通常,对于一个产品的定义通常至少会包括四个文件:AndroidProducts.mk,产品版本定义文件,BoardConfig.mk 以及 verndorsetup.sh。下面我们来详细说明这几个文件。

  • AndroidProducts.mk:该文文件中的内容很简单,其中只需要定义一个变量,名称为“PRODUCT_MAKEFILES”,该变量的值为产品版本定义文件名的列表,例如:
 PRODUCT_MAKEFILES := \ 
 $(LOCAL_DIR)/full_stingray.mk \ 
 $(LOCAL_DIR)/stingray_emu.mk \ 
 $(LOCAL_DIR)/generic_stingray.mk
  • 产品版本定义文件:顾名思义,该文件中包含了对于特定产品版本的定义。该文件可能不只一个,因为同一个产品可能会有多种版本(例如,面向中国地区一个版本,面向美国地区一个版本)。该文件中可以定义的变量以及含义说明如表 6 所示:
表 6. 产品版本定义文件中的变量及其说明
常量说明
PRODUCT_NAME最终用户将看到的完整产品名,会出现在“关于手机”信息中。
PRODUCT_MODEL产品的型号,这也是最终用户将看到的。
PRODUCT_LOCALES该产品支持的地区,以空格分格,例如:en_GB de_DE es_ES fr_CA。
PRODUCT_PACKAGES该产品版本中包含的 APK 应用程序,以空格分格,例如:Calendar Contacts。
PRODUCT_DEVICE该产品的工业设计的名称。
PRODUCT_MANUFACTURER制造商的名称。
PRODUCT_BRAND该产品专门定义的商标(如果有的话)。
PRODUCT_PROPERTY_OVERRIDES对于商品属性的定义。
PRODUCT_COPY_FILES编译该产品时需要拷贝的文件,以“源路径 : 目标路径”的形式。
PRODUCT_OTA_PUBLIC_KEYS对于该产品的 OTA 公开 key 的列表。
PRODUCT_POLICY产品使用的策略。
PRODUCT_PACKAGE_OVERLAYS指出是否要使用默认的资源或添加产品特定定义来覆盖。
PRODUCT_CONTRIBUTORS_FILEHTML 文件,其中包含项目的贡献者。
PRODUCT_TAGS该产品的标签,以空格分格。

 

通常情况下,我们并不需要定义所有这些变量。Build 系统的已经预先定义好了一些组合,它们都位于 /build/target/product 下,每个文件定义了一个组合,我们只要继承这些预置的定义,然后再覆盖自己想要的变量定义即可。例如:

 # 继承 full_base.mk 文件中的定义
 $(call inherit-product, $(SRC_TARGET_DIR)/product/full_base.mk) 
 # 覆盖其中已经定义的一些变量
 PRODUCT_NAME := full_lt26 
 PRODUCT_DEVICE := lt26 
 PRODUCT_BRAND := Android 
 PRODUCT_MODEL := Full Android on LT26
  • BoardConfig.mk:该文件用来配置硬件主板,它其中定义的都是设备底层的硬件特性。例如:该设备的主板相关信息,Wifi 相关信息,还有 bootloader,内核,radioimage 等信息。对于该文件的示例,请参看 Android 源码树已经有的文件。
  • vendorsetup.sh:该文件中作用是通过 add_lunch_combo 函数在 lunch 函数中添加一个菜单选项。该函数的参数是产品名称加上编译类型,中间以“-”连接,例如:add_lunch_combo full_lt26-userdebug。/build/envsetup.sh 会扫描所有 device 和 vender 二 级目 录下的名称 为"vendorsetup.sh"文件,并根据其中的内容来确定 lunch 函数的 菜单选项。

在配置了以上的文件之后,便可以编译出我们新添加的设备的系统镜像了。

首先,调用“source build/envsetup.sh”该命令的输出中会看到 Build 系统已经引入了刚刚添加的 vendorsetup.sh 文件。

然后再调用“lunch”函数,该函数输出的列表中将包含新添加的 vendorsetup.sh 中添加的条目。然后通过编号或名称选择即可。

最后,调用“make -j8”来执行编译即可。

添加新的模块

关于“模块”的说明在上文中已经提到过,这里不再赘述。

在 源码树中,一个模块的所有文件通常都位于同一个文件夹中。为了将当前模块添加到整个 Build 系统中,每个模块都需要一个专门的 Make 文件,该文件的名称为“Android.mk”。Build 系统会扫描名称为“Android.mk”的文件,并根据该文件中内容编译出相应的产物。

需 要注意的是:在 Android Build 系统中,编译是以模块(而不是文件)作为单位的,每个模块都有一个唯一的名称,一个模块的依赖对象只能是另外一个模块,而不能是其他类型的对象。对于已经 编译好的二进制库,如果要用来被当作是依赖对象,那么应当将这些已经编译好的库作为单独的模块。对于这些已经编译好的库使用 BUILD_PREBUILT 或 BUILD_MULTI_PREBUILT。例如:当编译某个 Java 库需要依赖一些 Jar 包时,并不能直接指定 Jar 包的路径作为依赖,而必须首先将这些 Jar 包定义为一个模块,然后在编译 Java 库的时候通过模块的名称来依赖这些 Jar 包。

下面,我们就来讲解 Android.mk 文件的编写:

Android.mk 文件通常以以下两行代码作为开头:

 LOCAL_PATH := $(call my-dir) 
 include $(CLEAR_VARS)

这两行代码的作用是:

  1. 设置当前模块的编译路径为当前文件夹路径。
  2. 清理(可能由其他模块设置过的)编译环境中用到的变量。

为了方便模块的编译,Build 系统设置了很多的编译环境变量。要编译一个模块,只要在编译之前根据需要设置这些变量然后执行编译即可。它们包括:

  • LOCAL_SRC_FILES:当前模块包含的所有源代码文件。
  • LOCAL_MODULE:当前模块的名称,这个名称应当是唯一的,模块间的依赖关系就是通过这个名称来引用的。
  • LOCAL_C_INCLUDES:C 或 C++ 语言需要的头文件的路径。
  • LOCAL_STATIC_LIBRARIES:当前模块在静态链接时需要的库的名称。
  • LOCAL_SHARED_LIBRARIES:当前模块在运行时依赖的动态库的名称。
  • LOCAL_CFLAGS:提供给 C/C++ 编译器的额外编译参数。
  • LOCAL_JAVA_LIBRARIES:当前模块依赖的 Java 共享库。
  • LOCAL_STATIC_JAVA_LIBRARIES:当前模块依赖的 Java 静态库。
  • LOCAL_PACKAGE_NAME:当前 APK 应用的名称。
  • LOCAL_CERTIFICATE:签署当前应用的证书名称。
  • LOCAL_MODULE_TAGS:当前模块所包含的标签,一个模块可以包含多个标签。标签的值可能是 debug, eng, user,development 或者 optional。其中,optional 是默认标签。标签是提供给编译类型使用的。不同的编译类型会安装包含不同标签的模块,关于编译类型的说明如表 7 所示:
表 7. 编译类型的说明
名称说明
eng 默认类型,该编译类型适用于开发阶段。
当选择这种类型时,编译结果将:
  • 安装包含 eng, debug, user,development 标签的模块
  • 安装所有没有标签的非 APK 模块
  • 安装所有产品定义文件中指定的 APK 模块
user 该编译类型适合用于最终发布阶段。
当选择这种类型时,编译结果将:
  • 安装所有带有 user 标签的模块
  • 安装所有没有标签的非 APK 模块
  • 安装所有产品定义文件中指定的 APK 模块,APK 模块的标签将被忽略
userdebug 该编译类型适合用于 debug 阶段。
该类型和 user 一样,除了:
  • 会安装包含 debug 标签的模块
  • 编译出的系统具有 root 访问权限

 

表 3 中的文件已经定义好了各种类型模块的编译方式。所以要执行编译,只需要引入表 3 中对应的 Make 文件即可(通过常量的方式)。例如,要编译一个 APK 文件,只需要在 Android.mk 文件中,加入“include $(BUILD_PACKAGE)

除此以外,Build 系统中还定义了一些便捷的函数以便在 Android.mk 中使用,包括:

  • $(call my-dir):获取当前文件夹路径。
  • $(call all-java-files-under, <src>):获取指定目录下的所有 Java 文件。
  • $(call all-c-files-under, <src>):获取指定目录下的所有 C 语言文件。
  • $(call all-Iaidl-files-under, <src>) :获取指定目录下的所有 AIDL 文件。
  • $(call all-makefiles-under, <folder>):获取指定目录下的所有 Make 文件。
  • $(call intermediates-dir-for, <class>, <app_name>, <host or target>, <common?> ):获取 Build 输出的目标文件夹路径。

清单 2 和清单 3 分别是编译 APK 文件和编译 Java 静态库的 Make 文件示例:

清单 2. 编译一个 APK 文件
  LOCAL_PATH := $(call my-dir) 
  include $(CLEAR_VARS) 
  # 获取所有子目录中的 Java 文件
  LOCAL_SRC_FILES := $(call all-subdir-java-files) 			
  # 当前模块依赖的静态 Java 库,如果有多个以空格分隔
  LOCAL_STATIC_JAVA_LIBRARIES := static-library 
  # 当前模块的名称
  LOCAL_PACKAGE_NAME := LocalPackage 
  # 编译 APK 文件
  include $(BUILD_PACKAGE)
清单 3. 编译一个 Java 的静态库
  LOCAL_PATH := $(call my-dir) 
  include $(CLEAR_VARS) 
   
  # 获取所有子目录中的 Java 文件
  LOCAL_SRC_FILES := $(call all-subdir-java-files) 
   
  # 当前模块依赖的动态 Java 库名称
  LOCAL_JAVA_LIBRARIES := android.test.runner 
   
  # 当前模块的名称
  LOCAL_MODULE := sample 
   
  # 将当前模块编译成一个静态的 Java 库
  include $(BUILD_STATIC_JAVA_LIBRARY)

结束语

整个 Build 系统包含了非常多的内容,由于篇幅所限,本文只能介绍其中最主要内容。

由于 Build 系统本身也是在随着 Android 平台不断的开发过程中,所以不同的版本其中的内容和定义可能会发生变化。网络上关于该部分的资料很零碎,并且很多资料中的一些内容已经过时不再适用,再加上缺少官方文档,所以该部分的学习存在一定的难度。

这就要求我们要有很强的代码阅读能力,毕竟代码是不会说谎的。 要知道,对于我们这些开发人员来说,源代码就是我们最忠实的朋友。 Use the Source,Luke!

 

http://blog.sina.com.cn/s/blog_62ef2f1401013icb.html

Android的优势就在于其开源,手机和平板生产商可以根据自己的硬件进行个性定制自己的手机产品,如小米,LePhone,M9等,因此,在我们在对Android的源码进行定制的时候,很有必要了解下,Android的编译过程。

如果你从来没有做过Android代码的编译,那么最官方的编译过程就是查看Android的官方网站:http://source.android.com/source/building.html

但是,这儿只是告诉你了如何去编译一个通用的系统,并没有详细告诉你细节,我们跟着编译过程来了解下。

+-------------------------------------------------------------------------------------------------+

 

本文使用Android版本为2.1,采用开发板为华清远见研发的FS_S5PC100 A8开发板。

+-------------------------------------------------------------------------------------------------+

按照google给出的编译步骤如下:

   1> source build/envsetup.sh:加载命令

   2> lunch:选择平台编译选项

   3> make:执行编译

我们按照编译步骤来分析编译过程的细节,最终添加自己的平台编译选项。

1. source build/envsetup.sh

这个命令是用来将envsetup.sh里的所有用到的命令加载到环境变量里去,我们来分析下它。

envsetup.sh里的主要命令如下:

 

function help()                  # 显示帮助信息
function get_abs_build_var()           # 获取绝对变量
function get_build_var()             # 获取绝对变量
function check_product()             # 检查product
function check_variant()             # 检查变量
function setpaths()                # 设置文件路径
function printconfig()              # 打印配置
function set_stuff_for_environment()        # 设置环境变量
function set_sequence_number()            # 设置序号
function settitle()                # 设置标题
function choosetype()               # 设置type
function chooseproduct()              # 设置product
function choosevariant()              # 设置variant
function tapas()                  # 功能同choosecombo
function choosecombo()               # 设置编译参数
function add_lunch_combo()             # 添加lunch项目
function print_lunch_menu()            # 打印lunch列表
function lunch()                 # 配置lunch
function m()                   # make from top
function findmakefile()              # 查找makefile
function mm()                   # make from current directory
function mmm()                   # make the supplied directories
function croot()                 # 回到根目录
function cproj()
function pid()
function systemstack()
function gdbclient()
function jgrep()                 # 查找java文件
function cgrep()                  # 查找c/cpp文件
function resgrep()
function tracedmdump()
function runhat()
function getbugreports()
function startviewserver()
function stopviewserver()
function isviewserverstarted()
function smoketest()
function runtest()
function godir ()                 # 跳到指定目录 405

 # add_lunch_combo函数被多次调用,就是它来添加Android编译选项
 # Clear this variable.  It will be built up again when the vendorsetup.sh
 406 # files are included at the end of this file.
 # 清空LUNCH_MENU_CHOICES变量,用来存在编译选项
 407 unset LUNCH_MENU_CHOICES
 408 function add_lunch_combo()  
 409 {
 410     local new_combo=$1         # 获得add_lunch_combo被调用时的参数
 411     local c
     # 依次遍历LUNCH_MENU_CHOICES里的值,其实该函数第一次调用时,该值为空
 412     for c in ${LUNCH_MENU_CHOICES[@]} ; do
 413         if [ "$new_combo" = "$c" ] ; then    # 如果参数里的值已经存在于LUNCH_MENU_CHOICES变量里,则返回
 414             return
 415         fi
 416     done
     # 如果参数的值不存在,则添加到LUNCH_MENU_CHOICES变量里
 417     LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)
 418 }


# 这是系统自动增加了一个默认的编译项 generic-eng
 420 # add the default one here
 421 add_lunch_combo generic-eng    # 调用上面的add_lunch_combo函数,将generic-eng作为参数传递过去
 422
 423 # if we're on linux, add the simulator.  There is a special case
 424 # in lunch to deal with the simulator
 425 if [ "$(uname)" = "Linux" ] ; then
 426     add_lunch_combo simulator
 427 fi

# 下面的代码很重要,它要从vendor目录下查找vendorsetup.sh文件,如果查到了,就加载它
1037 # Execute the contents of any vendorsetup.sh files we can find.
1038 for f in `/bin/ls vendorbuild/vendorsetup.sh 2> /dev/null`
1039 do
1040     echo "including $f"
1041    . $f       # 执行找到的脚本,其实里面就是厂商自己定义的编译选项
1042 done
1043 unset f

envsetup.sh其主要作用如下:

  1. 加载了编译时使用到的函数命令,如:help,lunch,m,mm,mmm等
  2. 添加了两个编译选项:generic-eng和simulator,这两个选项是系统默认选项
  3. 查找vendor/<-厂商目录>/和vendor/<厂商目录>/build/目录下的vendorsetup.sh,如果存在的话,加载执行它,添加厂商自己定义产品的编译选项
 其实,上述第3条是向编译系统添加了厂商自己定义产品的编译选项,里面的代码就是:add_lunch_combo xxx-xxx。

根据上面的内容,可以推测出,如果要想定义自己的产品编译项,简单的办法是直接在envsetup.sh最后,添加上add_lunch_combo myProduct-eng,当然这么做,不太符合上面代码最后的本意,我们还是老实的在vendor目录下创建自己公司名字,然后在公司目录下创建一个新的vendorsetup.sh,在里面添加上自己的产品编译项

 
?
#mkdir vendor/farsight/
#touch vendor/farsight/vendorsetup.sh
#echo "add_lunch_combo fs100-eng" > vendor/farsight/vendorsetup.sh

 

这样,当我们在执行source build/envsetup.sh命令的时候,可以在shell上看到下面的信息:

 
?
including vendor/farsight/vendorsetup.sh

 
2. 按照android官网的步骤,开始执行lunch full-eng

 

当然如果你按上述命令执行,它编译的还是通用的eng版本系统,不是我们个性系统,我们可以执行lunch命令,它会打印出一个选择菜单,列出可用的编译选项

如果你按照第一步中添加了vendorsetup.sh那么,你的选项中会出现:

You're building on Linux
 
generic-eng simulator fs100-eng
Lunch menu... pick a combo:
     1. generic-eng
     2. simulator
     3. fs100-eng

 

其中第3项是我们自己添加的编译项。

 

lunch命令是envsetup.sh里定义的一个命令,用来让用户选择编译项,来定义Product和编译过程中用到的全局变量。

我们一直没有说明前面的fs100-eng是什么意思,现在来说明下,fs100是我定义的产品的名字,eng是产品的编译类型,除了eng外,还有user, userdebug,分别表示:

eng: 工程机,

user:最终用户机

userdebug:调试测试机

tests:测试机

由此可见,除了eng和user外,另外两个一般不能交给最终用户的,记得m8出来的时候,先放出了一部分eng工程机,然后出来了user机之后,可以用工程机换。

 

那么这四个类型是干什么用的呢?其实,在main.mk里有说明,在Android的源码里,每一个目标(也可以看成工程)目录都有一个 Android.mk的makefile,每个目标的Android.mk中有一个类型声明:LOCAL_MODULE_TAGS,这个TAGS就是用来 指定,当前的目标编译完了属于哪个分类里。

 

    PS:Android.mk和Linux里的makefile不太一样,它是Android编译系统自己定义的一个makefile来方便编译成:c,c++的动态、静态库或可执行程序,或java库或android的程序,

 

好了,我们来分析下lunch命令干了什么?

 

function lunch()
{
    local answer

    if [ "$1" ] ; then
       # lunch后面直接带参数
        answer=$1
    else
       # lunch后面不带参数,则打印处所有的target product和variant菜单提供用户选择
        print_lunch_menu  
        echo -n "Which would you like? [generic-eng] "
        read answer
    fi

    local selection=

    if [ -z "$answer" ]
    then
           # 如果用户在菜单中没有选择,直接回车,则为系统缺省的generic-eng
        selection=generic-eng
    elif [ "$answer" = "simulator" ]
    then
        # 如果是模拟器
        selection=simulator
    elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
    then
        # 如果answer是选择菜单的数字,则获取该数字对应的字符串
        if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]
        then
            selection=${LUNCH_MENU_CHOICES[$(($answer-$_arrayoffset))]}
        fi
        # 如果 answer字符串匹配 *-*模式(*的开头不能为-)
    elif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")
    then
        selection=$answer
    fi

    if [ -z "$selection" ]
    then
        echo
        echo "Invalid lunch combo: $answer"
        return 1
    fi

    # special case the simulator
    if [ "$selection" = "simulator" ]
    then
        # 模拟器模式
        export TARGET_PRODUCT=sim
        export TARGET_BUILD_VARIANT=eng
        export TARGET_SIMULATOR=true
        export TARGET_BUILD_TYPE=debug
    else

        # 将 product-variant模式中的product分离出来
        local product=$(echo -n $selection | sed -e "s/-.*$//")

        # 检查之,调用关系 check_product()->get_build_var()->build/core/config.mk比较罗嗦,不展开了
        check_product $product
        if [ $? -ne 0 ]
        then
            echo
            echo "** Don't have a product spec for: '$product'"
            echo "** Do you have the right repo manifest?"
            product=
        fi

        # 将 product-variant模式中的variant分离出来
        local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")

        # 检查之,看看是否在 (user userdebug eng) 范围内
        check_variant $variant
        if [ $? -ne 0 ]
        then
            echo
            echo "** Invalid variant: '$variant'"
            echo "** Must be one of ${VARIANT_CHOICES[@]}"
            variant=
        fi

        if [ -z "$product" -o -z "$variant" ]
        then
            echo
            return 1
        fi
 #  导出环境变量,这里很重要,因为后面的编译系统都是依赖于这里定义的几个变量的
        export TARGET_PRODUCT=$product
        export TARGET_BUILD_VARIANT=$variant
        export TARGET_SIMULATOR=false
        export TARGET_BUILD_TYPE=release
    fi # !simulator

    echo

    # 设置到环境变量,比较多,不再一一列出,最简单的方法 set >env.txt 可获得
    set_stuff_for_environment
    # 打印一些主要的变量, 调用关系 printconfig()->get_build_var()->build/core/config.mk->build/core/envsetup.mk 比较罗嗦,不展开了
    printconfig
}

 

由上面分析可知,lunch命令可以带参数和不带参数,最终导出一些重要的环境变量,从而影响编译系统的编译结果。导出的变量如下(以实际运行情况为例)

TARGET_PRODUCT=fs100
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=false
TARGET_BUILD_TYPE=release

执行完上述两个步骤,就该执行:make命令了,下篇来分析。
 
 

Android Build System

Specifying what to build

In order to decide what to build, and how to build it, the build system requires that some variables be set. Different products, with different packages and options can be built from the same source tree. The variables to control this can be set via a file with declarations of 'make' variables, or can be specified in the environment.

A device vendor can create definition files that describe what is to be included on a particular board or for a particular product. The definition file is called: buildspec.mk, and it is located in the top-level source directory. You can edit this manually to hardcode your selections.

If you have a buildspec.mk file, it sets all the make variables needed for a build, and you don't have to mess with options.

Another method of specifying options is to set environment variables. The build system has a rather ornate method of managing these options for you.

To set up your build environment, you need to load the variables and functions in build/envsetup.sh. Do this by 'source-ing' the file into your shell environment, like this:

$ . build/envsetup.sh

You can type 'help' (or 'hmm') at this point to see some utility functions that are available to make it easier to work with the source.

To select the set of things you want to build, and what items to build for, you use either the 'choosecombo' function or the 'lunch' function. 'choosecombo' will walk you through the different items you have to select, one-by-one, while 'lunch' allows you select some pre-set combinations.

The items that have to be defined for a build are:

  • the product ('generic' or some specific board or platform name)
  • the build variant ('user', 'userdebug', or 'eng')
  • whether you're running on a simulator ('true' or 'false')
  • the build type ('release' or 'debug')

Descriptions of these different build variants are at http://source.android.com/porting/build_system.html#androidBuildVariants

The build process from a user perspective is described pretty well in this blog post: First Android platform build by CodePainters, December 2009

Actually building the system

Once you have things set up, you actually build the system with the 'make' command.

To build the whole thing, run 'make' in the top directory. The build will take a long time, if you are building everything (for example, the first time you do it).

Seeing the actual commands used to build the software

Use the "showcommands" target on your 'make' line:

$ make -j4 showcommands

This can be used in conjunction with another make target, to see the commands for that build. That is, 'showcommands' is not a target itself, but just a modifier for the specified build.

In the example above, the -j4 is unrelated to the showcommands option, and is used to execute 4 make sessions that run in parallel.

Make targets

Here is a list of different make targets you can use to build different parts of the system:

  • make sdk - build the tools that are part of an SDK (adb, fastboot, etc.)
  • make snod - build the system image from the current software binaries
  • make services
  • make runtime
  • make droid - make droid is the normal build.
  • make all - make everything, whether it is included in the product definition or not
  • make clean - remove all built files (prepare for a new build). Same as rm -rf out/<configuration>/
  • make modules - shows a list of submodules that can be built (List of all LOCAL_MODULE definitions)
  • make <local_module> - make a specific module (note that this is not the same as directory name. It is the LOCAL_MODULE definition in the Android.mk file)
  • make clean-<local_module> - clean a specific module
  • make bootimage TARGET_PREBUILT_KERNEL=/path/to/bzImage - create a new boot image with custom bzImage

Helper macros and functions

There are some helper macros and functions that are installed when you source envsetup.sh. They are documented at the top of envesetup.sh, but here is information about a few of them:

  • croot - change directory to the top of the tree
  • m - execute 'make' from the top of the tree (even if your current directory is somewhere else)
  • mm - builds all of the modules in the current directory
  • mmm <dir1> ... - build all of the modules in the supplied directories
  • cgrep <pattern> - grep on all local C/C++ files
  • jgrep <pattern> - grep on all local Java files
  • resgrep <pattern> - grep on all local res/*.xml files
  • godir <filename> - go to the directory containing a file

Speeding up the build

You can use the '-j' option with make, to start multiple threads of make execution concurrently.

In my experience, you should specify about 2 more threads than you have processors on your machine. If you have 2 processors, use 'make -j4', If they are hyperthreaded (meaning you have 4 virtual processors), try 'make -j6.

You can also specify to use the 'ccache' compiler cache, which will speed up things once you have built things a first time. To do this, specify 'export USE_CCACHE=1' at your shell command line. (Note that ccache is included in the prebuilt section of the repository, and does not have to be installed on your host separately.)

Building only an individual program or module

If you use build/envsetup.sh, you can use some of the defined functions to build only a part of the tree. Use the 'mm' or 'mmm' commands to do this.

The 'mm' command makes stuff in the current directory (and sub-directories, I believe). With the 'mmm' command, you specify a directory or list of directories, and it builds those.

To install your changes, do 'make snod' from the top of tree. 'make snod' builds a new system image from current binaries.

Setting module-specific build parameters

Some code in Android system can be customized in the way they are built (separate from the build variant and release vs. debug options). You can set variables that control individual build options, either by setting them in the environment or by passing them directly to 'make' (or the 'm...' functions which call 'make'.)

For example, the 'init' program can be built with support for bootchart logging by setting the INIT_BOOTCHART variable. (See Using Bootchart on Android for why you might want to do this.)

You can accomplish either with:

$ touch system/init/init.c
$ export INIT_BOOTCHART=true
$ make

or

$ touch system/init/init.c
$ m INIT_BOOTCHART=true

These are some tips for things you can use in your own Android.mk files.

build helper functions

A whole bunch of build helper functions are defined in the file build/core/definitions.mk

Try grep define build/core/definitions.mk for an exhaustive list.

Here are some possibly interesting functions:

  • print-vars - shall all Makefile variables, for debugging
  • emit-line - output a line during building, to a file
  • dump-words-to-file - output a list of words to a file
  • copy-one-file - copy a file from one place to another (dest on target?)

 

Add a file directly to the output area

You can copy a file directly to the output area, without building anything, using the add-prebuilt-files function.

The following line, extracted from prebuilt/android-arm/gdbserver/Android.mk copies a list of files to the EXECUTABLES directory in the output area:

$(call add-prebuilt-files, EXECUTABLES, $(prebuilt_files))

Adding a new program to build

Steps for adding a new program to the Android source tree

  • make a directory under 'external'
    • e.g. ANDROID/external/myprogram
  • create your C/cpp files.
  • create Android.mk as clone of external/ping/Android.mk
  • Change the names ping.c and ping to match your C/cpp files and program name
  • add the directory name in ANDROID/build/core/main.mk after external/zlib as external/myprogram
  • make from the root of the source tree
  • your files will show up in the build output area, and in system images.
    • You can copy your file from the build output area, under out/target/product/..., if you want to copy it individually to the target (not do a whole install)

See http://www.aton.com/android-native-development-using-the-android-open-source-project/ for a lot more detail.

Building the kernel

The kernel is "outside" of the normal Android build system (indeed, the kernel is not included by default in the Android Open Source Project). However, there are tools in AOSP for building a kernel. If you are building the kernel, start on this page: http://source.android.com/source/building-kernels.html

If you are building the kernel for the emulator, you may also want to look at: http://stackoverflow.com/questions/1809774/android-kernel-compile-and-test-with-android-emulator

And, Ron M wrote (on the android-kernel mailing list, on May 21, 2012):

This post is very old - but nothing has changes as far as AOSP is concerned, so in case anyone is interested and runs into this problem when building for QEMU:

There is actually a nice and shorter way to build the kernel for your QEMU target provided by the AOSP:

1. cd to your kernel source dir (Only goldfish 2.6.29 works out of the box for the emulator)

2. ${ANDROID_BUILD_TOP}/external/qemu/distrib/build-kernel.sh -j=64 --arch=x86 --out=$YourOutDir

3. emulator -kernel ${YourOutDir}/kernel-qemu # run emulator:


Step #2 calls the toolbox.sh wrapper scripts which works around the SSE disabling gcc warning - which happens for GCC < 4.5 (as in the AOSP prebuilt X86 toolchain).

This script adds the " -mfpmath=387 -fno-pic" in case it is an X86 and that in turn eliminates the compilation errors seen above.

To have finer control over the build process, you can use the "toolbox.sh" wrapper and set some other stuff without modifying the script files.

An example for building the same emulator is below:

# Set arch
export ARCH=x86
# Have make refer to the QEMU wrapper script for building android over x86 
(eliminates the errors listed above)
export 
CROSS_COMPILE=${ANDROID_BUILD_TOP}/external/qemu/distrib/kernel-toolchain/android-kernel-toolchain-
# Put your cross compiler here. I am using the AOSP prebuilt one in this example
export 
REAL_CROSS_COMPILE=${ANDROID_BUILD_TOP}/prebuilt/linux-x86/toolchain/i686-android-linux-4.4.3/bin/i686-android-linux-
# Configure your kernel - here I am taking the default goldfish_defconfig
make goldfish_defconfig
# build
make -j64
# Run emulator:
emulator -kernel arch/x86/boot/bzImage -show-kernel


This works for the 2.6.29 goldfish branch. If anyone is using the emulator with a 3+ kernel I would be like to hear about it.

 


转载于:https://www.cnblogs.com/welhzh/p/4532142.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值