喜好

喜好检索

生活 蔬菜

蔬菜大全

思考

见闻与录

BT技术

BT蓝牙技术

标签

安卓blueprint和soong构建系统 2021年04月12日

    简介

    blueprint背景介绍

    随着android工程越来越大,包含的module越来越多,以makefile组织的项目编译花费的时间越来越多。
    谷歌在7.0开始引入了ninja进行编译系统的组织。相对于make来说Ninja 在大的项目管理中【速度】和【并行】方面有突出的优势,
    因此谷歌采用了Ninja 来取代之前使用的 make。
    
    Android 7.0 之后再很多地方出现了Android.bp的文件,bp = BluePrint缩写,
    本文主要是介绍其由来和简单的语法和其使用方法,以及与其相关的Blueprint 和 Soong。
    
    Android 7.0之后希望用Android.bp替换Android.mk,bp简单的配置更方便Ninja 文件的产生,而Blueprint和Soong 就此产生
    
    Android 利用Blueprint和Soong 来解析bp 文件,经过最终转换为 ninja files
    
    Blueprint和Soong都是由Go语言写的项目   https://golang.org/#      // go 语言在线编辑
    从Android Nougat开始,prebuilts/go/ 目录下新增了Golang所需的运行环境,在编译时使用
    http://androidxref.com/9.0.0_r3/xref/prebuilts/go/
    
    
    kati是Google专门为了Android而开发的一个小项目,基于Golang和C++。 目的是为了把Android中的Makefile,转换成Ninja文件
    
    
    

    blueprint

    /build/blueprint/
    
    http://androidxref.com/9.0.0_r3/xref/build/blueprint/
    
    https://github.com/google/blueprint    // blueprint git开源仓库
    
    Android.bp文件设计得非常简洁。没有条件或控制流语言(任何复杂性都在用Go编写的构建逻辑中被处理)
    
    详细的Android.bp 的描述文档 :    http://note.qidong.name/demo/soong_build/
    
    Android.bp,是用来替换Android.mk的配置文件。 它使用Blueprint框架来解析,最终转换成Ninja文件
    
    与Android.mk不同的是,Android.bp是纯粹的配置文件,不包含分支、循环等流程控制,也不能做算数、逻辑运算。 
    与此同时,Ninja文件也是纯粹的配置文件,不包含分支、循环等流程控制,也不能做算数、逻辑运算。 
    这就产生了一些新的问题与需求——在Android项目上进行选择编译、解析配置、转换成Ninja等——Soong应运而生
    
    
    
    
    READ.MD 
    Blueprint Build System
    ======================
    [![Build Status](https://travis-ci.org/google/blueprint.svg?branch=master)](https://travis-ci.org/google/blueprint) 
    
    Blueprint is a meta-build system that reads in Blueprints files that describe
    modules that need to be built, and produces a
    [Ninja](https://ninja-build.org/) manifest describing the commands that
    need to be run and their dependencies.  Where most build systems use built-in
    rules or a domain-specific language to describe the logic for converting module
    descriptions to build rules, Blueprint delegates this to per-project build
    logic written in Go. 【Blueprint将此委托给每个项目构建 用Go编写的逻辑】
     For large, heterogenous projects this allows the inherent
    complexity of the build logic to be maintained in a high-level language, while
    still allowing simple changes to individual modules by modifying easy to
    understand Blueprints files.
    
    
    
    

    blueprint语法

    Blueprint文件的语法比较简单,毕竟只是配置文件
    
    1.模块    Android.bp文件中的模块以模块类型开头,后跟一组name: value格式的属性:
    cc_binary {
        name: "gzip",
        srcs: ["src/test/minigzip.c"],
        shared_libs: ["libz"],
        stl: "none",
    }
    每个模块必须有name属性,且其值在所有Android.bp文件中必须是唯一的。
    
    2.
    
    
    
    
    
    

    bp模块类型属性列表

    http://note.qidong.name/

    	android_app  【 index = 1 】
    	art_cc_binary
    	art_cc_defaults
    	art_cc_library
    	art_cc_test
    	art_cc_test_library
    	art_debug_defaults
    	art_global_defaults
    	blueprint_go_binary
    	bootstrap_core_go_binary
    	bootstrap_go_binary
    	bootstrap_go_package
    	cc_benchmark
    	cc_benchmark_host
    	cc_binary
    	cc_binary_host
    	cc_defaults
    	cc_library
    	cc_library_headers
    	cc_library_host_shared
    	cc_library_host_static
    	cc_library_shared
    	cc_library_static
    	cc_object
    	cc_prebuilt_binary
    	cc_prebuilt_library_shared
    	cc_prebuilt_library_static
    	cc_test
    	cc_test_host
    	cc_test_library
    	clang_binary_host
    	clang_tblgen
    	filegroup
    	fluoride_defaults
    	force_build_llvm_components_defaults
    	genrule
    	gensrcs
    	hal2vts
    	java_binary
    	java_binary_host
    	java_library
    	java_library_host
    	java_library_static
    	llndk_library
    	llvm_defaults
    	llvm_tblgen
    	ndk_headers
    	ndk_library
    	ndk_prebuilt_library
    	ndk_prebuilt_object
    	ndk_prebuilt_shared_stl
    	ndk_prebuilt_static_stl
    	phony
    	prebuilt_java_library
    	prebuilt_sdk
    	preprocessed_ndk_headers
    	toolchain_library  【 index = 57 】
    
    
    
    

    Soong

    /build/soong/
    
    http://androidxref.com/9.0.0_r3/xref/build/soong/
    
    Soong是以前Android基于make的编译系统的替代品。
    它以Android.bp文件替代Android.mk,Android.bp文件用类似JSON的简洁声明来描述需要构建的模块
    
    
    与Android.mk不同的是,Android.bp是纯粹的配置文件,不包含分支、循环等流程控制,也不能做算数、逻辑运算。 
    与此同时,Ninja文件也是纯粹的配置文件,不包含分支、循环等流程控制,也不能做算数、逻辑运算。 
    这就产生了一些新的问题与需求——在Android项目上进行选择编译、解析配置、转换成Ninja等————Soong应运而生。
    
    Soong  主要用于 选择编译、解析配置  然后 配合 blueprint框架生成  ninja 文件。
    
    Soong 其实就相当于Makefile编译系统的核心,即build/make/core/下面的内容
    http://androidxref.com/9.0.0_r3/xref/build/make/core/
    
    Soong还会编译产生一个androidmk命令,可以手动把Android.mk转换成Android.bp。 这只对无选择、循环等复杂流程控制的Android.mk生效
    /out/soong/host/linux-86/bin/androidmk
    /out/soong/host/linux-86/bin/bpfmt     用于格式化format bp文件
    
    READ.MD
    # Soong
    
    Soong is the replacement for the old Android make-based build system.  It
    replaces Android.mk files with Android.bp files, which are JSON-like simple
    declarative descriptions of modules to build.
    
    使用 bp 取代 mk , bp 文件类似 json格式的模块构建描述
    ## Android.bp file format
    
    By design, Android.bp files are very simple.  
    There are no conditionals or control flow statements 【没有条件流语句和控制语句】
     - any complexity is handled in build logic written in Go.  【 在用Go编写的构建逻辑中处理任何复杂性 】
     - The syntax and semantics of Android.bp files are intentionally similar
    to [Bazel BUILD files](https://www.bazel.io/versions/master/docs/be/overview.html)
    when possible.
    
    ### Modules
    
    A module in an Android.bp file starts with a module type, followed by a set of
    properties in `name: value,` format:
    
    

    cc_binary { name: “gzip”, srcs: [“src/test/minigzip.c”], shared_libs: [“libz”], stl: “none”, }

    
    Every module must have a `name` property, and the value must be unique across all Android.bp files.
    每个模块必须有一个“name”属性,该值在所有Android的blueprint构建系统中必须是唯一的
    
    For a list of valid module types and their properties see
    [$OUT_DIR/soong/.bootstrap/docs/soong_build.html](https://go/Android.bp).
    /out/soong/docs/soong_build.html
    

    Soong 编译

    /build/soong/soong.bash    启用Soong以后,在Android编译最开始的准备阶段,会执行build/soong/soong.bash进行环境准备
    Soong是与Android强关联的一个项目,  而Blueprint则相对比较独立,可以单独编译、使用
    
    
    

    Soong处理bp后生成的文件

    soong处理后的bp文件生成 
    1. /out/soong/build.ninja     256万行   450MB大小     尼玛这么大   所有 .bp 文件生成的 ninja 文件
    2. /out/soong/Android-xxxproduct.mk     31万行 也是个狠角色     这个文件是编译完成后模块的安装脚本,负责将模块安装到对应位置
    3. /out/build-xxxxproduct.ninja     /out/build-aosp_arm.ninja  154万行  650MB 更狠角色 所有 .mk 文件生成的ninja 文件  
    
    
    4. /out/soong/.minibootstrap/build.ninja       5行    主要是用来编译blueprint和生成   /out/soong/.bootstrap/build.ninja
    		bootstrapBuildDir = out/soong
    		topFile = ./Android.bp
    		extraArgs =  -t -l out/.module_paths/Android.bp.list
    		builddir = out
    		include ./build/blueprint/bootstrap/build.ninja
    
    5. /out/soong/.bootstrap/build.ninja           4000 行    主要是生成 soong相关工具和 /out/soong/build.ninja文件
    

    kati

    /build/kati/
    https://github.com/google/kati                        //  谷歌 kati 开源项目
    http://androidxref.com/9.0.0_r3/xref/build/kati/
    
    apt install ninja-build
    apt install gcc
    apt install g++
    kati是Google专门为了Android而开发的一个小项目,基于Golang和C++。 目的是为了把Android中的Makefile,转换成Ninja文件
    
    git clone https://github.com/google/kati.git
    在Git库中,可以用make来直接编译。
     编译完成后,目录下会出现ckati这个可执行文件。
     产生ckati后,通过执行【 ./m2n 】,可以把Makefile转换成build.ninja文件。 接下来,可以通过执行ninja来再次编译
    在Android项目中,这个Git库自带Android.bp文件,可以作为一个模块自动跟随项目一起编译。 
    也可以在项目路径中,执行mm单独编译。 编译产物主要是ckati,会被安装到项目的out/host/linux-x86/bin/ckati,作为编译过程中主机环境的一部分
    在Android项目中,ckati会在编译过程中,自动被使用,无需操心。
    
    单独使用时,在包含Makefile的目录下,执行ckati,效果与make基本相同。 
    执行【 ckati --ninja 】,可以根据Makefile生成build.ninja文件,并且附带env.sh和ninja.sh。
    通过env.sh来配置环境   初始化为空文件
    通过执行./ninja.sh来启动Ninja、使用build.ninja编译
    
     生成的ninja.sh文件,主要内容如下
    . ./env.sh
    exec ninja -f ./build.ninja "$@"
    
    
    步骤:
    【0】 apt install gcc     apt install g++    apt install ninja-build     
    【1】 git clone https://github.com/google/kati.git
    【2】 make         // 生成  ckati   
    【3】./m2n         // 使用kati 把 makefile 转为 ninja文件  并附带     env.sh    ninja.sh
    
    【4】./ninja.sh    // 使用 ninja-build  来对  ninja文件进行处理   再次生成 ckati   但是是通过 ninja
    【5】 ckati --ninja  // 单独使用 把makefile 转为 ninj  在   testcase 目录中 
    
    kati是一个基于Makefile来生成ninja.build的小项目。 
    它是Google专为Android而开发,用来修正Android项目创建之初的错误——使用Makefile来做一个超复杂的编译构建系统
    在编译过程中,kati负责把既有的Makefile、Android.mk,转换成Ninja文件
    在Android 7.0中,它独挑大梁。 在Android 8.0以后,它与Soong一起,成为Ninja文件的两大来源
    
    在单独使用时,它对普通的小项目还能勉强生效。 面对复杂的、多嵌套的Makefile时,它往往无法支持,会出现各种各样的问题。
     当然,也可以理解为,它只为Android而设计。
    所以 不推荐在Android以外的任何项目中使用kati
    

    ninja

    https://ninja-build.org/
    https://github.com/ninja-build/ninja
    
    Ninja是一个专注于速度的小型构建系统。
    它与其他构建系统的不同之处在于两个主要方面:
    1. 目的是让更高级别的构建系统生成其输入文件,
    2. 目的是尽可能快地运行构建。
    3. 专注于处理 ninja 配置文件   而该文件由 soong  kati 项目 产生 
    
    使用ninja构建系统为什么还需要一个其他的构建系统?
    
    在其他构建系统使用的语言是高级语言,Ninja的目标语言是构建汇编器。
    
    Ninja构建文件是人类可读的,但是手工编写并不方便
    Ninja build files are human-readable but not especially convenient to write by hand
    
    
    

    build.ninja.html ninja自构建例子

    https://ninja-build.org/build.ninja.html
    
    
    # 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
    
    # browse_py.h is used to inline browse.py.
    rule inline
      command = "$root/src/inline.sh" $varname < $in > $out
      description = INLINE $out
    build $builddir/browse_py.h: inline $root/src/browse.py | $root/src/inline.sh
      varname = kBrowsePy
    
    build $builddir/browse.o: cxx $root/src/browse.cc || $builddir/browse_py.h
    
    # the depfile parser and ninja lexers are generated using re2c.
    rule re2c
      command = re2c -b -i --no-generation-date -o $out $in
      description = RE2C $out
    build $root/src/depfile_parser.cc: re2c $root/src/depfile_parser.in.cc
    build $root/src/lexer.cc: re2c $root/src/lexer.in.cc
    
    # Core source files all build into ninja library.
    build $builddir/build.o: cxx $root/src/build.cc
    build $builddir/build_log.o: cxx $root/src/build_log.cc
    build $builddir/clean.o: cxx $root/src/clean.cc
    build $builddir/debug_flags.o: cxx $root/src/debug_flags.cc
    build $builddir/depfile_parser.o: cxx $root/src/depfile_parser.cc
    build $builddir/deps_log.o: cxx $root/src/deps_log.cc
    build $builddir/disk_interface.o: cxx $root/src/disk_interface.cc
    build $builddir/edit_distance.o: cxx $root/src/edit_distance.cc
    build $builddir/eval_env.o: cxx $root/src/eval_env.cc
    build $builddir/graph.o: cxx $root/src/graph.cc
    build $builddir/graphviz.o: cxx $root/src/graphviz.cc
    build $builddir/lexer.o: cxx $root/src/lexer.cc
    build $builddir/line_printer.o: cxx $root/src/line_printer.cc
    build $builddir/manifest_parser.o: cxx $root/src/manifest_parser.cc
    build $builddir/metrics.o: cxx $root/src/metrics.cc
    build $builddir/state.o: cxx $root/src/state.cc
    build $builddir/util.o: cxx $root/src/util.cc
    build $builddir/version.o: cxx $root/src/version.cc
    build $builddir/subprocess-posix.o: cxx $root/src/subprocess-posix.cc
    build $builddir/libninja.a: ar $builddir/browse.o $builddir/build.o $
        $builddir/build_log.o $builddir/clean.o $builddir/debug_flags.o $
        $builddir/depfile_parser.o $builddir/deps_log.o $
        $builddir/disk_interface.o $builddir/edit_distance.o $
        $builddir/eval_env.o $builddir/graph.o $builddir/graphviz.o $
        $builddir/lexer.o $builddir/line_printer.o $builddir/manifest_parser.o $
        $builddir/metrics.o $builddir/state.o $builddir/util.o $
        $builddir/version.o $builddir/subprocess-posix.o
    
    # Main executable is library plus main() function.
    build $builddir/ninja.o: cxx $root/src/ninja.cc
    build ninja: link $builddir/ninja.o | $builddir/libninja.a
      libs = -lninja
    
    # Tests all build into ninja_test executable.
    build $builddir/build_log_test.o: cxx $root/src/build_log_test.cc
    build $builddir/build_test.o: cxx $root/src/build_test.cc
    build $builddir/clean_test.o: cxx $root/src/clean_test.cc
    build $builddir/depfile_parser_test.o: cxx $root/src/depfile_parser_test.cc
    build $builddir/deps_log_test.o: cxx $root/src/deps_log_test.cc
    build $builddir/disk_interface_test.o: cxx $root/src/disk_interface_test.cc
    build $builddir/edit_distance_test.o: cxx $root/src/edit_distance_test.cc
    build $builddir/graph_test.o: cxx $root/src/graph_test.cc
    build $builddir/lexer_test.o: cxx $root/src/lexer_test.cc
    build $builddir/manifest_parser_test.o: cxx $root/src/manifest_parser_test.cc
    build $builddir/ninja_test.o: cxx $root/src/ninja_test.cc
    build $builddir/state_test.o: cxx $root/src/state_test.cc
    build $builddir/subprocess_test.o: cxx $root/src/subprocess_test.cc
    build $builddir/test.o: cxx $root/src/test.cc
    build $builddir/util_test.o: cxx $root/src/util_test.cc
    build ninja_test: link $builddir/build_log_test.o $builddir/build_test.o $
        $builddir/clean_test.o $builddir/depfile_parser_test.o $
        $builddir/deps_log_test.o $builddir/disk_interface_test.o $
        $builddir/edit_distance_test.o $builddir/graph_test.o $
        $builddir/lexer_test.o $builddir/manifest_parser_test.o $
        $builddir/ninja_test.o $builddir/state_test.o $
        $builddir/subprocess_test.o $builddir/test.o $builddir/util_test.o | $
        $builddir/libninja.a
      libs = -lninja
    
    # Ancillary executables.
    build $builddir/build_log_perftest.o: cxx $root/src/build_log_perftest.cc
    build build_log_perftest: link $builddir/build_log_perftest.o | $
        $builddir/libninja.a
      libs = -lninja
    build $builddir/canon_perftest.o: cxx $root/src/canon_perftest.cc
    build canon_perftest: link $builddir/canon_perftest.o | $builddir/libninja.a
      libs = -lninja
    build $builddir/depfile_parser_perftest.o: cxx $
        $root/src/depfile_parser_perftest.cc
    build depfile_parser_perftest: link $builddir/depfile_parser_perftest.o | $
        $builddir/libninja.a
      libs = -lninja
    build $builddir/hash_collision_bench.o: cxx $root/src/hash_collision_bench.cc
    build hash_collision_bench: link $builddir/hash_collision_bench.o | $
        $builddir/libninja.a
      libs = -lninja
    build $builddir/manifest_parser_perftest.o: cxx $
        $root/src/manifest_parser_perftest.cc
    build manifest_parser_perftest: link $builddir/manifest_parser_perftest.o | $
        $builddir/libninja.a
      libs = -lninja
    
    # Generate a graph using the "graph" tool.
    rule gendot
      command = ./ninja -t graph all > $out
    rule gengraph
      command = dot -Tpng $in > $out
    build $builddir/graph.dot: gendot ninja build.ninja
    build graph.png: gengraph $builddir/graph.dot
    
    # Generate the manual using asciidoc.
    rule asciidoc
      command = asciidoc -b docbook -d book -o $out $in
      description = ASCIIDOC $out
    rule xsltproc
      command = xsltproc --nonet doc/docbook.xsl $in > $out
      description = XSLTPROC $out
    build $builddir/manual.xml: asciidoc $root/doc/manual.asciidoc
    build $root/doc/manual.html: xsltproc $builddir/manual.xml | $
        $root/doc/style.css $root/doc/docbook.xsl
    build manual: phony || $root/doc/manual.html
    
    rule dblatex
      command = dblatex -q -o $out -p doc/dblatex.xsl $in
      description = DBLATEX $out
    build $root/doc/manual.pdf: dblatex $builddir/manual.xml | $
        $root/doc/dblatex.xsl
    # Generate Doxygen.
    rule doxygen
      command = doxygen $in
      description = DOXYGEN $in
    doxygen_mainpage_generator = $root/src/gen_doxygen_mainpage.sh
    rule doxygen_mainpage
      command = $doxygen_mainpage_generator $in > $out
      description = DOXYGEN_MAINPAGE $out
    build $builddir/doxygen_mainpage: doxygen_mainpage README COPYING | $
        $doxygen_mainpage_generator
    build doxygen: doxygen $root/doc/doxygen.config | $builddir/doxygen_mainpage
    
    # Regenerate build files if build script changes.
    rule configure
      command = ${configure_env}python $root/configure.py $configure_args
      generator = 1
    build build.ninja: configure | $root/configure.py $root/misc/ninja_syntax.py
    
    default ninja
    
    # Packaging
    rule rpmbuild
      command = misc/packaging/rpmbuild.sh
      description = Building rpms..
    build rpm: rpmbuild
    
    build all: phony ninja ninja_test build_log_perftest canon_perftest $
        depfile_parser_perftest hash_collision_bench manifest_parser_perftest
    
    

    Android.mk、Android.bp、Soong、Blueprint、Ninja 关系

    Android.bp --> Blueprint --> Soong --> Ninja
    Makefile or Android.mk --> kati --> Ninja
    (Android.mk --> Blueprint --> Soong  --> Android.bp) 
    
    
    现存的Android.mk、既有的Android.bp,都会分别被转换成Ninja
    
     从Android.mk与其它Makefile,会生成 /out/build-<product_name>.ninja   154万行  650MB 
     从Android.bp,则会生成out/soong/build.ninja     256万行   450MB大小 
    此外,还会生成一个较小的out/combined-<product_name>.ninja     5行  负责把二者组合起来,作为执行入口
    Ninja文件才是真正直接控制源码编译的工具
    
    1. /out/soong/build.ninja     256万行   450MB大小     尼玛这么大   所有 .bp 文件生成的 ninja 文件
    2. /out/soong/Android-xxxproduct.mk     31万行 也是个狠角色     这个文件是编译完成后模块的安装脚本,负责将模块安装到对应位置
    3. /out/build-xxxxproduct.ninja     /out/build-aosp_arm.ninja  154万行  650MB 更狠角色 所有 .mk 文件生成的ninja 文件  
    
    
    4. /out/soong/.minibootstrap/build.ninja       5行    主要是用来编译blueprint和生成   /out/soong/.bootstrap/build.ninja
    		bootstrapBuildDir = out/soong
    		topFile = ./Android.bp
    		extraArgs =  -t -l out/.module_paths/Android.bp.list
    		builddir = out
    		include ./build/blueprint/bootstrap/build.ninja
    
    5. /out/soong/.bootstrap/build.ninja           4000 行    主要是生成 soong相关工具和 /out/soong/build.ninja文件
    6. /out/combined-xxxproduct.ninja              5 行     include    1和3
    

    Android 8.0的编译过程与问题

    在原先Android 6.0纯Makefile编译的传统流程前,8.0版本新增了四个步骤:
    
    1.Soong的自举(bootstrap)。这个步骤会编译Soong的核心组件。
    2.收集Android.bp并生成out/soong/build.ninja文件。
    3.收集Android.mk并生成out/build-<product>.ninja与out/combined-<product>.ninja文件。
    4.执行Ninja文件,进行编译这个combined-*.ninja   文件,就是真正的执行入口。
      后面的步骤就是自底向上编译Android所有模块,与以前相同
    
    问题1: Ninja文件太大
    在AOSP 8.0版本,out/build-<product>.ninja文件超过400MB,
    out/soong/build.ninja也超过200MB,实际的项目会更大。 
    out/combined-<product>.ninja是对另外两个文件的组合,本身并不大。
    比起out目录下其它几十GB的产物,这可能不算是一个真正的问题。 但这些巨大的纯文本文件,会产生其它问题。
    
    问题2: 生成Ninja文件很慢
    Android编译脚本重新生成Ninja文件的场景,比想象中要多。
    如果只是在项目lunch不同产品参数时,生成不同的Ninja文件,这还可以接受。 
    实际上不仅如此,用mm进行单模块编译时,也会生成一组Ninja文件, 
    命名规律为build-<product_name>-<path_to_Android.mk>.ninja
    比如,在AOSP项目lunch aosp_arm64-eng后,于system/netd目录下,做mm编译,会出现以下打印:
    out/build-aosp_arm64-system_netd_Android.mk.ninja is missing, regenerating...
    卡4~6分钟后,生成了out/build-aosp_arm64-system_netd_Android.mk.ninja文件,并继续编译过程
    此外,mmm会额外生成另外一种Ninja文件:out/build-aosp_arm64-_system_netd_Android.mk.ninja。 
    仔细看,可以发现,差别就是多了一个下划线_! 这两个文件,不仅名称相近,而且内容相同,可能是一个bug
    有时,也会生成一种带SHA1的的文件,比如out/build-8015fee4a534af9c63f8512e71a7f51b.ninja
    
    问题3:重新生成Ninja的条件很多
    在system/netd目录下,再次执行mm,则大约9秒就得到结果 ninja: no work to do.
    然而,如果touch Android.mk,再执行mm,就需要重新生成Ninja文件
    system/netd/Android.mk was modified, regenerating...
    已知的规则,只有编译配置文件被修改。 除了显式修改,还有类似git pull、repo sync这样的情况。
     然而实际使用中,有更多未知因素,也会导致Ninja文件重新生成。
    
    
    

    使用ninja能改进什么

    使用ninja,可以不经过make,直接执行Ninja文件,完全避免重新生成,以及解析Makefile的运行开销。
    
    此外,还能、或者说必须,指定更细致的执行内容。 mm的意思,是执行当前目录下的所有编译模块。
     Android.mk定义了几个模块,就会执行几个。 
    使用ninja必须指定一个target,否则是全编译。 可以直接指定模块名,单独执行某个模块。 
    甚至可以指定具体的*.o、*.jar、*.dex文件为target,避免编译整个模块。
    
    (当然,make也支持指定模块名,或更细致的target。 用Ninja只是为了避免重新生成,其实相比Android 6.0,执行速度未见得有多快。)
    
    

    使用ninja

    1.准备build.ninja
    make的默认编译文件是Makefile,而Ninja默认的编译文件则是build.ninja。
    ninja需要在Android项目根目录执行,那里当然是没有这个文件的,需要自行准备。
    【一】 ln -s out/combined-aosp_arm64.ninja build.ninja    // 在根目录创建一个软链接 build.ninja 指向  out/combined-aosp_arm64.ninja
    
    以上仍然以AOSP的lunch aosp_arm64-eng为例。 如果有多个combined-*.ninja文件,可以自行选择一个。
    ninja 也支持-f参数,可以临时指定一个Ninja文件来执行
    ninja -f out/combined-aosp_arm64.ninja <target>
    
    2. 找到模块名
    在source build/envsetup.sh后,有一些额外的function,可以帮助查找代码,详见hmm。 
    其中mgrep,对查找*.mk文件,很有帮助。 而Android.bp,则还没做相关功能,不过也可以通过原版grep来实现
    比如,在AOSP的system/netd目录下,查找所有Makefile里的模块名:
    source build/envsetup.sh
    mgrep -w 'LOCAL_MODULE\|LOCAL_PACKAGE_NAME'
    
    再查找所有Android.bp里的模块名。
    grep -rnws --include='*.bp' 'name:'
    libnetdutils/Android.bp:2:    name: "libnetdutils",
    libnetdutils/Android.bp:26:    name: "netdutils_test",
    
    
    有些Java层的模块,可能会没有指定LOCAL_MODULE,而是指定LOCAL_PACKAGE_NAME
    mgrep -w 'LOCAL_MODULE\|LOCAL_PACKAGE_NAME'
    
    
    原理大概是这样,而为了方便,可以用一个alias来简化查找
    
    alias findm="grep -rnws --include='*.[mb][kp]' 'LOCAL_MODULE\|LOCAL_PACKAGE_NAME\|name:'"
    

    使用ninja执行单模块编译

    要编译单个文件,先要找到对应的target,这一个原则是相同的。 但由于C/C++与Java的编译方式大不相同,因此分开举例介绍。
    
    C/C++
    开发过程中,如果单个文件编译出错了,可以在修改后,用ninja单独编译这个文件。
    $ echo error >> system/netd/server/Network.cpp  # Make an error
    $ ninja netd
    [1/6] target  C++: netd <= system/netd/server/Network.cpp
    FAILED: out/target/product/aosp_arm64/obj/EXECUTABLES/netd_intermediates/Network.o  【Network.cpp 生成 Network.o 失败 】
    ...
    system/netd/server/Network.cpp:95:1: error: unknown type name 'error'
    error
    ^
    system/netd/server/Network.cpp:95:6: error: expected unqualified-id
    error
         ^
    2 errors generated.
    ninja: build stopped: subcommand failed.
    
    在修改了这个错误后,可以通过FAILED:的提示,直接编译这个*.o文件
    ninja out/target/product/aosp_arm64/obj/EXECUTABLES/netd_intermediates/Network.o
    这样,能最快地检查编译问题是否修复
    
    
    
    
    
    Java
    Java层的情况也类似,但没有办法做到像C/C++这么精确。
    
    echo error >> packages/apps/Settings/src/com/android/settings/Settings.java  # Make an error 报错
    $ ninja Settings
    ...
    [3/9] Building with Jack: out/target/common/obj/APPS/Settings_intermediates/with-local/classes.dex
    FAILED: out/target/common/obj/APPS/Settings_intermediates/with-local/classes.dex
    ...
    ERROR: /home/yanqd1/workspace/aosp_arm64/packages/apps/Settings/src/com/android/settings/Settings.java:183.1: Syntax error on token "error", delete this token
    ninja: build stopped: subcommand failed.
    
    
    复问题后,同样是根据FAILED:这一行的提示,编译这个*.dex文件
    $ ninja out/target/common/obj/APPS/Settings_intermediates/with-local/classes.dex
    ...
    [3/3] Building with Jack: out/target/common/obj/APPS/Settings_intermediates/with-local/classes.dex