1. Ninja
1.1 Ninja简介
Ninja 是一个专注于速度的小型构建系统。它与其他构建系统在两个主要方面不同:(是个系统,管理构建的)
- Ninja的输入文件被设计为由更高级的构建系统生成。
- Ninja被设计为尽可能快地运行构建, 其他构建系统基于高级语言,而Ninja基于汇编。
Ninja基于汇编,专注于速度,不支持分支、循环等流程控制,也不支持逻辑运算,但它允许以其它语言如来维护这些复杂的编译流程和逻辑。
例如,我们可以采用Makefile, go, python等等来维护编译的流程和逻辑。
1.2 Ninja的构建文件
虽然Ninja的构建文件是可读的(human-readable),但是手写不是特别方便。我们可以先来看一段ninja文件的内容:
# This file is used to build ninja itself. # It is generated by configure.py. ninja_required_version = 1.3 # The arguments passed to configure.py, for rerunning it. configure_args = root = . builddir = build cxx = g++ ar = ar cflags = -g -Wall -Wextra -Wno-deprecated -Wno-missing-field-initializers $ -Wno-unused-parameter -fno-rtti -fno-exceptions -fvisibility=hidden $ -pipe '-DNINJA_PYTHON="python"' -O2 -DNDEBUG -DUSE_PPOLL $ -DNINJA_HAVE_BROWSE -I. ldflags = -L$builddir rule cxx command = $cxx -MMD -MT $out -MF $out.d $cflags -c $in -o $out description = CXX $out depfile = $out.d deps = gcc rule ar command = rm -f $out && $ar crs $out $in description = AR $out rule link command = $cxx $ldflags -o $out $in $libs description = LINK $out
可以看到,ninja的构建文件,书写起来是不很方便的,所以,我们需要一些ninja构建文件的生成器。
这些生成器就是一些元构建系统(meta-build system),例如Blueprint、CMake等等。
Ninja的基于底层实现使其非常适合嵌入到这些功能更强大的构建系统中。
更多ninja构建文件的生成器,可参考:List of generators producing ninja build files。
Ninja用于构建Google Chrome浏览器,Android,LLVM的一部分。由于CMake可在大多数平台上运行,并且可以生成多种格式的项目文件,包括Ninja。所以Ninja也可以作为CMake的底层实现,在许多其他项目中使用。
1.3 Ninja的下载和使用
Ninja的最新版本为v1.10.2,于2020年11月23日发布:Ninja release v1.10.2。
当然我们也可以下载ninja源码自行编译:
$ git clone git://github.com/ninja-build/ninja.git && cd ninja $ git checkout release $ cat README
Ninja的设计哲学和设计背景,是否以及如何在项目中使用Ninja,Ninja的平台支持以及Ninja详细的语言语义等等更多Ninja相关的知识,请参考:
Ninjia使用手册
2. Blueprint
2.1 Blueprint简介
Blueprint 是一个元构建系统,该系统读取Blueprint文件来描述需要构建的模块,并生成一个Ninja清单来描述需要运行的命令及其依赖项。
在大多数构建系统使用内置规则或特定领域语言来描述将模块描述转换为构建规则的逻辑。
Blueprint将其委托给使用Go编写的针对每个项目的构建逻辑。
对于大型,异构项目,这允许以高级语言维护固有、复杂的构建逻辑,同时仍可以通过修改易于理解的Blueprint文件来对单个模块进行简单更改。
2.2 android中的Blueprint
前面在Ninja的构建文件一节我们提到了,Blueprint是ninja构建文件的生成器。android 编译系统soong集成了Blueprint,Blueprint可将我们编写的android.bp解析生成一个ninja构建文件。
我们在编译一个模块时,只需要将这个模块的android.bp文件配置好,编译系统会自动为这个模块生成ninja清单,最终使用ninja来调用gcc、clang、java、dex、aapt2等等命令来构建模块。
3. kati
kati 是Google开发的一个实验性的GNU make clone,kati的主要目标是加快Android的增量构建。目前,kati本身并不能提供更快的构建。 而是将Makefile转换为ninja。一开始kati是用go语言开发的,但作者发现使用go写的有性能问题,后来作者又用C++进行了重写,也就是kati变成了ckati(作者的原文描述可以查看android源码目录下的:build/kati/INTERNALS.md)。后续提到kati时,如不特别指出,即是指ckati。
简单点说,kati就是一个转换工具,它可以将Makefile和.mk文件转换为ninja。
android源码目录下的:prebuilts/build-tools下有预置的kati,Android 7.0及以上版本,编译源码时会自动使用kati。大多数情况下,我们不会直接使用kati,但如果你还想了解更多kati的相关信息,可以访问:https://github.com/google/kati。也可以查看android源码目录下的 build/kati/INTERNALS.md 和 build/kati/README.md。
4. soong
4.1 soong简介
在android 6.0版本之前,编译android源码采用的是基于make的编译系统(make-based build system),也就是android的各个库、APK等等目标文件都是采用make来构建的。 但是,由于make在编译时表现出效率不够高、增量编译速度慢等问题,Google在android 7.0版本引进了编译速度更快的soong来替代make。
Soong集成了Ninja, 而Ninja专注于速度,没有条件或流程控制语句,也不支持逻辑运算。但它允许以其它语言如来维护这些复杂的编译流程和逻辑。例如,我们可以继续采用makefile, 或者采用go语言来维护编译流程和逻辑。
上面已经提到了Ninja,Blueprint, kati等等好几种工具,为了完整、快速的构建一个android系统,就需要一个“管家”来协调这些工具。例如,将.bp转换成ninja时使用Blueprint, 将Makefile转换成ninja时使用kati。这个选择转换工具、选择解析框架、解析维护构建逻辑的“管家”就是soong。
编译android源码时,soong也会被自动使用,我们可以和原来一样:
首先,source build/envsetup.sh。
然后,lunch选择target。
最后,使用m、mm、mmm或者make来编译指定的模块或者整个系统。
但是,m、mm、mmm或者make最终都会使用soong来编译。
因为source之后,build/envsetup.sh中已经将make指向soong了
function get_make_command() { # If we're in the top of an Android tree, use soong_ui.bash instead of make if [ -f build/soong/soong_ui.bash ]; then # Always use the real make if -C is passed in for arg in "$@"; do if [[ $arg == -C* ]]; then echo command make return fi done echo build/soong/soong_ui.bash --make-mode else echo command make fi } function make() { _wrap_build $(get_make_command "$@") "$@" } function m() { local T=$(gettop) if [ "$T" ]; then _wrap_build $T/build/soong/soong_ui.bash --make-mode $@ else echo "Couldn't locate the top of the tree. Try setting TOP." return 1 fi } function mm() { local T=$(gettop) # If we're sitting in the root of the build tree, just do a # normal build. if [ -f build/soong/soong_ui.bash ]; then _wrap_build $T/build/soong/soong_ui.bash --make-mode $@ else # Find the closest Android.mk file. local M=$(findmakefile) local MODULES= local GET_INSTALL_PATH= local ARGS= # Remove the path to top as the makefilepath needs to be relative local M=`echo $M|sed 's:'$T'/::'` if [ ! "$T" ]; then echo "Couldn't locate the top of the tree. Try setting TOP." return 1 elif [ ! "$M" ]; then echo "Couldn't locate a makefile from the current directory." return 1 else local ARG for ARG in $@; do case $ARG in GET-INSTALL-PATH) GET_INSTALL_PATH=$ARG;; esac done if [ -n "$GET_INSTALL_PATH" ]; then MODULES= ARGS=GET-INSTALL-PATH-IN-$(dirname ${M}) ARGS=${ARGS//\//-} else MODULES=MODULES-IN-$(dirname ${M}) # Convert "/" to "-". MODULES=${MODULES//\//-} ARGS=$@ fi if [ "1" = "${WITH_TIDY_ONLY}" -o "true" = "${WITH_TIDY_ONLY}" ]; then MODULES=tidy_only fi ONE_SHOT_MAKEFILE=$M _wrap_build $T/build/soong/soong_ui.bash --make-mode $MODULES $ARGS fi fi } function mmm() { local T=$(gettop) if [ "$T" ]; then local MAKEFILE= local MODULES= local MODULES_IN_PATHS= local ARGS= local DIR TO_CHOP local DIR_MODULES local GET_INSTALL_PATH= local GET_INSTALL_PATHS= local DASH_ARGS=$(echo "$@" | awk -v RS=" " -v ORS=" " '/^-.*$/') local DIRS=$(echo "$@" | awk -v RS=" " -v ORS=" " '/^[^-].*$/') for DIR in $DIRS ; do DIR_MODULES=`echo $DIR | sed -n -e 's/.*:\(.*$\)/\1/p' | sed 's/,/ /'` DIR=`echo $DIR | sed -e 's/:.*//' -e 's:/$::'` # Remove the leading ./ and trailing / if any exists. DIR=${DIR#./} DIR=${DIR%/} if [ -f $DIR/Android.mk -o -f $DIR/Android.bp ]; then local TO_CHOP=`(\cd -P -- $T && pwd -P) | wc -c | tr -d ' '` local TO_CHOP=`expr $TO_CHOP + 1` local START=`PWD= /bin/pwd` local MDIR=`echo $START | cut -c${TO_CHOP}-` if [ "$MDIR" = "" ] ; then MDIR=$DIR else MDIR=$MDIR/$DIR fi MDIR=${MDIR%/.} if [ "$DIR_MODULES" = "" ]; then MODULES_IN_PATHS="$MODULES_IN_PATHS MODULES-IN-$MDIR" GET_INSTALL_PATHS="$GET_INSTALL_PATHS GET-INSTALL-PATH-IN-$MDIR" else MODULES="$MODULES $DIR_MODULES" fi MAKEFILE="$MAKEFILE $MDIR/Android.mk" else case $DIR in showcommands | snod | dist | *=*) ARGS="$ARGS $DIR";; GET-INSTALL-PATH) GET_INSTALL_PATH=$DIR;; *) if [ -d $DIR ]; then echo "No Android.mk in $DIR."; else echo "Couldn't locate the directory $DIR"; fi return 1;; esac fi done if [ -n "$GET_INSTALL_PATH" ]; then ARGS=${GET_INSTALL_PATHS//\//-} MODULES= MODULES_IN_PATHS= fi if [ "1" = "${WITH_TIDY_ONLY}" -o "true" = "${WITH_TIDY_ONLY}" ]; then MODULES=tidy_only MODULES_IN_PATHS= fi # Convert "/" to "-". MODULES_IN_PATHS=${MODULES_IN_PATHS//\//-} ONE_SHOT_MAKEFILE="$MAKEFILE" _wrap_build $T/build/soong/soong_ui.bash --make-mode $DASH_ARGS $MODULES $MODULES_IN_PATHS $ARGS else echo "Couldn't locate the top of the tree. Try setting TOP." return 1 fi }
使用make构建项目,主要工作是编写Makefile;
类似的,使用soong构建项目,主要工作是编写bp文件。关于bp文件我们会在接下来的第5节中详细介绍。
4.2 androidmk
soong中还集成了一个非常有用的工具androidmk。androidmk可以将android.mk转换成android.bp。我们可以使用androidmk来转换代码中已有的android.mk,以此来减少重写android.bp的工作量。例如,我们将一个Android.mk转换成Android.bp:
androidmk Android.mk > Android.bp
注意:androidmk工具可以转换变量,模块,注释和某些条件,但是自定义的Makefile规则,复杂的条件语句或其它的额外的include语句,必须手动转换。
在现有版本,由于kati工具的存在,继续使用android.mk也是没有问题的。但是,也许在将来的某一天,Google可能会不再支持Makefile。因此,建议新增的模块采用android.bp,存量的android.mk,在条件允许的情况下,也应当尽量转换成android.bp。
4.3 bpfmt
为了方便格式化,Soong还包含了一个用于格式化Android.bp文件的工具bpfmt,类似于gofmt。 以一个简单的bp文件为例:
cc_binary { name: "gzip", srcs: [ "src/test/minigzip.c", "src/test/utils.c", ], shared_libs: ["libz"], stl: "none", }
Android.bp的规范格式包括:
- 4个空格缩进。
- 多元素列表的每个元素后的换行符。
- 在lists和maps的结尾处始终包含一个逗号。
我们可以使用bpfmt工具来规范格式化Android.bp文件。例如,要递归地格式化当前目录中的所有Android.bp文件:
bpfmt -w .
4.4 ninja,Blueprint,kati,androidmk与Soong的关系和作用
- kati可以将Android.mk文件转换成ninja文件。
- androidmk可以将Android.mk文件转换成Android.bp文件
- Blueprint可以将Android.bp文件转换成ninja文件。
- Blueprint,kati,androidmk由Soong调用和协调,一起合作完成android源码的构建。
它们的关系示意图如下:
5. Android.bp
5.1 bp文件的命名与文件格式
soong的编译配置文件以.bp结尾,通常命名为Android.bp,但也有少数情况不以Android.bp命名。例如:frameworks/rs/support.bp。与Makefile一样,使用soong编译前,会遍历所有以bp为后缀名的文件。因此,soong的编译配置文件只要以.bp结尾即可。
5.2 模块(module)
bp文件中的模块(module) 以模块类型(module type)开头,后面跟着一系列的属性(property)。每个模块都必须具有一个属性名为name的属性,并且name的属性值在所有Android.bp文件中必须是唯一的。bp文件的内容与JSON、Bazel BUILD很像,模块的格式为:
[module type] { name: "[name value]", [property1 name]:"[property1 value]", [property2 name]:"[property2 value]", }
一个简单的bp文件:
cc_binary { name: "gzip", srcs: ["src/test/minigzip.c"], shared_libs: ["libz"], stl: "none", } 常见的模块类型有: cc_library, cc_library_headers, cc_library_shared, cc_library_static, android_app, android_app_certificate, java_library, java_library_static, java_sdk_library等等。
soong预置了一系列的模块类型和属性,芯片原厂也会添加一些自定义的模块类型和属性,例如,MTK平台就定义了一些以mtk开头的模块类型。如果想了解更多,已有源码并且已编译的可以查看:out/soong/docs/soong_build.html。如果没有源码,也可查看AOSP的在线文档:$AOSP/out/soong/docs/soong_build.html
5.2.1 默认模块(defaults)
soong提供了一系列xx_defaults模块类型,例如:cc_defaults, java_defaults, doc_defaults, stub_defaults等等。模块类型为xx_defaults的模块提供了一组可由其它模块继承的属性。其它模块可以通过添加属性`defaults:[“ <:default_module_name>”]“来指定继承xx_defaults类型的模块定义的属性。因此,我们定义一个新模块时,可以通过将默认模块的属性放在name属性之后,其它属性之前,来合并两个模块的属性。
例如:
java_defaults { name: "framework-defaults", installable: true, srcs: [ // From build/make/core/pathmap.mk FRAMEWORK_BASE_SUBDIRS "core/java/**/*.java", "graphics/java/**/*.java", "location/java/**/*.java", "lowpan/java/**/*.java", "media/java/**/*.java", ] //......省略无关 } java_library { name: "framework", defaults: ["framework-defaults"], javac_shard_size: 150, } java_library { name: "framework-annotation-proc", defaults: ["framework-defaults"], // Use UsedByApps annotation processor plugins: ["unsupportedappusage-annotation-processor"], }
这个例子中,模块aidl_mapping在srcs属性中引用了framework-defaults。
5.2.2 引用模块
一个模块可以通过它的模块名来引用。在默认模块这一小节中,其实我们已经引用过了其它模块了。这里我们再来一个例子:
cc_binary { name: "app", shared_libs: ["libfoo"], }
例子中,libfoo模块必须存在才有效,并且在构建树中有且仅有一个libfoo模块时才有效。
5.3 文件列表
一个属性的属性值为一系列文件时,属性值也可以采用全局匹配模式和输出路径扩展。
全局匹配模式可以包含普通的Unix通配符*,例如“ * .java”。
全局匹配模式也可以包含单个**通配符作为路径元素,它将匹配零个或多个路径元素。 例如,java / ** / *。java将匹配 java/Main.java 和 java/com/android/Main.java。
输出路径扩展采用:module或:module {.tag}格式,其中module是生成输出文件的模块的名称,并且扩展为这些输出文件的列表。 使用可选的{.tag}后缀,模块可以根据tag生成不同的输出列表。
例如:
// AIDL interface between storaged and framework.jar filegroup { name: "storaged_aidl", srcs: [ "binder/android/os/IStoraged.aidl", ], path: "binder", } java_library_static { name: "services.core.unboosted", srcs: [ "java/**/*.java", ":storaged_aidl", ], //......省略无关 }
5.4 类型和变量
Android.bp文件中,变量和属性是强类型的,变量是基于首次分配动态地创建的,属性是根据模块类型静态地创建的。 支持的类型有:
Bool (true or false) Integers (int) Strings ("string") Lists of strings (["string1", "string2"]) Maps ({key1: "value1", key2: ["value2"]})
maps可以是任何类型的值,包括嵌套maps。 Lists和Map的最后一个值后面可能带有逗号。
字符串可以使用\“包含双引号,例如” cat \“ a b \”“。
5.5 注释
Android.bp文件可以和C/C++的注释类似,可以包含多行注释和单行注释。
多行注释:/ * 注释 * /
单行注释://注释。
5.6 运算符
Android.bp文件可以使用+运算符附加字符串,字符串列表和映射。 整数可以使用+运算符求和。 追加映射会在两个映射中生成键的并集,并追加两个映射中都存在的任何键的值。
例如:
cc_test { name: "libandroidfw_tests", host_supported: true, defaults: ["libandroidfw_defaults"], cppflags: [ // This is to suppress warnings/errors from gtest "-Wno-unnamed-type-template-args", ], srcs: [ // Helpers/infra for testing. "tests/CommonHelpers.cpp", "tests/TestHelpers.cpp", "tests/TestMain.cpp", // 省略无关部分 ], static_libs: ["libgmock"], target: { android: { srcs: [ "tests/BackupData_test.cpp", "tests/ObbFile_test.cpp", ], shared_libs: common_test_libs + ["libui"], }, host: { static_libs: common_test_libs + ["liblog", "libz"], }, }, data: ["tests/data/**/*.apk"], }
注意:
除了上述介绍的内容之外,Soong还包含了包、命名空间、名称解析、可见性、soong配置变量等等内容。但是,我在Android 9.0和Android 10.0两份源码中都没有找到合适的例子,本人也不是很理解,为避免误导他人,这些内容就不介绍了,如果想了解更多,请参考:https://android.googlesource.com/platform/build/soong/
6. Android.mk和Android.bp的区别
Android.mk文件通常可以包含多个同名模块(例如,用于库的静态(static)和共享(shared)版本,用于不同主机(host)的版本,用于不同设备(device)版本)。
Android.bp文件的每个模块都需要唯一的名称,但是单个模块可以构建为多个变体。例如,通过添加host_supported:true。 包含多个同名模块的Android.mk被androidmk转换成Android.bp之后,将生成多个冲突的模块,这些模块必须手动处理,将同名模块改为一个具有多个变体的模块,这些在target:{android:{},host:{}}块内的变体可以有区别,例如,引用不同的源文件,不同架构的共享库文件等等。
Soong故意不支持Android.bp文件中的大多数条件。 Google建议从构建中删除大多数条件。Soong将本地支持的大多数条件将转换为map属性。 构建模块时,将选择map中的属性之一,并且将其值追加到模块顶层的同名属性中。
例如,要支持特定于体系结构的文件:
cc_library { ... srcs: ["generic.cpp"], arch: { arm: { srcs: ["arm.cpp"], }, x86: { srcs: ["x86.cpp"], }, }, }
为arm平台构建时,将构建generic.cpp和arm.cpp。 在为x86平台构建时,将构建generic.cpp和x86.cpp。
7.Android.bp实战
7.1 编译APP
以DocumentsUI为例(为看起来清晰,有整理和部分删减):
android_app { name: "DocumentsUI", manifest: "AndroidManifest.xml", srcs: [ "src/**/*.java", ], resource_dirs: [ "res", ], static_libs: [ "androidx.appcompat_appcompat", "androidx.recyclerview_recyclerview", ], privileged: true, certificate: "platform", optimize: { proguard_flags_files: ["proguard.flags"], }, sdk_version: "system_current", min_sdk_version: "28", target_sdk_version: "28", required: ["privapp_whitelist_com.android.documentsui"], }
可以看到,Android.bp非常简洁,而且命名很清晰,基本上看属性名就知道是起什么作用的。这里只列出了android_app的常用属性,并没有将它的所有属性列出来,开发过程中如果需要配置APP的其它属性,请参考:out/soong/docs/soong_build.html。如果没有源码,也可查看AOSP的在线文档:$AOSP/out/soong/docs/soong_build.html
7.2 编译java库
java_library { name: "updatable-media", srcs: [ ":updatable-media-srcs", ], aidl: { export_include_dirs: [ "apex/java", ], // It would be great if we don't need to add include_dirs for public // parcelable classes. Find a better way. include_dirs: [ // To refer: // android.os.Bundle // android.os.ResultReceiver "frameworks/base/core/java", ], }, permitted_packages: [ "android.media", ], installable: true, // Make sure that the implementaion only relies on SDK or system APIs. no_framework_libs: true, libs: [ // The order matters. android_system_* library should come later. "framework_media_annotation", "android_system_stubs_current", ], }
7.3 编译java静态库
java_library_static { name: "services.core.unboosted", aidl: { include_dirs: [ "frameworks/native/aidl/binder", "system/core/storaged/binder", "system/netd/server/binder", "system/vold/binder", ], }, srcs: [ "java/**/*.java", ":netd_aidl", ":netd_metrics_aidl", ":installd_aidl", ":storaged_aidl", ":vold_aidl", ":mediaupdateservice_aidl", "java/com/android/server/EventLogTags.logtags", "java/com/android/server/am/EventLogTags.logtags", ], libs: [ "services.net", "android.hardware.light-V2.0-java", "android.hardware.power-V1.0-java", "android.hardware.tv.cec-V1.0-java", "android.hidl.manager-V1.0-java", ], static_libs: [ "time_zone_distro", "time_zone_distro_installer", "android.hardware.authsecret-V1.0-java", "android.hardware.broadcastradio-V2.0-java", "android.hardware.health-V1.0-java", "android.hardware.health-V2.0-java", "android.hardware.weaver-V1.0-java", "android.hardware.biometrics.fingerprint-V2.1-java", "android.hardware.oemlock-V1.0-java", "android.hardware.tetheroffload.control-V1.0-java", "android.hardware.vibrator-V1.0-java", "android.hardware.configstore-V1.0-java", "android.hardware.contexthub-V1.0-java", ], }
7.4 编译共享库(shared)
cc_library_shared { name: "libbluetooth_jni", compile_multilib: "first", srcs: [ "bluetooth_socket_manager.cc", "com_android_bluetooth_btservice_AdapterService.cpp", "com_android_bluetooth_hfp.cpp", "com_android_bluetooth_hfpclient.cpp", "com_android_bluetooth_a2dp.cpp", "com_android_bluetooth_a2dp_sink.cpp", "com_android_bluetooth_avrcp_controller.cpp", "com_android_bluetooth_avrcp_target.cpp", "com_android_bluetooth_hid_host.cpp", "com_android_bluetooth_hid_device.cpp", "com_android_bluetooth_hearing_aid.cpp", "com_android_bluetooth_pan.cpp", "com_android_bluetooth_gatt.cpp", "com_android_bluetooth_sdp.cpp", "IUserManager.cc", "permission_helpers.cc", ], header_libs: ["libbluetooth_headers"], include_dirs: [ "libnativehelper/include/nativehelper", "system/bt/types", ], shared_libs: [ "libandroid_runtime", "libbinder", "libbluetooth-binder", "libchrome", "libnativehelper", "liblog", "libutils", ], static_libs: [ "libbluetooth-types", "libcutils", ], cflags: [ "-Wall", "-Werror", "-Wextra", "-Wno-unused-parameter", ], sanitize: { scs: true, }, }
7.5 编译静态库(static)
cc_library_static { name: "libmedia_player2_util", defaults: [ "libmedia_defaults" ], srcs: [ "AudioParameter.cpp", "BufferingSettings.cpp", "DataSourceDesc.cpp", "MediaCodecBuffer.cpp", "Metadata.cpp", "NdkWrapper.cpp", ], shared_libs: [ "libbinder", "libcutils", "liblog", "libmediandk", "libnativewindow", "libmediandk_utils", "libstagefright_foundation", "libui", "libutils", ], export_shared_lib_headers: [ "libbinder", "libmediandk", ], header_libs: [ "media_plugin_headers", ], include_dirs: [ "frameworks/av/media/ndk", ], static_libs: [ "libstagefright_rtsp", "libstagefright_timedtext", ], export_include_dirs: [ "include", ], cflags: [ "-Werror", "-Wno-error=deprecated-declarations", "-Wall", ], sanitize: { misc_undefined: [ "unsigned-integer-overflow", "signed-integer-overflow", ], cfi: true, }, }
8. 结语
Google在Android N就引入了Soong,目的是替换make。但是,很遗憾的是截止到Android Q,甚至Android R,它们的源码中都还存在大量的Makefile。也许make就是构建语言里的C语言,无论世界如何发展变化,它的生命力依然旺盛。未来的很长一段时间内Android.bp和Android.mk可能会一直共存,一起完成Android系统源码的构建。这并不代表我们可以不用去学习Android.bp,技术总是不断的发展,我们则需要不断的学习才能适应技术的发展。
9.本文参考
https://android.googlesource.com/platform/build/soong/
https://android.googlesource.com/platform/build/soong/+/HEAD/docs/best_practices.md#network-access
作者:qiuxintai
链接:https://www.jianshu.com/p/f69d1c381182
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。