一、需求
Android的源码非常的庞大,编译Android系统往往会占用我们很长的时间,我们需要了解下Android的编译规则,以期能提高我们的开发效率,达到程序员按时下班的伟大理想。
二、环境
- 平台:QCM2290
- 版本:Android 11
- 编译服务器: 64G + 32核
三、相关概念
3.1 Treble架构
由于Android各个层级之间的耦合性大,Android系统更新成本高,导致Android系统版本虽然已经释放了很久,但是市面上Android的系统依然存在滞后、碎片化的情况。Android 8.0 重新设计了 Android 操作系统框架(名为“Treble”的项目),以便让制造商能够以更低的成本更轻松、更快速地将设备更新到新版 Android 系统。
Android 7.x 及更早版本中没有正式的供应商接口。当设备制造上升级Android系统时,需要移植大量的代码。因为Framework与供应商代码打包在一个镜像中,所以必须整体升级。
Android 8.0 及更高版本提供了一个稳定的新供应商接口。设备制造商访问的是 Android 代码中特定于硬件的部分,而不会依赖供应商的私有接口。并且供应商会使用独立的vendor分区。这样,设备制造商在更新 Android 操作系统框架,只需要单独升级system分区。同时稳定的供应商接口会保证兼容性。
Treble的目标是将供应商实现(大部分有芯片制造商编写特定设备软件和底层软件)与Android操作系统框架分隔开来,这通过供应商接口来实现。为了确保供应商实现的向前兼容性,供应商接口会由供应商测试套件 (VTS) 进行验证,该套件类似于兼容性测试套件 (CTS)。您可以使用 VTS 在旧版 Android 架构和当前 Android 架构中自动执行 HAL 和操作系统内核测试。
3.2 Soong
Soong 构建系统是在 Android 7.0 (Nougat) 中引入的,旨在取代 Make。它利用 Kati GNU Make 克隆工具和 Ninja 构建系统组件来加速 Android 的构建。
Soong是由Go语言写的一个项目,从Android 7.0开始,在prebuilts/go/目录下新增了Go语言所需的运行环境,Soong在编译时使用,解析Android.bp,将之转化为Ninja文件,完成Android的选择编译,解析配置工作等。故Soong相当于Makefile编译系统的核心,即build/make/core下面的内容。
3.3 Blueprint
Blueprint由Go语言编写,是生成、解析Android.bp的工具,是Soong的一部分。Soong则是专为Android编译而设计的工具,Blueprint只是解析文件的形式,而Soong则解释内容的含义。
3.4 KATI
kati是Google专门为了Android而开发的一个小项目,基于Golang和C++。目的是为了把Android中的Makefile,转换成Ninja文件。
在最新的Android R(11)中,Google已经移除了/build/kati目录,只保留了一个预先编译出来的可执行文件:prebuilts/build-tools/linux-x86/bin/ckati,这意味着Google在逐渐从编译系统中移除kati,预计1-2个Android大版本,.mk文件全部都切换成.bp文件后,kati将会正式退出Android历史舞台。
kati是go语言写的,而ckati是c++写的。kati官方文档对它的描述是:kati is an experimental GNU make clone。也就是说,kati是对等make命令的。只不过kati并不执行具体的编译工作,而是生成ninja文件。kati刚开始是使用Golang编写的,但是后来验证下来发现编译速度不行,于是改成C++编写,所以现在存在两个版本:kati、ckati。
3.5 Ninja
Ninja 是Google的一名程序员推出的注重速度的构建工具。一般在Unix/Linux上的程序通过make/makefile来构建编译,而Ninja通过将编译任务并行组织,大大提高了构建速度。
Ninja是一个致力于速度的小型编译系统(类似于Make),如果把其他编译系统比做高级语言的话,Ninja就是汇编语言。通常使用Kati或soong把makefile转换成Ninja files,然后用Ninja编译。
ninja核心是由C/C++编写的,同时有一部分辅助功能由python和shell实现。由于其开源性,所以可以利用ninja的开源代码进行各种个性化的编译定制。
3.6 Makefile
Makefile是一个文本文件,是GNU make程序在执行的时候默认读取的配置文件。其关系到了整个工程的编译规则。一个工程中的源文件按类型、功能、模块分别放在若干个目录中,makefile定义了一系列规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。
其好处在于:写好makefile之后,只需要一个“make”命令,整个工程就能完全自动编译,极大地提高了软件开发的效率。
四、高通编译
我司高通项目的编译,有一个痛点,就是编译的时间太久,即便是remake一个工程都要占用小半天的时间,不仅自身调试效率低,还频繁占用服务器编译资源。故我们有必要了解下,高通的编译脚本的逻辑。
4.1 编译指令
以下为我司编译常用指令:
平台 | 编译指令 | 备注 |
---|---|---|
QCM2290 | ./build.sh dist -j32 | 全编译,并打包 |
QCM2290 | source build/envsetup.sh lunch qssi-userdebug ./build.sh dist -j32 --qssi_only | 单独编译,qssi模块 |
QCM2290 | source build/envsetup.sh lunch bengal-userdebug ./build.sh dist -j32 --target_only | 单独编译,target模块 |
QCM2290 | source build/envsetup.sh lunch bengal-userdebug ./build.sh dist -j32 --merge_only | 打包操作,打包ota zip and super.img |
QCM2290 | source build/envsetup.sh lunch bengal-userdebug ./build.sh -j32 --merge_only | 打包操作,打包super.img |
4.2 编译脚本
(1)qssi模块,qcom single system image,就类似system分区,编译生成路径:out\target\product\qssi;
(2)target模块,就类似vendor分区,编译生成路径:out\target\product\bengal;
(3)AndroidQ以前只用lunch自己项目的target就可以进行编译了,现在随着Treble架构的强制推行,system和vendor要强制解耦了。
@android/build.sh
function build_qssi_only () {//编译qssi
command "source build/envsetup.sh"
command "$QTI_BUILDTOOLS_DIR/build/kheaders-dep-scanner.sh"
command "lunch ${TARGET_QSSI}-${TARGET_BUILD_VARIANT}"
command "make $QSSI_ARGS"
}
function build_target_only () {//编译target
command "source build/envsetup.sh"
command "$QTI_BUILDTOOLS_DIR/build/kheaders-dep-scanner.sh"
command "lunch ${TARGET}-${TARGET_BUILD_VARIANT}"
QSSI_ARGS="$QSSI_ARGS SKIP_ABI_CHECKS=$SKIP_ABI_CHECKS"
command "make $QSSI_ARGS"
command "run_qiifa"
}
function merge_only () {//打包
# DIST/OTA specific operations:
if [ "$DIST_ENABLED" = true ]; then
generate_ota_zip
fi
# Handle dynamic partition case and generate images
if [ "$BOARD_DYNAMIC_PARTITION_ENABLE" = true ]; then
generate_dynamic_partition_images
fi
}
function full_build () {//全编译
build_qssi_only
build_target_only
# Copy Qssi system|product.img to target folder so that all images can be picked up from one folder
command "cp $QSSI_OUT/system.img $OUT/"
if [ -f $QSSI_OUT/product.img ]; then
command "cp $QSSI_OUT/product.img $OUT/"
fi
if [ -f $QSSI_OUT/system_ext.img ]; then
command "cp $QSSI_OUT/system_ext.img $OUT/"
fi
merge_only
}
ps:编译时,根据需要,编译对应的target模块或者qssi模块,尽量避免全编。
五、Android-Make指令
从高通的编译脚本来看,其最终是调用make指令,来实现Android的编译。那么问题来了,这个make指令实现在哪里呢?具体做了什么事情呢?
5.1 Android编译指令
Android项目的编译,不同的ODM厂商会根据自身的编译规则,定制不同的编译脚本、打包脚本,但是归根到底,都是基于如下命令进行客制化:
source build/envsetup.sh //step 1.初始化编译环境
lunch xxx //step 2.选择编译目标
make -j8 //step 3.执行编译
make snod //step 4.打包生成镜像
5.2 初始化编译环境
envsetup.sh脚本:主要是定义了make、mm、lunch、cgrep等相关函数,为Android系统的编译提供支持。
@LINUX\android\build\envsetup.sh
...
function mmm()
(
call_hook ${FUNCNAME[0]} $@
if [ $? -ne 0 ]; then
return 1
fi
_trigger_build "modules-in-dirs-no-deps" "$@"
)
...
function make()
{
_wrap_build $(get_make_command "$@") "$@"
}
...
常用指令:
5.3 选择编译目标
lunch命令:主要作用是根据用户输入或者选择的产品名来设置与具体产品相关的环境变量。
lunch 24后,生成的编译环境变量信息:
5.4 执行编译
make命令:主要用来编译Android系统,生成对应的编译文件。其中,Android10及之后,通过soong构建系统执行编译。
5.4.1 编译流程图
5.4.2 shell脚本部分
step 1. make指令入口
@LINUX\android\build\envsetup.sh
function make()
{
_wrap_build $(get_make_command "$@") "$@"
}
step 2. 获取构建方式(以前通过make构建,后续更换成soong方式),通过判断soong_ui.bash文件是否存在,来决定系统构建方式。
@LINUX\android\build\envsetup.sh
function get_make_command()
{
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
}
step 3. 执行构建指令,并打印构建时间、构建结果
@LINUX\android\build\envsetup.sh
function _wrap_build()
{
...
local start_time=$(date +"%s")
"$@" //执行构建指令
local ret=$?
local end_time=$(date +"%s")
local tdiff=$(($end_time-$start_time))
local hours=$(($tdiff / 3600 ))
local mins=$((($tdiff % 3600) / 60))
local secs=$(($tdiff % 60))
local ncolors=$(tput colors 2>/dev/null)
...
echo
if [ $ret -eq 0 ] ; then
echo -n "${color_success}#### build completed successfully " //打印构建成功结果
else
echo -n "${color_failed}#### failed to build some targets " //打印构建失败结果
fi
if [ $hours -gt 0 ] ; then
printf "(%02g:%02g:%02g (hh:mm:ss))" $hours $mins $secs //打印构建时间
...
return $ret
}
构建指令如下:
高通平台指令:
build/soong/soong_ui.bash --make-mode dist -j32 ENABLE_AB=true SYSTEMEXT_SEPARATE_PARTITION_ENABLE=true BOARD_DYNAMIC_PARTITION_ENABLE=true ENABLE_VIRTUAL_AB=false SHIPPING_API_LEVEL=29
5.4.3 goLang脚本部分
5.4.3.1 执行soong_ui.bash脚本
soong_ui.bash脚本主要做了两件事:
- 根据"android/soong/cmd/soong_ui/"内容,生成soong_ui的go可执行程序,生成路径:out\soong_ui
- 执行soong_ui程序,进入soong_ui世界
@build\soong\soong_ui.bash
...
# Save the current PWD for use in soong_ui
export ORIGINAL_PWD=${PWD}
export TOP=$(gettop)
source ${TOP}/build/soong/scripts/microfactory.bash
soong_build_go soong_ui android/soong/cmd/soong_ui//构建soong_ui执行程序
cd ${TOP}
exec "$(getoutdir)/soong_ui" "$@"//执行soong_ui程序,启动构建
5.4.3.2 soong入口
soong_ui是个go程序,至此进入soong构建系统的世界。
@android\build\soong\cmd\soong_ui\main.go
func main() {
...
c, args := getCommand(os.Args)
...
f := build.NewSourceFinder(buildCtx, config)
defer f.Shutdown()
build.FindSources(buildCtx, config, f)//遍历整个项目,记录所有的mk、bp等文件
c.run(buildCtx, config, args, logsDir)//启动构建
}
5.4.3.3 soong构建系统
soong构建系统最核心的步骤。其主要通过将bp、mk文件,解析成ninja文件,再通过ninja去实现系统构建任务。
@android\build\soong\ui\build\build.go
func Build(ctx Context, config Config, what int) {
...
runSoong(ctx, config)//step 1.处理bp文件
...
runKatiBuild(ctx, config)//step 2.处理mk文件
...
createCombinedBuildNinjaFile(ctx, config)//step 3.整合ninja文件
...
runNinja(ctx, config)//step 4.构建
...
}
step 1. runSoong
runSoong 对工具进行编译,先编译出blueprint等编译工具, 再把*.bp 编译成 out/soong/build.ninja。
@android\build\soong\ui\build\soong.go
func runSoong(ctx Context, config Config) {
...
ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")//1.主要用于生成out/soong/.minibootstrap目录相关文件
...
ctx.BeginTrace(metrics.RunSoong, "environment check")//2.初始环境检查
...
ctx.BeginTrace(metrics.RunSoong, "minibp")//3.创建minibp可执行程序
...
ctx.BeginTrace(metrics.RunSoong, "bpglob")//4.创建bpglob可执行程序
...
ninja("minibootstrap", ".minibootstrap/build.ninja")//5.主要用于生成out/soong/.bootstrap/build.ninja文件
ninja("bootstrap", ".bootstrap/build.ninja")//6.生成out/soong/build.ninja
}
(1)创建 out/soong/.minibootstrap/目录并在这个目录下创建一系列文件,包括out/soong/.minibootstrap/build.ninja这个文件。该路径下的内容,会参与到bootstrap阶段的构建;
(2)检查soong构建的环境、工具是否存在&正常;
(3)通过microfactory生成out/soong/.minibootstrap/minibp可执行程序,会参与到bootstrap阶段的构建;
(4)通过microfactory生成out/soong/.minibootstrap/bpglob可执行程序,会参与到bootstrap阶段的构建;
(5)通过步骤三编译生成的minibp程序,生成out/soong/.bootstrap/build.ninja文件,该文件会参与到bootstrap阶段的构建,可参考verbose.log打印的日志:
@android\out\verbose.log
[1/1] out/soong/.minibootstrap/minibp -t -l out/.module_paths/Android.bp.list -b out/soong -n out -d out/soong/.bootstrap/build.ninja.d -globFile out/soong/.minibootstrap/build-globs.ninja -o out/soong/.bootstrap/build.ninja Android.bp
(6)bootstrap表示从无到有创建Soong,该阶段会先生成bootstrap相关的工具程序:./out/soong/.bootstrap/bin/*,再使用编译生成的soong_build程序,生成out/soong/build.ninja文件。用于后续参与Ninja编译构建工作,可参考verbose.log打印的日志:
@android\out\verbose.log
[2/2] out/soong/.bootstrap/bin/soong_build -t -l out/.module_paths/Android.bp.list -b out/soong -n out -d out/soong/build.ninja.d -globFile out/soong/.bootstrap/build-globs.ninja -o out/soong/build.ninja Android.bp
out/soong/build.ninja文件罗列了项目上所有的bp模块编译规则,及其相关依赖模块、SDK、签名信息、临时文件等(文件很大,约3.53G,慎重打开)。如下为该文件部分内容:
(7)该阶段在编译时,控制台打印的log如下:
step 2. runKatiBuild
runKatiBuild, 加载 build/make/core/main.mk, 搜集所有的Android.mk文件生成out/build-xxx.ninja文件
@android\build\soong\ui\build\kati.go
func runKatiBuild(ctx Context, config Config) {
ctx.BeginTrace(metrics.RunKati, "kati build")
...
args := []string{
"--writable", config.OutDir() + "/",
"-f", "build/make/core/main.mk",
}
...
runKati(ctx, config, katiBuildSuffix, args, func(env *Environment) {})//执行ckati指令,构建mk
...
}
(1)参考soong.log的日志,runKati函数最终会引用cKati指令,加载main.mk文件,生成ninja文件,其指令如下:
@android\out\soong.log
2023/09/08 10:47:50.479597 build/soong/ui/build/exec.go:60: prebuilts/build-tools/linux-x86/bin/ckati [prebuilts/build-tools/linux-x86/bin/ckati --ninja --ninja_dir=out --ninja_suffix=-qssi --no_ninja_prelude --regen --ignore_optional_include=out/%.P --detect_android_echo --color_warnings --gen_all_targets --use_find_emulator --werror_find_emulator --no_builtin_rules --werror_suffix_rules --warn_real_to_phony --warn_phony_looks_real --werror_real_to_phony --werror_phony_looks_real --werror_writable --top_level_phony --kati_stats --writable out/
-f build/make/core/main.mk --werror_implicit_rules SOONG_MAKEVARS_MK=out/soong/make_vars-qssi.mk SOONG_ANDROID_MK=out/soong/Android-qssi.mk TARGET_DEVICE_DIR=device/qcom/qssi KATI_PACKAGE_MK_DIR=out/target/product/qssi/obj/CONFIG/kati_packaging]
(2)build/make/core/main.mk又是何方神圣?从main.mk开始,将通过include命令将其所有需要的.mk文件包含进来,最终在内存中形成一个包括所有编译脚本的集合,这个相当于一个巨大Makefile文件。网上有个图,可以很好的描述其关系,如下:
文件 | 说明 |
---|---|
build/make/core/main.mk | Build的主控文件,主要作用是包含其他mk,以及定义几个最重要的编译目标,同时检查编译工具的版本,例如gcc、clang、java等 |
build/make/core/config.mk | Build的配置文件,主要是区分各个产品的配置,并将这些编译器参数引入产品配置 BoardConfig.mk,同时也配置了一些编译器的路径等 |
build/make/core/clang/config.mk | clang编译的配置文件 |
build/make/core/definitions.mk | 最重要的 Make 文件之一,在其中定义了大量的函数。这些函数都是 Build 系统的其他文件将用到的。例如:my-dir,all-subdir-makefiles,find-subdir-files,sign-package 等,关于这些函数的说明请参见每个函数的代码注释。 |
build/make/core/dex_preopt.mk | 定义了dex优化相关的路径和参数 |
build/make/core/pdk_config.mk | 编译pdk的配置文件 |
build/make/core/Makefile | 系统最终编译完成所需要的各种目标和规则 |
build/make/core/envsetup.mk | 包含进product_config.mk文件并且根据其内容设置编译产品所需要的环境变量,并检查合法性,指定输出路径等 |
build/make/core/combo/select.mk | 根据当前编译器的平台选择平台相关的 Make 文件 |
build/make/core/ninja_config.mk | 解析makefile的的列表,传给kati,配置传给ninja和kati的目标 |
build/make/core/soong_config.mk | 配置soong的环境变量,建立go变量和mk变量的json映射关系,让go变量可以获取到mk中定义的变量值 |
(3)如上关系图所示,mk文件索引的几乎都是config.mk之类的配置文件,那我们编译模块对应的Android.mk又在哪个位置被引入呢?在5.4.3.2节,在soong启动时,我们便会去搜索项目中所有的Android.mk文件,并记录于out/.module_paths/Android.mk.list文件。在main.mk里面,便可以根据这个文件,将所有的内容include进来。因此,在该项目下定义的任一Android.mk都可以被引用。
@android/build/make/core/main.mk
...
subdir_makefiles := $(SOONG_ANDROID_MK) $(file <$(OUT_DIR)/.module_paths/Android.mk.list) $(SOONG_OUT_DIR)/late-$(TARGET_PRODUCT).mk
subdir_makefiles_total := $(words int $(subdir_makefiles) post finish)
.KATI_READONLY := subdir_makefiles_total
//遍历相关mk文件
$(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] including $(mk) ...)$(eval include $(mk)))
...
(4)main.mk加载完成后,最终生成out/build-xxx.ninja文件,用于后续参与Ninja编译构建工作。out/build-xxx.ninja文件罗列了项目上所有的mk模块编译规则,及其相关依赖模块、SDK、签名信息、临时文件等(qssi模块和target模块,约1G+1.08G,文件较大,慎重打开)。
(5)该阶段在编译时,控制台打印的log如下:
step 3. createCombinedBuildNinjaFile
为了方便统一管理,Soong将out/soong/build.ninja文件 、out/build-*.ninja文件和out/build-*-package.ninja文件, 合成为out/combined-*.ninja文件,由该文件记录所有待执行ninja文件。
@android\build\soong\ui\build\build.go
var combinedBuildNinjaTemplate = template.Must(template.New("combined").Parse(`
builddir = {{.OutDir}}
{{if .UseRemoteBuild }}pool local_pool
depth = {{.Parallel}}
{{end -}}
pool highmem_pool
depth = {{.HighmemParallel}}
build _kati_always_build_: phony
{{if .HasKatiSuffix}}subninja {{.KatiBuildNinjaFile}}//追加文件out/build-*.ninja
subninja {{.KatiPackageNinjaFile}}//追加文件out/build-*-package.ninja
{{end -}}
subninja {{.SoongNinjaFile}}//追加文件out/soong/build.ninja
`))
func createCombinedBuildNinjaFile(ctx Context, config Config) {
...
file, err := os.Create(config.CombinedNinjaFile())//创建combined-*.ninja文件
...
if err := combinedBuildNinjaTemplate.Execute(file, config); //执行合并动作
...
}
out/combined-qssi.ninja文件,如下:
step 4. runNinja
runNinja,运行Ninja命令, 解析combined-*.ninja,执行编译过程
@android\build\soong\ui\build\ninja.go
func runNinja(ctx Context, config Config) {
...
executable := config.PrebuiltBuildTool("ninja")//获取ninja指令
args := []string{
"-d", "keepdepfile",
"-d", "keeprsp",
"--frontend_file", fifo,
}
...
args = append(args, "-f", config.CombinedNinjaFile())//配置组合的ninja文件
args = append(args,
"-w", "dupbuild=err",
"-w", "missingdepfile=err"
cmd := Command(ctx, config, "ninja", executable, args...)//初始化ninja指令参数
...
ctx.Status.Status("Starting ninja...")
cmd.RunAndStreamOrFatal()//执行ninja指令
}
(1)参考soong.log的日志,runNinja函数最终会引用ninja指令,加载out/combined-*.ninja文件,执行最终的编译,其指令如下:
@android\out\soong.log
2023/09/12 14:13:05.709769 build/soong/ui/build/exec.go:60: prebuilts/build-tools/linux-x86/bin/ninja [prebuilts/build-tools/linux-x86/bin/ninja -d keepdepfile -d keeprsp --frontend_file out/.ninja_fifo droid -j 34 -f out/combined-bengal.ninja -w dupbuild=err -w missingdepfile=err]
(2)该阶段在编译时,控制台打印的log如下:
5.4.3.4 soong编译创建文件
文件 | 备注 |
---|---|
android/out/soong.log | soong模块打印内容 |
android/out/verbose.log | 控制台编译日志 |
android/out/dumpvars-verbose.log | lunch的log信息 |
android/out/.ninja_log | ninja模块编译log |
android/out/soong_ui | go可执行程序,执行soong编译 |
android/out/.module_paths/ | 遍历整个项目,记录所有的mk、bp等文件 |
android/out/soong/build.ninja | 项目上所有bp模块的编译规则 |
android/out/build-*.ninja | 项目上所有mk模块的编译规则 |
android/out/combined-*.ninja | 项目上所有模块的编译规则组合 |
android/out/soong/host/linux-x86/bin/androidmk | mk文件转bp文件的指令 |
六、编译小窍门
6.1 模块编译速度优化
随着Android版本的更迭,尤其是Androd10和Android11以上源码的编译,单编模块的时间也特别慢,每次都需要小半个小时甚至更长,因为每次单编都会重新加载所有mk文件,再生成ninja编译,此过程很慢,实际编译过程很快。
(1)如下指令是AOSP的快编指令,用于快速编译单个模块:
./prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-bengal.ninja SnapdragonCamera -j32
格式 | 描述 |
---|---|
./prebuilts/build-tools/linux-x86/bin/ninja | 指定了要使用的构建工具,即Ninja构建系统。该指令通过在AOSP预构建工具链目录下找到Ninja可执行文件进行调用。 |
out/combined-aosp_arm-eng.ninja | 指定了Ninja构建系统要使用的构建文件的路径和名称。在AOSP编译过程中,生成的构建文件会存储在out目录中,且命名通常包含目标设备的相关信息。 |
SnapdragonCamera | 指定了要构建的目标模块或子模块的名称。 |
-j32 | 指定了并发构建的线程数。-j32表示同时使用32个线程进行构建,以加快构建速度。 |
其他常用的指令,如: |
#编译Settings
./prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-bengal.ninja Settings -j32
#编译selinux
./prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-bengal.ninja selinux_policy -j32
#编译Framework
./prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-bengal.ninja frameworks -j32
#全编译
./prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-bengal.ninja -j32 2>&1 |tee ninja_build.log
(2)ninja与mm指令对比,查看编译速度(工程已经编译过,删除SnapdragonCamera目录,比较编译完成时间)
指令 | 编译时间 (mm:ss) | 编译时间 (mm:ss) | 编译时间 (mm:ss) |
---|---|---|---|
mm SnapdragonCamera | 05:58 | 05:37 | 04:25 |
./prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-bengal.ninja SnapdragonCamera -j32 | 00:05 | 00:04 | 00:04 |
6.2 Android.mk转换为Android.bp
Android.bp的出现就是为了替换Android.mk文件。bp跟mk文件不同,它是纯粹的配置,没有分支、循环等流程控制,不能做算数逻辑运算。如果需要控制逻辑,那么只能通过Go语言编写。旧的mk可以转换为bp,Soong会编译生成一个androidmk命令,用于将Android.mk文件转换为Android.bp文件。
(1)生成androidmk文件
确认out/soong/host/linux-x86/bin/目录下是否存在androidmk文件,如不存在androidmk文件,使用如下命令生成:
source build/envsetup.sh
m -j blueprint_tools
(2)通过androidmk执行转化动作
cd out/soong/host/linux-x86/bin/
androidmk android.mk > android.bp
(3)转换前的android.mk
预置应用到vendor分区
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := TCP_UDP
LOCAL_MODULE_CLASS := APPS
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)
LOCAL_SRC_FILES := app/TCP_UDP.apk
include $(BUILD_PREBUILT)
(4)转换后的android.bp
转化对应的规则,可参考:build/soong/androidmk/androidmk/android.go
android_app_import {
name: "TCP_UDP",
local_module_path: {
var: "TARGET_OUT_VENDOR",
},
apk: "app/TCP_UDP.apk",
presigned: true,
}
七、小结
- Android的Treble架构;
- Soong构建流程;
- Out路径下关于构建生成的临时文件、日志;
- Blueprint、Kati、Ninja模块的作用;
八、参考资料
Treble架构:
https://segmentfault.com/a/1190000021550665?sort=newest
Android-Make指令:
https://blog.csdn.net/yiranfeng/article/details/109084082
https://blog.csdn.net/yiranfeng/article/details/109148537
https://zhuanlan.zhihu.com/p/342303212
https://zhuanlan.zhihu.com/p/342817768
https://blog.csdn.net/m0_37624402/article/details/91409900
编译优化:
https://blog.csdn.net/lontano_0406/article/details/131162119
http://wed.xjx100.cn/news/268761.html?action=onClick
https://blog.csdn.net/weixin_36389889/article/details/128469488