在之前的公司参与项目开发的时候,虽然不负责系统固件编译脚本的维护,但为了工作的过程中更加的顺利,便学习了一下Android的Build系统。本文就是对之前的学习做个总结。
- 系统版本:AOSP 5.1.1
1. Android Build 系统核心
Android Build系统的核心位于源码目录的build/core,该目录下有几十个mk文件以及若干个shell脚本。
通常是使用下面的命令来编译Android系统:
$ . build/envsetup.sh
$ lunch
$ make
- envsetup.sh 文件的作用
该脚本会建立Android的编译环境,该脚本中,定义了很多的shell命令,比如m、mm、mmm和lunch等命令就是在该脚本中定义的,如图:
除了命令的定义外,来看看运行envsetup.sh脚本实际执行的代码:
可以看到,envsetup.sh有两段代码会执行:
- 中间的6条add_lunch_combo命令的调用
- 结尾处的在device和vendor目录下搜索vendorsetup.sh文件
这里以mako产品为例(对应Nexus 4手机),看一下device/lge/mako目录下的vendorsetup.sh的内容:
可以看到它仍然是调用了add_lunch_combo命令。这样看来,build/core/envsetup.sh 除了定义shell命令外,就是执行add_lunch_combo命令而已。它的定义也是在envsetup.sh中,如下:
add_lunch_combo命令的功能就是将调用该命令的参数存放到一个全局数组变量LUNCH_MENU_CHOICES中。
执行lunch命令时的打印出来的菜单项正是这个数组的内容。
envsetup.sh脚本定义了一些挺有用的命令,如下表:
命令 | 说明 |
---|---|
lunch | 用法:lunch <product_name>-<build_variant>,指定当前编译的产品 |
croot | 快速切换到源码的根目录,方便开始编译 |
m | 编译整个源码,但是不用将当前目录切换到源码的根目录 |
mm | 编译当前目录下的所有模块,但不编译它们的依赖模块 |
mmm | 编译指定目录下的所有模块,但不编译它们的依赖模块 |
mma | 编译当前目录下的所有模块,同时编译它们的依赖模块 |
mmma | 编译指定目录下的所有模块,同时编译它们的依赖模块 |
cgrep | 对系统所有的C/C++文件执行grep命令 |
ggrep | 对系统中所有本地的gradle文件执行grep命令 |
jgrep | 对系统所有的Java文件执行grep命令 |
resgrep | 对系统中所有的res目录下的XML文件执行grep命令 |
sgrep | 对系统中所有源文件执行grep命令 |
godir | 根据godir后的参数文件名在整个源码目录中查找,然后切换到目录 |
- lunch命令
lunch命令如果没有参数,系统会打印出产品列表供选择。lunch命令也可以有参数,格式为:lunch <product_name>-<build_variant>
参数前半部分的 "product_name"必须是系统中已经定义的产品名称,后半部分的 "build_variant" 必须是 "eng"、"userdebug" 和 "user" 三者之一。lunch命令的主要功能是根据用户输入或选择的产品名来设置与之相关的环境变量。这些环境变量与产品编译相关的,主要有下面三项:
- TARGET_PRODUCT:对应 “product_name”;
- TARGET_BUILD_VARIANT:对应 "build_variant";
- TARGET_BUILD_TYPE:一般是release。
- 与编译相关的环境变量
执行lunch命令后,系统会打印出当前配置所生成的环境变量。比如选择 full_mako-eng后,打印如下信息:
如上图,这些环境变量将影响编译过程,其中:
- PLATFORM_VERSION_CODENAME:表示平台版本的名称
- PLATFORM_VERSION:Android平台的版本号
- TARGET_PRODUCT:所编译的产品名称
- TARGET_BUILD_VARIANT:表示编译的产品的类型。可能的值有:eng、user和userdebug
- TARGET_BUILD_TYPE:表示编译的类型,可选的值是:release和debug。当选择debug版时,编译系统会加入调试信息,方便追踪底层的bug。
- TARGET_BUILD_APPS:编译Android系统时,该变量的值为NULL。使用Build系统编译单个模块时,这个变量的值是所编译模块的路径。
- TARGET_ARCH:表示编译目标的CPU架构
- TARGET_ARCH_VARIANT:表示编译目标的CPU架构版本
- TARGET_CPU_VARIANT:表示编译目标的CPU代号
- TARGET_2ND_ARCH:表示编译目标的第二CPU架构
- TARGET_2ND_ARCH_VARIANT:表示编译目标的第二CPU架构版本
- TARGET_2ND_CPU_VARIANT:表示编译目标的第二CPU的代号
- HOST_ARCH:表示编译平台的CPU架构
- HOST_OS:表示编译平台使用的操作系统
- HOST_OS_EXTRA:编译平台操作系统的一些其他信息,如内核版本号,产品名称和系统代号。
- BUILD_ID:BUILD_ID的值会出现在编译的版本信息中,可以利用这个环境变量来定义公司特有的标识
- OUT_DIR:指定编译结果的输出目录
这些变量中,TARGET_2ND_ARCH、TARGET_2ND_ARCH_VARIANT和TARGET_2ND_CPU_VARIANT是Android 5.0新增加的。当系统运行在64位环境时,考虑到还要支持32位的应用,因此这里定义了两套CPU架构.
- Build相关脚本的层次包含关系
下图显示了Build系统中编译脚本的包含关系,图中不带路径的文件都位于build/core下,同时 combo 目录和 clang目录也都位于 build/core目录下,而device目录位于系统源码根目录下。
Build系统会在其中的 $(TOP)/*/Android.mk、device/*/AndroidProducts.mk 和 device/*/BroadConfig.mk 三处地方引入具体的产品配置文件AndroidProduct.mk和BroadConfig.mk,以及各个模块的编译文件Android.mk。
combo目录下的这些mk文件定义了gcc编译器的版本和参数
clang目录下的mk文件定义了LLVM的编译器clang的版本和参数
下表罗列了Build系统各编译脚本的简介:
文件名 | 说明 |
---|---|
main.mk | Android Build系统的主控文件。该文件主要作用是包含进其他mk文件,以及定义几个最重要的编译目标,如droid、sdk等。同事检查编译工具的版本,如make、gcc、javac等 |
help.mk | Android Build系统的帮助。文件中定义了一个名为help的编译目标,因此,输入"make help"会打印出Build系统的使用说明 |
config.mk | Android Build系统的配置文件,主要定义了许多常量来负责不同类型模块的编译,定义编译器参数并引入产品的BoardConfig.mk文件来配置产品参数,同时也定义了一些编译工具的路径,如aapt、mkbootimg等 |
pathmap.mk | 给一些头文件所在的目录定义别名,将framework下的一些源码目录按类别组合在一起并定义了别名,方便引用 |
buildspec.mk | 放在build目录下的buildspec.mk文件可以定义产品编译的参数,但一般很少用它 |
envsetup.mk | 包含进product_config.mk文件并根据其内容设置编译产品所需要的环境变量,如TARGET_PRODUCT、TARGET_BUILD_VARIANT等,并检查这些变量值的合法性,同时还制定了各种编译结果的输出路径 |
version_defaults.mk | 定义系统版本相关的变量 |
build_id.mk | 定义了环境变量BUILD_ID |
product_config.mk | 包含进了系统中所有的AndroidProduct.mk文件,并根据当前产品的配置文件来设置产品编译相关的变量 |
product.mk | 定义product_config.mk文件中使用的各种函数 |
combo/select.mk | 根据环境变量的设置,指定对应的系统和架构中所使用的编译工具路径 |
clang/config.mk | 定义了LLVM编译器clang在不同架构下的路径和参数 |
dumpvar.mk | 打印输出本次编译的配置信息 |
cleanbuild.mk | 包含了源码中所有CleanSpec.mk,定义编译目标dataclean和installclean |
definitions.mk | 定义了大量Build系统中使用的函数。如果熟悉这些函数,编写产品配置文件将会更加得心应手 |
dex_preopt.mk | 定义与dex优化有关的路径和参数 |
pdk_config.mk | 编译pdk的配置文件 |
post_clean.mk | 比较当前系统的overlay目录与上一次build时是否发生变化,如果有变化,重新生成受影响模块的资源定义文件R.java |
legacy_prebuilts.mk | 定义系统prebuild模块列表 |
Makefile | 定义了系统最终编译完成所需要的各种目标和规则 |
Android Build系统中定义了大量的编译变量,通过改变这些编译变量的值就能控制整个编译过程和结果。产品的配置文件实际上是对这些编译变量赋值的脚本文件。学习Android Build系统很大程度上是在了解这些编译变量的含义和用法。
2. Android的产品配置文件
产品配置文件的作用就是按照Build系统的要求,将生成产品的各种image文件所需要的配置信息(如版本号、各种参数等)、资源(图片、字体、铃声等)、二进制文件(apk、jar包、so库等)有机地组织起来,同时进行裁剪,加入或去掉一些模块。
Android的产品配置文件位于系统源码根目录的device目录下,但产品配置文件也可以放在vendor目录下。这两个目录从Build系统的角度看没有太大的区别,Build系统中搜寻产品配置的关联文件时会同时在这两个目录进行,但实际使用时,往往会让这两个目录配合使用,通常产品配置文件放在device目录下,而vendor目录下则存放一些硬件的HAL库。比如我给Nexus 4手机编译固件之前,会从Google官方的相关站点下载适用于Nexus 4手机的不开源的那些HAL库、驱动,并将其放在系统源码根目录下并释放出放在vendor目录下。
- 与mako相关的配置文件
通常device目录下有以下几个子目录:
- common:用来存放各个产品通用的配置脚本、文件等
- sample:一个产品配置的例子,写一个新的产品配置时可以使用sample目录下的文件作为模板
- google:用途不详
- generic:存放的是用于模拟器的产品,包括x86、arm、mips架构
- asus、lge、samsung:分别代表宏碁、LG、三星3家公司。各家产品放在对应的目录下。
如果需要添加新的产品,可在device目录下新建一个目录。
mako手机(Nexus 4) 是由LG代工的,所以它的产品配置文件位于lge目录下,内容如下:
- mako:存放 Google Nexus4(mako) 的产品配置文件。
- mako-kernel:存放的是mako kernel的二进制文件
- hammerhead:存放Google Nexus5(hammerhead) 的产品配置文件。
- hammerhead:存放的是 hammerhead kernel的二进制文件。
下面简单说一下 device/lge/mako目录下与Build系统相关的几个产品配置的关键文件:
vendorsetup.sh
该文件在初始化编译环境时被envsetup.sh文件包含进去。它作用是调用add_lunch_combo命令来添加产品名称串。AndroidProduct.mk
AndroidProduct.mk会在Build系统的ProductConfig.mk文件中被包含进去,这个文件最重要的作用是定义了一个变量 PRODUCT_MAKEFILES,它定义了本配置目录中所有编译入口文件,但每种产品编译时只会使用其中之一。BoardConfig.mk
BoardConfig.mk文件会被Build系统的envsetup.sh文件包含进去。该文件主要定义了和设备硬件(包括CPU、WIFI、GPS等)相关的一些参数。device.mk
device.mk是产品配置中经常需要修改的一个文件。产品定义中需要包含进的模块、文件以及各种环境变量的定义一般都放在这个文件里。device.mk中一些重要的编译变量说明如下:
(1) PRODUCT_COPY_FILES:一个格式为"源文件路径:目标文件路径" 字符串的组合。使用PRODUCT_COPY_FILES变量能方便地将编译目录下的一个文件复制到目标文件系统中。需要注意的是,PRODUCT_COPY_FILES仅仅复制文件。如果复制的是apk或者Java库,这些文件的签名会保留。(2) PRODUCT_PACKAGES:用来定义产品的模块列表,所有在模块列表中的模块的定义都会被执行。
(3) PRODUCT_AAPT_CONFIG:指定了系统中能够被支持的屏幕密度类型(dip)。所谓支持,是指系统编译时,会将相应的资源文件添加到framework_res.apk文件中。
(4) PRODUCT_AAPT_PREF_CONFIG:指定系统实际的屏幕密度类型。
(5) DEVICE_PACKAGE_OVERLAYS:这是一个很重要的变量,它指定了系统的overlay目录。系统编译时会使用overlay目录下存放的资源文件替换系统或模块原有的资源文件。这样在不覆盖原生资源文件的情况下,就能实现产品的个性化。而且overlay的目录可以有多个,它们会按照在变量中的先后顺序来替换资源文件,利用这个特性可以定义公共的overlay目录,以及各个产品专属的overlay目录,最大限度地重用资源文件。
(6) PRODUCT_PROPERTY_OVERRIDES:定义系统的属性值。如果属性名称以 "ro." 开头,那这个属性就是只读属性。一旦设置,属性值将不能改变。如果属性名称以 "persist."开头,则当设置这个属性时,它的值将写入文件/data/property中。
- 编译类型 eng、user和userdebug
(1) eng
缺省的编译类型。执行 "make" 相当于执行 "make eng"
编译时会将下列模块安装到系统:
- 在Android.mk 中用 LOCAL_MODULE_TAGS 变量定义了标签:eng、debug、shell_$(TARGET_SHELL)、user和development 的模块
- 非APK模块并且不带任何标签的模块
所有产品配置文件中指定的APK模块编译的系统带有如下属性:
- ro.secure=0
- ro.debuggable=1
- ro.kernel.android.checkjni=1
编译的系统中缺省情况下adb是可用的
(2) user
编译时会将下列模块安装到系统:
- 在Android.mk 中用 LOCAL_MODULE_TAGS 变量定义了标签:shell_$(TARGET_SHELL)、user的模块
- 非APK模块并且不带任何标签的模块
所有产品配置文件中指定的APK模块,同时忽略其标签属性编译的系统带有如下属性:
- ro.secure=1
- ro.debuggable=0
编译的系统中缺省情况下adb是不可用的,需要在系统设置中手动开启
(3) userdebug
编译时会将下列模块安装到系统:
- 在Android.mk 中用 LOCAL_MODULE_TAGS 变量定义了标签:shell_$(TARGET_SHELL)、debug和user的模块
- 非APK模块并且不带任何标签的模块
所有产品配置文件中指定的APK模块,同时忽略其标签属性编译的系统带有如下属性:
- ro.secure=1
- ro.debuggable=1
编译的系统中缺省情况下adb是不可用的,需要在系统设置中手动开启
3. 编译Android的模块
Android中的各种模块,无论是APK应用、可执行程序还是jar包,都可以通过Build系统编译生成。在每个模块的源码目录下,都有一个Android.mk文件,里面包含了模块代码的位置、模块的名称、需要链接的动态库等一系列的定义。
这里以 package/apps/Settings 目录下的 Android.mk文件为例:
- 模块的编译变量
Android.mk 文件能编译出不同的模块,是通过包含某个模块编译文件实现的,如上面例子中的 include $(BULID_PACKAGE) 。Android的Build系统定义了很多模块编译变量,如下表:
模块编译变量 | 说明 |
---|---|
BUILD_HOST_STATIC_LIBRARY | 对应的文件是host_static_library.mk,用来产生编译平台使用的本地静态库 |
BUILD_HOST_SHARED_LIBRARY | 对应的文件是host_shared_library.mk,用来产生编译平台使用的本地共享库 |
BUILD_STATIC_LIBRARY | 对应的文件是 static_library.mk,用来产生目标系统使用的本地静态库 |
BUILD_RAW_STATIC_LIBRARY | 对应的文件是raw_static_library.mk,用途不明 |
BUILD_SHARED_LIBRARY | 对应的文件是shared_library.mk,用来产生目标系统使用的本地共享库 |
BUILD_EXECUTABLE | 对应的文件是executable.mk,用来产生目标系统使用的Linux可执行程序 |
BUILD_RAW_EXECUTABLE | 对应的文件是raw_executable.mk,用途不明 |
BUILD_HOST_EXECUTABLE | 对应的文件是host_executable.mk,用来产生编译平台下使用的可执行程序 |
BUILD_PACKAGE | 对应的文件是package.mk。用来产生apk文件 |
BUILD_PHONY_PACKAGE | 对应的文件是phony_package.mk |
BUILD_HOST_PREBUILT | 对应的文件是host_prebuilt.mk。用来定义编译平台下的预编译模块目标 |
BUILD_PREBUILT | 对应的文件是prebuilt.mk,定义预编译的模块目标,作用是将这些预编译的模块引入系统 |
BUILD_MULTI_PREBUILT | 对应的文件是multi_prebuilt.mk,定义多个预编译模块目标 |
BUILD_JAVA_LIBRARY | 对应的文件是java_library.mk,用来产生目标平台的Java共享库 |
BUILD_STATIC_JAVA_LIBRARY | 对应的文件是static_java_library.mk,用来产生目标平台的Java静态库 |
BUILD_HOST_JAVA_LIBRARY | 对应的文件是host_java_library.mk,用来产生编译平台下的Java共享库 |
BUILD_DROIDDOC | 对应的文件是droiddoc.mk |
BUILD_COPY_HEADERS | 对应的文件是copy_headers.mk,用来将LOCAL_COPY_HEADERS变量定义的文件复制到LOCAL_COPY_HEADERS_TO变量定义的路径中 |
BUILD_NATIVE_TEST | 对应的文件是native_test.mk,用来产生一个目标系统的可执行程序,相比较BUILD_EXECUTABLE只是多定义了测试相关的库的路径和头文件路径 |
BUILD_HOST_NATIVE_TEST | 对应的文件是host_native_test.mk,用来产生一个编译平台下的可执行程序,相比较BUILD_HOST_EXECUTABLE只是多定义了测试相关的库的路径和头文件路径 |
- 常用模块的定义示例
(1) 编译一个apk文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_JAVA_LIBRARIES := # 指定依赖的共享Java类库
LOCAL_STATIC_JAVA_LIBRARIES := # 指定依赖的静态Java类库
# 指定源码列表。这里使用系统定义的函数搜寻src目录下的文件形成列表
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_MODULE_TAGS := optional #指定模块的标签
LOCAL_CERTIFICATE := shared #指定模块的签名方式
LOCAL_PACKAGE_NAME := testapk # 指定模块的名称
include $(BUILD_PACKAGE)
(2) 编译一个Java共享库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_MODULE_TAGS := optional # 指定模块的标签
LOCAL_MODULE := javadynamiclib # 指定模块的名称
include $(BUILD_JAVA_LIBRARY)
(3) 编译一个Java静态库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_MODULE := javastaticlib # 指定模块的名称
include $(BUILD_STATIC_JAVA_LIBRARY)
(4) 编译一个Java资源包文件
资源包也是一个apk文件,但没有代码,类似 framework_res.apk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_NO_STANDARD_LIBRARIES := true # 指定依赖的静态Java类库
LOCAL_PACKAGE_NAME := javareslib # 定义模块名
LOCAL_CERTIFICATE := platform # 指定签名类型
LOCAL_AAPT_FLAGS := -x # 定义AAPT工具参数
LOCAL_MODULE_TAGS := user # 定义模块标签为user
LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES) #指定模块的安装路径
LOCAL_EXPORT_PACKAGE_RESOURCES := true #为true时,其他的apk模块能引用本模块的资源
include $(BUILD_PACKAGE)
(5) 编译一个可执行文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := service.cpp
LOCAL_SHARED_LIBRARIES := libutils libbinder #指定模块需要链接的动态库
ifeq ($(TARGET_OS), linux)
LOCAL_CFLAGS += -DXP_UNIX #定义编译标志
endif
LOCAL_MODULE := service #指定模块的名称
include $(BUILD_EXECUTABLE) #指定模块的名称
(6) 编译一个native 的共享库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := libnativedynamic # 指定模块的名称
LOCAL_SRC_FILES := \ # 指定模块的源文件
nativedynamic.cpp
LOCAL_SHARED_LIBRARIES := \ # 指定模块需要链接的动态库
libcutils \
libutils
LOCAL_STATIC_LIBRARIES := libnativestatic # 指定模块依赖的静态库
LOCAL_C_INCLUDES += \ # 指定头文件的查找路径
$(JNI_H_INCLUDE) \
$(LOCAL_PATH)/../include
LOCAL_CFLAGS += -O # 定义编译标志
include $(BUILD_SHARED_LIBRARY)
(7) 编译一个native静态库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional #指定模块的标签
LOCAL_MODULE := libnativestatic #指定模块的名称
LOCAL_SRC_FILES := \ #指定模块的源文件
nativestatic.cpp
LOCAL_C_INCLUDES +=
LOCAL_CFLAGS += -o #定义编译标志
include $(BUILD_STATIC_LIBRARY)
- 预编译模块的目标定义
在实际的系统开发中,并不会像Android一样将所有源码集中在一起编译,有很多APK文件,jar包等都是预先编译好的,编译系统时需要将这些二进制文件复制到生成的image文件中。
常用的方法是通过 PRODUCT_COPY_FILES 变量将这些文件直接复制到生成的image文件中。但有些APK文件或jar包,需要使用系统的签名文件才能正常运行,这样用复制的方式就行不通了。另外,一些动态库文件可能是源码中的某些模块所依赖的,用复制的方法也无法建立依赖关系,这将导致这些模块编译失败。
Android 可通过定义预编译模块的方式来解决上述问题。
定义一个预编译模块和定义一个普通的编译模块格式相似。不同的是LOCAL_SRC_FILES 变量指定的不是源文件,而是二进制文件的路径,同时还要通过 LOCAL_MODULE_CLASS 来指定模块的类型,最后 include 的是 BUILD_PREBUILT 变量定义的编译文件。
下面是常见预编译模块的示例:
(1) 定义apk文件目标
include $(CLEAR_VARS)
LOCAL_MODULE := ThemeManager.apk #这里可以是任何字符串,但必须是系统唯一的目标
LOCAL_SRC_FILES := app/$(LOCAL_MODULE)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := APPS #这里的值是APPS
LOCAL_CERTIFICATE := platform #这里可以指定签名方式
include $(BUILD_PREBUILT)
(2) 定义静态jar包目标
include $(CLEAR_VARS)
LOCAL_MODULE := libfirewall.jar
LOCAL_SRC_FILES := app/$(LOCAL_MODULE)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := JAVA_LIBRARIES #这里的值是JAVA_LIBRARIES
LOCAL_CERTIFICATE := platform
include $(BUILD_PREBUILT)
(3) 定义动态库文件目标
include $(CLEAR_VARS)
LOCAL_MODULE := libglobaltheme_jni.so
LOCAL_MODULE_OWNER :=
LOCAL_SRC_FILES := lib/$(LOCAL_MODULE)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := SHARED_LIBRARIES # 这里的值是SHARED_LIBRARIES
include $(BUILD_PREBUILT)
(4) 定义可执行文件目标
include $(CLEAR_VARS)
LOCAL_MODULE := bootanimation
LOCAL_MODULE_OWNER :=
LOCAL_SRC_FILES := bin/bootanimation
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := EXECUTABLES #这里的值是EXECUTABLES
LOCAL_MODULE_PATH := $(TARGET_OUT)/bin #还可以指定复制到的目标目录
include $(BUILD_PREBUILT)
(5) 定义xml文件目标
include $(CLEAR_VARS)
LOCAL_MODULE := apns-conf-cu.xml
LOCAL_MODULE_OWNER :=
LOCAL_SRC_FILES := etc/$(LOCAL_MODULE)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := ETC # ETC表示文件将复制到/system/etc 目录下
include $(BUILD_PREBUILT)
(6) 定义host平台下的jar包
这例子挺有意思的,将系统编译时用到的 signapk.jar 预编译,然后复制到out目录下,这样Build系统将能够使用这个文件来给其他文件签名:
include $(CLEAR_VARS)
LOCAL_MODULE := signapk
LOCAL_PREBUILT_JAVA_LIBRARIES := lib/$(LOCAL_MODULE).jar
include $(BUILD_HOST_PREBUILT)
该例子中除了使用 BUILD_HOST_PREBUIT 表示目标定义是针对编译平台而不是设备平台外,还给出了定义预编译jar包模块的另外一种定义方式,即使用变量 LOCAL_PREBUILT_JAVA_LIBRARIES 来定义。
- 常用的 "LOCAL_" 变量
编写模块的编译文件,实际就是定义一系列以 "LOCAL_" 开头的编译变量,下表罗列了一些经常使用的编译变量和说明:
变量名 | 说明 |
---|---|
LOCAL_ASSET_FILES | 编译APK文件时用于指定资源列表,通常写成 LOCAL_ASSET_FILES += $(call find-subdir-assets) |
LOCAL_CC | 自定义C编译器来代替缺省的编译器 |
LOCAL_CXX | 自定义C++编译器来代替缺省的编译器 |
LOCAL_CFLAGS | 定义额外的C/C++编译器的参数 |
LOCAL_CPPFLAGS | 仅定义额外的C++编译器的参数,不用在C编译器中 |
LOCAL_CPP_EXTENSION | 自定义C++源文件的后缀。例如:LOCAL_CPP_EXTENSION := .cc 注意:一旦定义,模块中所有源文件都必须使用该后缀,目前不支持混合后缀 |
LOCAL_C_INCLUDES | 指定头文件的搜索路径 |
LOCAL_FORCE_STATIC_EXECUTABLE | 如果编译时需要链接的库有共享和静态两者并存的情况。设定此变量值为true将会优先链接静态库。通常这种情况只会在编译root/sbin目录下的应用才会用到,因为它们执行的时间比较早,文件系统的其他部分还没有加载 |
LOCAL_GENERATED_SOURCES | 指定由系统自动生成的源文件列表 |
LOCAL_MODULE_TAGS | 定义模块标签,Build系统根据标签决定哪些模块将安装 |
LOCAL_REQUIRED_MODULES | 指定依赖的模块。一旦本模块被安装,通过此变量指定的模块也将被安装 |
LOCAL_JAVACFLAGS | 定义额外的Javac编译器的参数 |
LOCAL_JAVA_LIBRARIES | 指定模块依赖的Java共享库 |
LOCAL_LDFLAGS | 定义链接器ld的参数 |
LOCAL_LDLIBS | 指定模块链接时依赖的库。如果这些库文件不存在,并不会引发对它们的编译。这是此变量和LOCAL_SHARED_LIBRARIES的主要区别 |
LOCAL_NO_MANIFEST | 在一个资源apk中可以指定此变量为true,表示此apk文件没有AndroidManifest.xml文件 |
LOCAL_PACKAGE_NAME | 指定APP应用名称 |
LOCAL_PATH | 指定Android.mk 文件所在的目录 |
LOCAL_POST_PROCESS_COMMAND | 在编译host相关的模块时,可以用此变量定义一条命令在link完成后执行 |
LOCAL_PREBUILT_LIBS | 指定预编译C/C++动态和静态库列表。用于预编译模块定义中 |
LOCAL_PREBUILT_JAVA_LIBRARIES | 指定预编译Java库列表。用于预编译模块定义中 |
LOCAL_SHARED_LIBRARIES | 指定模块依赖的C/C++共享库列表 |
LOCAL_SRC_FILES | 指定源文件列表 |
LOCAL_STATIC_LIBRARIES | 指定依赖的C/C++静态库列表 |
LOCAL_MODULE | 除应用(apk)以 LOCAL_PACKAGE_NAME指定模块名以外,其余的模块都以LOCAL_MODULE指定模块名 |
LOCAL_MODULE_PATH | 指定模块在目标系统的安装路径 |
LOCAL_UNSTRIPPED_PATH | 指定模块的unstripped版本在out目录下的保存路径 |
LOCAL_WHOLE_STATIC_LIBRARIES | 这个变量也定义了模块依赖的静态库列表,和 LOCAL_STATIC_LIBRARIES 类似。但通过这个变量定义,链接时链接器不会将静态库中无人调用的代码去掉 |
LOCAL_YACCFLAGS | 指定yacc的参数 |
LOCAL_ADDITIONAL_DEPENDENCIES | 指定本模块的依赖。用在不方便使用别的方法来指定依赖关系时 |
LOCAL_BUILT_MODULE | 指定编译时存放中间文件的目录 |
LOCAL_INSTALLED_MODULE | 指定模块的安装路径 |
LOCAL_MODULE_CLASS | 定义模块的分类。根据分类,生成的模块文件会安装到目标系统相应的目录下。例如,APPS:安装到/system/app下;SHARED_LIBRARIES:安装到/system/lib下; EXECUTABLES:安装到/system/bin下;ETC:安装到/system/etc 下;但如果同时用LOCAL_MODULE_PATH定义了路径,则安装到该路径 |
LOCAL_MODULE_NAME | 指定模块的名称 |
LOCAL_MODULE_SUFFIX | 指定当前模块的后缀。一旦指定,系统在产生目标文件时,会以模块名加后缀创建目标文件 |
LOCAL_STRIP_MODULE | 指定模块是否需要strip,该模块是可执行文件或动态库时才能使用该变量 |
LOCAL_STRIPPABLE_MODULE | 此变量的值通常由Build系统设置,一般编译可执行文件和动态库时设为true |
LOCAL_SYSTEM_SHARED_LIBRARIES | 此变量在编译系统的基本库,如libc、libm、libdl时,用来定义这些库的依赖库。通常在应用模块定义中不应该使用该变量 |
LOCAL_PRELINK_MODULE | 编译 .so 模块时,定义是否需要prelink。prelink通过预链接的方式来加快程序启动速度。如果要设置此值为true,要先在build/core/prelink-linux-arm.map文件中定义该库的地址和大小,否则报错。但在Android4.2以后的代码中找不到文件prelink-linux-arm.map了,在build目录下也搜寻不到这个变量。可能Android 已经取消了prelink的功能 |
参考
http://gityuan.com/2016/03/19/android-build/
https://blog.csdn.net/luoshengyang/article/details/18928789
https://www.jianshu.com/p/4a6d8c0e034a
https://www.ibm.com/developerworks/cn/opensource/os-cn-android-build/index.html
https://source.android.com/setup/develop/64-bit-builds
《深入解析Android 5.0系统》