Android系统源码Build系统入门详解

前言

最近在做系统开发,很多知识都要重新归纳和整理,最近在编写Makefile文件的时候,网上找了些文章发现这篇文章对于入门很有价值《Android系统源码Build系统入门详解》文章大部分的内容都是转载原文的,我准备整理下,放在我自己的博客下方便我自己查阅。

1.包含C的Android.mk文件

以前早期版本的时候,我们再下载NDK的时候,通常都会有一个hello-jni的用例,方便我们自己去学习和理解,由于找不到了,我这里就直接将内容贴出来。

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)

第一行的解释

LOCAL_PATH := $(call my-dir)

此变量指示源文件在开发树中所处的位置。在这里,构建系统提供的宏函数my-dir将返回当前目录(Android.mk文件本身所在的目录)的路径。
LOCAL_PATH:是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 是默认标签。标签是提供给编译类型使用的。不同的编译类型会安装包含不同标签的模块,那么这些标签的值有代表什么呢?

名称 说明
eng 默认类型,该编译类型适用于开发阶段。 当选择这种类型时,编译结果将: 安装包含 eng, debug, user,development 标签的模块 安装所有没有标签的非 APK 模块 安装所有产品定义文件中指定的 APK 模块
user 该编译类型适合用于最终发布阶段。 当选择这种类型时,编译结果将: 安装所有带有 user 标签的模块 安装所有没有标签的非 APK 模块 安装所有产品定义文件中指定的 APK 模块,APK 模块的标签将被忽略
userdebug 该编译类型适合用于 debug 阶段。 该类型和 user 一样,除了: 会安装包含 debug 标签的模块 编译出的系统具有 root 访问权限。

第二行的解释

include $(CLEAR_VARS)

CLEAR_VARS 变量指向一个特殊的 GNU Makefile,后者会清除许多 LOCAL_XXX 变量,例如 LOCAL_MODULELOCAL_SRC_FILESLOCAL_STATIC_LIBRARIES。 请注意,GNU Makefile 不会清除 LOCAL_PATH。 此变量必须保留其值,因为系统在单一 GNU Make 执行环境(其中的所有变量都是全局变量)中解析所有构建控制文件。

在描述每个模块之前,必须声明(重新声明)此变量。

第三行

LOCAL_MODULE := hello-jni

我们回到上面看下LOCAL_MODULE的定义。LOCAL_MODULE 模块必须定义,以表示Android.mk中的每一个模块。名字必须唯一且不包含空格。Build System会自动添加适当的前缀和后缀。例如,foo要产生动态库,则生成libfoo.so。但请注意:如果模块名被定为:libfoo,则生成libfoo.so. 不再加前缀。模块间的依赖关系就是通过这个名称来引用的。

第四行

LOCAL_SRC_FILES := hello-jni.c

这句话的意思就是说,我这个工程的源码路径只有一个文件,这个文件就是我这个MK文件所在目录下的hello-jni.c

第五行

include $(BUILD_SHARED_LIBRARY)

最后一行帮助系统将所有内容连接到一起。BUILD_SHARED_LIBRARY 变量指向一个 GNU Makefile 脚本,该脚本用于收集您自最近 include 以来在 LOCAL_XXX 变量中定义的所有信息。 此脚本确定要构建的内容以及构建方式。

2.不含C的Android.mk文件

其实上面就是在源码树下我们添加jni文件的mk写法。我们先来研究一下纯不包含C文件的Android.mk文件,也就是我们写的第一个程序helloworld在安卓源码树中的展现。我们来分析分析Android.mk内容。

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

  # Build all java files in the java subdirectory
  LOCAL_SRC_FILES := $(call all-subdir-java-files)

  # Name of the APK to build
  LOCAL_PACKAGE_NAME := LocalPackage

  LOCAL_CERTIFICATE := platform

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

第二行

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

我们看到这里的写法和上面C的写法不相同,all-subdir-java-files代表的是子目录下的所有java文件,那么如果是有多个C文件怎么办?在Build系统中还定义了一些便捷的函数在Android.mk中使用,如下几种:

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

第三行

LOCAL_PACKAGE_NAME := LocalPackage //应用名称

