这篇文章将从整体上概述下AOSP 编译时的产品配置相关知识。Android 的产品配置文件的作用是按照Build系统的要求,将生成产品的各种image文件所需要的对应信息(如版本号等各种参数)、资源(图片、字体、多媒体等)、二进制文件(apk、jar、so库等)有机组织起来,同时根据配置文件进行裁剪或增加模块,所谓系统支持在一定程度上其实就像将相应的资源添加到对应的文件中。
要了解Android编译选项eng、user和userdebug的区别,需先了解下LOCAL_MODULE_TAGS这一Android.mk文件里的配置项,这个选项告诉了当前模块是否需要被编译打包到系统固件中。
所以该选项一般会被赋值user\userdebug\eng中的一个或多个。
如: LOCAL_MODULE_TAGS := user eng optional test 这个样子。
那么LOCAL_MODULE_TAGS设置为不同值有何作用呢?
下面是对应不同值编译的结果:
默认的编译类型,该编译类型适用于开发阶段。
执行make即make eng,编译时以下模块将安装进系统:
在Android.mk里使用LOCAL_MODULE_TAGS变量定义了标签为:
eng、debug、shell_$(TARGET_SHELL)、user和development的模块
非APK模块且不带任何标签的模块
所有产品配置文件中指定的APK模块
编译后adb是默认开启的,附带以下系统属性:
ro.secure=0
ro.debuggable=1
ro.kernel.android.checkjni=1
该编译类型适合用于最终发布阶段,编译时以下模块将安装进系统:
在Android.mk里使用LOCAL_MODULE_TAGS变量定义了标签为:
shell_$(TARGET_SHELL)和user的模块
非APK模块且不带任何标签的模块
所有产品配置文件中指定的APK模块,同时忽略其标签属性
编译后adb是默认不开启,需要自己手动打开,附带以下系统属性:
ro.secure=0
ro.debuggable=0
该编译类型适合用于 debug 阶段,编译时以下模块将安装进系统:
在Android.mk里使用LOCAL_MODULE_TAGS变量定义了标签为:
`shell_$(TARGET_SHELL)、user和debug
非APK模块且不带任何标签的模块
所有产品配置文件中指定的APK模块,同时忽略其标签属性
编译后具有root权限,adb是默认开启。附带以下系统属性:
ro.secure=0
ro.debuggable=1
Android.mk文件可以编译出不同的模块。
本质上是通过include $(BUILD_) 指令把其他模块编译文件包含进来。
常见的模块编译变量(以BUILD_为前缀)有:
编译变量 | 描述 |
---|---|
BUILD_HOST_STATIC_LIBRARY | 包含host_static_library.mk文件,生成编译平台的本地静态库 |
BUILD_HOST_SHARED_LIBRARY | 包含host_shared_library.mk文件,生成编译平台的本地共享库 |
BUILD_STATIC_LIBRARY | 包含static_library.mk文件,生成目标系统的本地静态库 |
BUILD_SHARED_LIBRARY | 包含shared_library.mk文件,生成目标系统的本地共享库 |
BUILD_HOST_EXECUTABLE | 包含host_executable.mk文件,生成编译平台的Linux可执行程序 |
BUILD_EXECUTABLE | 包含executable.mk文件,生成目标系统的Linux可执行程序 |
BUILD_PACKAGE | 包含package.mk文件,生成apk |
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 | 包含java_static_library.mk文件,生成Java 静态库 |
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_JAVA_LIBRARIES := #依赖的Java 共享库
LOCAL_SHARED_JAVA_LIBRARIES := #依赖的Java 静态库
# 使用系统函数自动搜索src 目录下的java文件形成的源码列表
LOCAL_SRC_FILES:= $(call all-java-files-under, src)
LOACAL_MODULE_TAGS:= eng user #可选的模块标签
LOCAL_CERTIFICATE:= #APK 签名方式 如 platform
LOCAL_PACKAGE_NAME:= #apk名称
include $(BUILD_PACKAGE)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_JAVA_LIBRARIES := #依赖的Java 共享库
LOCAL_STATIC_JAVA_LIBRARIES := #依赖的Java 静态库
# 使用系统函数自动搜索src 目录下的java文件形成的源码列表
LOCAL_SRC_FILES:= $(call all-java-files-under, src)
LOACAL_MODULE_TAGS:= #可选的模块标签
LOCAL_MODULE:= #java库名称
include $(BUILD_JAVA_LIBRARY) #编译共享库
# include $(BUILD_STATIC_JAVA_LIBRARY) 编译静态库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:=helloworld.c #c源文件
LOCAL_MODULE:= libtest_static #库名称
LOCAL_C_INCLUDES := # 编译标志
LOCAL_STATIC_LIBRARIES := # 依赖.a静态库
LOCAL_SHARED_LIBRARIES := # 依赖.so动态库
include $(BUILD_SHARED_LIBRARY) # 编译动态so库
# include $(BUILD_STATIC_LIBRARY) 编译静态库.a库
在实际系统开发中,并不会像Android一样将所有的源码一起编译,事实上有很多的APK、jar包都是需要预先编译好的,编译时通过PRODUCT_COPY_FILES 变量将这些二进制文件复制到生成的image文件中。
但是针对一些APK或者jar包需要使用系统签名才能正常运行或者系统所需的依赖动态库文件,这种简单复制方式就不行了。于是Android 定义预编译模块来解决之,定义一个预编译模块和定义上面那种普通模块语法相似,区别在于预编译模块的LOCAL_SRC_FILES变量配置的是二进制文件的路径且需要配合LOCAL_MODULE_CLASS来设置模块类型,最后再通过include $(BUILD_PREBUILT)。
Android提供了Perbuilt编译方式,用于处理已经编译好的库或配置文件使其打包到系统image中。
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Module name should match apk name to be installed 系统唯一的名称
LOCAL_MODULE := LamyNetServer
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_CLASS := APPS #类别是APK
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := platform # 使用系统的platform级别签名
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_DEX_PREOPT := false
LOCAL_PRIVILEGED_MODULE := true
include $(BUILD_PREBUILT)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := xxxxx
LOCAL_SRC_FILES :=app/$(LOCAL_MODULE)
LOCAL_MODULE_CLASS := JAVA_LIBRARIES #类别是Java静态jar包
LOCAL_CERTIFICATE := platform # 使用系统的platform级别签名
include $(BUILD_PREBUILT)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := xxxxx
LOCAL_SRC_FILES :=app/$(LOCAL_MODULE)
LOCAL_MODULE_CLASS := SHARED_LIBRARIES #类别是SHARED_LIBRARIES
include $(BUILD_PREBUILT)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := xxxxx
LOCAL_SRC_FILES :=
LOCAL_MODULE_CLASS := EXECUTABLES #类别是EXECUTABLES
include $(BUILD_PREBUILT)
LOCAL_MODULE_CLASS 在采用预装这种形式的时候,该变量来指定预装的模块的类型;同时如果LOCAL_MODULE_PATH这个没有定义时,通过规则自动生成预编译模块的安装路径。
LOCAL_MODULE_CLASS用于制定LOCAL_MODULE_PATH的路径所在。
如果在Android.mk没有直接明确LOCAL_MODULE_PATH 的话,需要通过以下规则来自动生成>base_rules.mk:154 LOCAL_MODULE_PATH := $(strip $(LOCAL_MODULE_PATH)) 155 ifeq ($(LOCAL_MODULE_PATH),) 156 #LOCAL_MODULE_CLASS := 157 LOCAL_MODULE_PATH := $($(my_prefix)OUT$(partition_tag)_$(LOCAL_MODULE_CLASS)) 158 $(info *******//$(LOCAL_MODULE_PATH)) 159 ifeq ($(strip $(LOCAL_MODULE_PATH)),) 160 $(error $(LOCAL_PATH): unhandled LOCAL_MODULE_CLASS "$(LOCAL_MODULE_CLASS)") 161 endif 162 endif
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
在不同的Android.mk文件中,对于模块include()不同的编译类型选项,比如对于Library或者app,execut等在调用对应的处理mk文件时,会默认就指定当前的LOCAL_MODULE_CLASS的值,比如EXECUTABLES、SHARED_LIBRARIES等。所以在自己编写的Android.mk可不显示的指定LOCAL_MODULE_CLASS的值。
但当遇到include $(BUILD_PREBUILT)的预编译选项时不会指定模块模块编译输出的类型CLASS,需要在自己编写的Android.mk中明确指定LOCAL_MODULE_CLASS的值如ETC/APP等,使其值为非空,从而帮助系统确定LOCAL_MODULE_PATH的路径,比如最终编译输出LOCAL_MODULE_PATH := $(TARGRT_OUT_ETC)。
```bash
LOCAL_MODULE_CLASS := ETC #表示放于system/etc目录 ${TARGET_OUT_ETC}
LOCAL_MODULE_CLASS := EXECUTABLES #放于/system/bin ${TARGET_OUT_BIN}
LOCAL_MODULE_CLASS := SHARED_LIBRARIES #放在/system/lib下 ${TARGET_OUT_LIB}
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_PATH := $(call my-dir)
##############################
include $(CLEAR_VARS)
LOCAL_SRC_FILES := xxx.file
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := /xxx/xxx/xxx/ #指定具体copy目录
LOCAL_MODULE := xxx.file
include $(BUILD_PREBUILT)
这种情况通常用于当前某一个模块引用了第三方静态依赖库.a文件。 在编译的时候提示:
ndk/18.1.5063045/build//../build/core/build-binary.mk:707: Android NDK: Module SerialPortWrapperTest depends on undefined modules: usb1.0 18.1.5063045/build//../build/core/build-binary.mk:720: *** Android NDK: Aborting (set APP_ALLOW_MISSING_DEPS=true to allow missing dependencies) . Stop
这个时候需要将静态库.a文件在链接的时候告诉链接器。比如在要引用这个静态库的地方加上:
LOCAL_LDLIBS += libusb1.0
虽然可以编译过但会有如下警告提示:
> Task :libxserialport:ndkBuild Android NDK: WARNING:cpp/pulsewrapper/android/jni/Android.mk:SerialPortWrapperTest: non-system libraries in linker flags: cpp/pulsewrapper/src/test/../../../libs/armeabi-v7a/libusb1.0.a Android NDK: This is likely to result in incorrect builds. Try using LOCAL_STATIC_LIBRARIES Android NDK: or LOCAL_SHARED_LIBRARIES instead to list the library dependencies of the Android NDK: current module
得到一个“linker flags”中的“非系统库”作为警告,警告您没有使用默认的系统库(usr/lib),这可能非常好,但也可能导致错误(不同库版本之间的不兼容性)。这个警告bug是否完全取决于你自己。
因此我们需要通过预编译的将这个库声明系统库。 这样我们就可以在编译的模块中通过:
LOCAL_STATIC_LIBRARIES进行引用
ifeq ($(APP_LIBUSB_MODULE_HAS), yes)
include $(CLEAR_VARS)
LOCAL_MODULE := usb1.0
LOCAL_EXPORT_C_INCLUDES := $(LIBUSB_ROOT_REL)/../libs/$(APP_ABI)/include
LOCAL_SRC_FILES := $(LIBUSB_ROOT_REL)/../libs/$(APP_ABI)/libusb1.0.a
include $(PREBUILT_STATIC_LIBRARY)
endif