这个指的是当前 APK 应用的名称,这个和LOCAL_MODULE不一样,它不是唯一的,就相当于Manifest.xml清单文件里面的appName。

第四行

LOCAL_CERTIFICATE := platform//签署当前应用的证书名称。

这个名字对应的系统签名,系统前面包含四种签名类型。

签名类型

  • testkey
  • media
  • platform
  • shared

以上的四种,可以在源码的/build/target/product/security里面看到对应的**,其中shared.pk8代表私钥,shared.x509.pem公钥,一定是成对出现的。其中testkey是作为android编译的时候默认的签名key,如果系统中的apk的android.mk中没有设置LOCAL_CERTIFICATE的值,就默认使用testkey。而如果设置成:LOCAL_CERTIFICATE := platform 就代表使用platform来签名,这样的话这个apk就拥有了和system相同的签名,因为系统级别的签名也是使用的platform来签名,此时使用android:sharedUserId="android.uid.system"才有用!

第五行

include $(BUILD_PACKAGE)

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_SHARED_LIBRARY
  • BUILD_EXECUTABLE
  • BUILD_PACKAGE BUILD_PREBUILT
  • BUILD_MULTI_PREBUILT
  • BUILD_JAVA_LIBRARY
  • BUILD_HOST_JAVA_LIBRARY

通过名称大概就可以猜出每个变量所对应的模块类型。(在模块的 Android.mk文件中,只要包含进这里对应的常量便可以执行相应类型模块的编译。

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

文件名 说明
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库

依赖关系

Android系统源码Build系统入门详解

3.变量和宏

构建系统提供许多可用于Android.mk文件中的变量。其中的许多变量已预先复制。另一些变量由您赋值。

  • 以 LOCAL_ 开头的名称,例如 LOCAL_MODULE。
  • 以 PRIVATE_、NDK_ 或 APP 开头的名称。 构建系统在内部使用这些变量。
  • 小写名称,例如 my-dir。 构建系统也是在内部使用这些变量。
    如果您为了方便而需要在 Android.mk 文件中定义自己的变量,建议在名称前附加 MY_

NDK定义的包括变量

本小节探讨构建系统在解析Android.mk文件前定义的GNU Make变量。在某些情况下,NDK可能会解析Android.mk文件,每次使用其中某些变量的不同的定义。

  • CLEAR_VARS
    此变量指向的构建脚本用于取消定义下文“开发者定义的变量”一节中列出的几乎全部 LOCAL_XXX 变量。 在描述新模块之前,请使用此变量来包括此脚本。 使用此变量的语法为:

    include $(CLEAR_VARS)

  • BUILD_SHARED_LIBRARY
    此变量指向的构建脚本用于收集您在 LOCAL_XXX 变量中提供的模块的所有相关信息,以及确定如何根据列出的源文件构建目标共享库。 请注意,使用此脚本要求您至少已经为 LOCAL_MODULE 和 LOCAL_SRC_FILES 赋值(如需了解有关这些变量的详细信息,请参阅模块描述变量)。
    使用此变量的语法为:

    include $(BUILD_SHARED_LIBRARY)

共享库变量会导致构建系统生成扩展名为 .so 的库文件。

  • PREBUILT_SHARED_LIBRARY
    指向用于指定预建共享库的构建脚本。 与 BUILD_SHARED_LIBRARY 和 BUILD_STATIC_LIBRARY 的情况不同,这里的 LOCAL_SRC_FILES 值不能是源文件, 而必须是指向预建共享库的单一路径,例如 foo/libfoo.so。 使用此变量的语法为:

    include $(PREBUILT_SHARED_LIBRARY)

    您也可以使用 LOCAL_PREBUILTS 变量来引用另一个模块中的预建库。 如需了解有关使用预建库的详细信息,请参阅使用预建库

  • PREBUILT_STATIC_LIBRARY
    与 PREBUILT_SHARED_LIBRARY 相同,但用于预构建的静态库。 如需了解有关使用预建库的详细信息,请参阅使用预建库

结束语

在示例目录中有更为复杂的示例,包括带有注释的 Android.mk 文件供您查看。 此外,示例:native-activity 详细说明该示例的 Android.mk 文件。 最后,变量和宏提供本节中变量的更多信息。

参考链接