本文轉(zhuǎn)載自:Blueprint代碼詳細分析-Android10.0編譯系統(tǒng)(七)
本文基于Android 10.0源碼分析
1.概述
??上一節(jié)庄新,我們介紹了blueprint的作用和執(zhí)行過程峡继,這一節(jié)我們從代碼的層面來分析blueprint的具體執(zhí)行流程较坛。啟用Soong以后殉摔,在Android編譯最開始的準備階段唯咬,會執(zhí)行build/soong/soong.bash進行環(huán)境準備秃殉。其中會先編譯姥份、安裝Blueprint到out目錄下树埠。也就是說祠丝,在編譯Android項目時疾呻,Android.bp相關(guān)工具鏈會自動編譯除嘹,無需費神。
??Soong是與Android強關(guān)聯(lián)的一個項目岸蜗,而Blueprint則相對比較獨立憾赁,可以單獨編譯、使用散吵。
2.blueprint相關(guān)編譯鏈生成
??通過前面的make編譯龙考,我們了解到blueprint等工具鏈的生成,是在runSoong()中編出來的矾睦,因此我們就從runSoong()為突破口進行分析晦款。
2.1 Build調(diào)用棧
??make后,走的是Soong的構(gòu)建枚冗,其中會把一些編譯工具給編出來缓溅,例如blueprint,然后通過runSoong找到所有的Android.bp文件赁温,編譯成out/soong/build.ninja坛怪。
??通過runKatiBuild找到所有的Android通過runKatiBuild找到所有的Android.mk文件,編譯out/build-aosp_arm.ninja股囊,最后把這些ninja文件合并成combined-aosp_arm.ninja袜匿,通過ninja完成最終編譯。
??其中runSoong對工具進行編譯稚疹,編譯出blueprint等編譯工具居灯,把*.bp 編譯成 out/soong/build.ninja。
2.2 runSoong調(diào)用棧
??runSoong執(zhí)行bootstrap.bash和blueprint_impl.bash内狗,最終生成minibp和bpglob進程怪嫌,建立/out/soong/.minibootstrap/build.ninja和/out/soong/.bootstrap/build.ninja 兩個文件。再通過out/soong/.bootstrap/bin/soong_build柳沙,編譯out/.module_paths/Android.bp.list及out/soong/.bootstrap/build-globs.ninja生成out/soong/build.ninja岩灭,參與最終的ninja編譯。
2.3 runSoong()
??runSoong()的執(zhí)行過程:
執(zhí)行build/blueprint/bootstrap.bash生成.minibootstrap/build.ninja和.bootstrap/build.ninja赂鲤;
生成minibp\bpglob噪径;
通過ninja來編譯.minibootstrap/build.ninja和.bootstrap/build.ninja。
(1)首先執(zhí)行build/blueprint/bootstrap.bash
(2)bootstrap.bash的作用
??它可以引導獨立的blueprint來生成minibp二進制文件蛤袒,可以直接運行./build/blueprint/bootstrap.bash熄云。也可以從另一個腳本調(diào)用它來引導基于Bleprint的自定義構(gòu)建系統(tǒng)。
// /build/soong/ui/build/soong.go
func runSoong(ctx Context, config Config) {
ctx.BeginTrace(metrics.RunSoong, "soong")
defer ctx.EndTrace()
func() {
ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
defer ctx.EndTrace()
cmd := Command(ctx, config, "blueprint bootstrap", "build/blueprint/bootstrap.bash", "-t")
cmd.Environment.Set("BLUEPRINTDIR", "./build/blueprint")
cmd.Environment.Set("BOOTSTRAP", "./build/blueprint/bootstrap.bash")
cmd.Environment.Set("BUILDDIR", config.SoongOutDir())
cmd.Environment.Set("GOROOT", "./"+filepath.Join("prebuilts/go", config.HostPrebuiltTag()))
cmd.Environment.Set("BLUEPRINT_LIST_FILE", filepath.Join(config.FileListDir(), "Android.bp.list"))
cmd.Environment.Set("NINJA_BUILDDIR", config.OutDir())
cmd.Environment.Set("SRCDIR", ".")
cmd.Environment.Set("TOPNAME", "Android.bp")
cmd.Sandbox = soongSandbox
//執(zhí)行build/blueprint/bootstrap.bash
cmd.RunAndPrintOrFatal()
}()
func() {
ctx.BeginTrace(metrics.RunSoong, "environment check")
defer ctx.EndTrace()
envFile := filepath.Join(config.SoongOutDir(), ".soong.environment")
envTool := filepath.Join(config.SoongOutDir(), ".bootstrap/bin/soong_env")
if _, err := os.Stat(envFile); err == nil {
if _, err := os.Stat(envTool); err == nil {
cmd := Command(ctx, config, "soong_env", envTool, envFile)
cmd.Sandbox = soongSandbox
var buf strings.Builder
cmd.Stdout = &buf
cmd.Stderr = &buf
if err := cmd.Run(); err != nil {
ctx.Verboseln("soong_env failed, forcing manifest regeneration")
os.Remove(envFile)
}
if buf.Len() > 0 {
ctx.Verboseln(buf.String())
}
} else {
ctx.Verboseln("Missing soong_env tool, forcing manifest regeneration")
os.Remove(envFile)
}
} else if !os.IsNotExist(err) {
ctx.Fatalf("Failed to stat %f: %v", envFile, err)
}
}()
var cfg microfactory.Config
cfg.Map("github.com/google/blueprint", "build/blueprint")
cfg.TrimPath = absPath(ctx, ".")
func() {
ctx.BeginTrace(metrics.RunSoong, "minibp")
defer ctx.EndTrace()
minibp := filepath.Join(config.SoongOutDir(), ".minibootstrap/minibp")
if _, err := microfactory.Build(&cfg, minibp, "github.com/google/blueprint/bootstrap/minibp"); err != nil {
ctx.Fatalln("Failed to build minibp:", err)
}
}()
func() {
ctx.BeginTrace(metrics.RunSoong, "bpglob")
defer ctx.EndTrace()
bpglob := filepath.Join(config.SoongOutDir(), ".minibootstrap/bpglob")
if _, err := microfactory.Build(&cfg, bpglob, "github.com/google/blueprint/bootstrap/bpglob"); err != nil {
ctx.Fatalln("Failed to build bpglob:", err)
}
}()
ninja := func(name, file string) {
ctx.BeginTrace(metrics.RunSoong, name)
defer ctx.EndTrace()
fifo := filepath.Join(config.OutDir(), ".ninja_fifo")
nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo)
defer nr.Close()
cmd := Command(ctx, config, "soong "+name,
config.PrebuiltBuildTool("ninja"),
"-d", "keepdepfile",
"-w", "dupbuild=err",
"-j", strconv.Itoa(config.Parallel()),
"--frontend_file", fifo,
"-f", filepath.Join(config.SoongOutDir(), file))
cmd.Sandbox = soongSandbox
cmd.RunAndPrintOrFatal()
}
ninja("minibootstrap", ".minibootstrap/build.ninja")
ninja("bootstrap", ".bootstrap/build.ninja")
}
2.4 minibp的生成
??從build/blueprint/Blueprints中可知妙真,需要編譯的二進制文件名為minibp缴允,源文件為:bootstrap/minibp/main.go,依賴于blueprint、blueprint-bootstrap练般、gotestmain-tests矗漾。
// build/blueprint/Blueprints
bootstrap_go_binary {
name: "minibp",
deps: [
"blueprint",
"blueprint-bootstrap",
"gotestmain-tests",
],
srcs: ["bootstrap/minibp/main.go"],
}
out/soong/.bootstrap/build.ninja中模塊的相關(guān)規(guī)則如下:
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Module: minibp
# Variant:
# Type: bootstrap_go_binary
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
# Defined: build/blueprint/Blueprints:132:1
build ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a: $
g.bootstrap.compile $
${g.bootstrap.srcDir}/build/blueprint/bootstrap/minibp/main.go | $
${g.bootstrap.compileCmd} $
${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a $
${g.bootstrap.buildDir}/.bootstrap/gotestmain-tests/pkg/github.com/google/blueprint/gotestmain.a
incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg -I ${g.bootstrap.buildDir}/.bootstrap/gotestmain-tests/pkg
pkgPath = minibp
build ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/a.out: g.bootstrap.link $
${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a | $
${g.bootstrap.linkCmd} $
${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a $
${g.bootstrap.buildDir}/.bootstrap/gotestmain-tests/pkg/github.com/google/blueprint/gotestmain.a
libDirFlags = -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg -L ${g.bootstrap.buildDir}/.bootstrap/gotestmain-tests/pkg
build out/soong/.bootstrap/bin/minibp: g.bootstrap.cp $
${g.bootstrap.buildDir}/.bootstrap/minibp/obj/a.out || $
${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/test/test.passed $
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/test/test.passed $
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/test/test.passed $
${g.bootstrap.buildDir}/.bootstrap/blueprint/test/test.passed $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/test/test.passed $
${g.bootstrap.buildDir}/.bootstrap/gotestmain-tests/test/test.passed
可以看到最終生成的minibp需要三部生成,第一部編譯成minibp.a其中依賴于blueprint薄料、blueprint-pathtools和main.go敞贡,第二部是link成a.out,第三部通過cp到對應的bin的目錄下摄职,完成一個module的解析誊役。
3.Blueprint的編譯階段
??生成Ninja文件的過程分為四個階段。解析一個Blueprint文件需要四步:
第一步:注冊階段準備上下文來處理包含各種類型模塊的Blueprint文件谷市。注冊module也就是bootstrap_go_binary和內(nèi)容處理規(guī)則蛔垢,如deps等。
第二步:解析階段讀取一個或多個Blueprint文件迫悠,并根據(jù)已注冊的模塊類型驗證其內(nèi)容鹏漆。
第三步:generate階段分析解析的Blueprint內(nèi)容,為必須執(zhí)行的構(gòu)建操作創(chuàng)建一個內(nèi)部表示创泄。此 階段還執(zhí)行模塊依賴項和已解析Bleprint文件中定義的屬性值的驗證艺玲。
第四步:寫入階段根據(jù)生成的構(gòu)建操作生成Ninja文件。
3.1 minibp編譯入口
主要步驟:
配置一個Context結(jié)構(gòu)鞠抑;
執(zhí)行command.go的Main()進行最終編譯饭聚。
// /build/blueprint/bootstrap/minibp/main.go
func main() {
flag.Parse()
ctx := blueprint.NewContext() //配置一個Context結(jié)構(gòu)
if !runAsPrimaryBuilder {
ctx.SetIgnoreUnknownModuleTypes(true)
}
config := Config{
generatingPrimaryBuilder: !runAsPrimaryBuilder,
}
//執(zhí)行 command.go 的Main() 進行最終編譯
bootstrap.Main(ctx, config)
}
3.2 Context配置
??返回一個Context的結(jié)構(gòu),存儲了bp里面的一些字段碍拆,例如之前看到的bootstrap_core_go_binary若治。
// /build/blueprint/context.go
func newContext() *Context {
return &Context{
Context: context.Background(),
moduleFactories: make(map[string]ModuleFactory), //存儲著我們后面注冊的module如“bootstrap_core_go_binary”
nameInterface: NewSimpleNameInterface(),
moduleInfo: make(map[Module]*moduleInfo),
globs: make(map[string]GlobPath),
fs: pathtools.OsFs,
ninjaBuildDir: nil,
requiredNinjaMajor: 1,
requiredNinjaMinor: 7,
requiredNinjaMicro: 0,
}
}
3.3 執(zhí)行 command.go Main()
3.3.1 調(diào)用棧
// /build/blueprint/bootstrap/command.go
package bootstrap
func Main(ctx *blueprint.Context, config interface{}, extraNinjaFileDeps ...string) {
...
ctx.RegisterBottomUpMutator("bootstrap_plugin_deps", pluginDeps)
ctx.RegisterModuleType("bootstrap_go_package", newGoPackageModuleFactory(bootstrapConfig))
ctx.RegisterModuleType("bootstrap_go_binary", newGoBinaryModuleFactory(bootstrapConfig, false))
ctx.RegisterModuleType("blueprint_go_binary", newGoBinaryModuleFactory(bootstrapConfig, true))
ctx.RegisterSingletonType("bootstrap", newSingletonFactory(bootstrapConfig))
ctx.RegisterSingletonType("glob", globSingletonFactory(ctx))
deps, errs := ctx.ParseFileList(filepath.Dir(bootstrapConfig.topLevelBlueprintsFile), filesToParse)
if len(errs) > 0 {
fatalErrors(errs)
}
// Add extra ninja file dependencies
deps = append(deps, extraNinjaFileDeps...)
extraDeps, errs := ctx.ResolveDependencies(config)
...
extraDeps, errs = ctx.PrepareBuildActions(config)
...
err = ctx.WriteBuildFile(out)
...
}
步驟:
注冊一些bp模塊類型、依賴和類型感混;
解析Blueprints文件;
檢查解析的Blueprint文件中定義的所有模塊指定的依賴項是否有效礼烈;
生成需要執(zhí)行的所有生成操作的內(nèi)部表示弧满;
將生成的構(gòu)建操作的Ninja manifest文本寫入文件。
3.3.2 RegisterBottomUpMutator()
??RegisterBottomUpMutator注冊一個mutator此熬,它將被調(diào)用來將模塊拆分為變量庭呜。每個注冊的mutator按注冊順序調(diào)用(混合使用toppownmutator和BottomUpMutators)每個模塊一次,在所有模塊依賴項的調(diào)用返回之前犀忱,不會在模塊上調(diào)用募谎。
??此處給定的賦值函數(shù)類型名稱對于上下文中所有自下而上或早期的賦值函數(shù)必須是唯一的。返回一個MutatorHandle阴汇,在這個句柄上可以調(diào)用Parallel來設(shè)置mutator在維護順序的同時并行訪問模塊数冬。
// /build/blueprint/context.go
func (c *Context) RegisterBottomUpMutator(name string, mutator BottomUpMutator) MutatorHandle {
for _, m := range c.variantMutatorNames {
if m == name {
panic(fmt.Errorf("mutator name %s is already registered", name))
}
}
info := &mutatorInfo{
bottomUpMutator: mutator,
name: name,
}
c.mutatorInfo = append(c.mutatorInfo, info)
c.variantMutatorNames = append(c.variantMutatorNames, name)
return info
}
3.3.3 RegisterSingletonType()
??RegisterSingletonType注冊將被調(diào)用以生成生成操作的單例類型。作為生成階段的一部分搀庶,每個注冊的單例類型被實例化和調(diào)用一次拐纱。按照注冊順序調(diào)用每個已注冊的單例铜异。此處給定的單例類型名稱對于上下文必須是唯一的。factory函數(shù)應該是一個命名函數(shù)秸架,這樣它的包和名稱就可以包含在生成的Ninja文件中揍庄,以便進行調(diào)試。
// /build/blueprint/context.go
func (c *Context) RegisterSingletonType(name string, factory SingletonFactory) {
for _, s := range c.singletonInfo {
if s.name == name {
panic(errors.New("singleton name is already registered"))
}
}
c.singletonInfo = append(c.singletonInfo, &singletonInfo{
factory: factory,
singleton: factory(),
name: name,
})
}
3.3.4 ResolveDependencies()
??ResolveDependencies檢查解析的Blueprint文件中定義的所有模塊指定的依賴項是否有效东抹。這意味著依賴的模塊已經(jīng)定義蚂子,并且不存在循環(huán)依賴。
// /build/blueprint/context.go
func (c *Context) resolveDependencies(ctx context.Context, config interface{}) (deps []string, errs []error) {
pprof.Do(ctx, pprof.Labels("blueprint", "ResolveDependencies"), func(ctx context.Context) {
c.liveGlobals = newLiveTracker(config)
deps, errs = c.generateSingletonBuildActions(config, c.preSingletonInfo, c.liveGlobals)
if len(errs) > 0 {
return
}
errs = c.updateDependencies()
if len(errs) > 0 {
return
}
var mutatorDeps []string
mutatorDeps, errs = c.runMutators(ctx, config)
if len(errs) > 0 {
return
}
deps = append(deps, mutatorDeps...)
c.cloneModules()
c.dependenciesReady = true
})
if len(errs) > 0 {
return nil, errs
}
return deps, nil
}
3.3.5 PrepareBuildActions()
??PrepareBuildActions生成需要執(zhí)行的所有生成操作的內(nèi)部表示缭黔。這個過程包括對在解析階段創(chuàng)建的每個Module對象調(diào)用GenerateBuildActions方法食茎,然后對每個注冊的單例對象調(diào)用GenerateBuildActions方法。
??如果尚未調(diào)用ResolveDependencies方法试浙,則此方法會自動調(diào)用該方法董瞻。通過傳遞給GenerateBuildActions的ModuleContext和SingletonContext對象上的config方法,config參數(shù)可用于所有Module和Singleton對象田巴。它還傳遞給通過PoolFunc钠糊、RuleFunc和VariableFunc指定的函數(shù),以便它們可以計算特定配置的值壹哺。返回的deps是由模塊和singleton通過ModuleContext添加的ninja文件依賴項的列表抄伍。AddNinjaFileDeps(),SingletonContext.AddNinjaFileDeps()管宵,和PackageContext.AddNinjaFileDeps()方法夭谤。
// /build/blueprint/context.go
func (c *Context) PrepareBuildActions(config interface{}) (deps []string, errs []error) {
pprof.Do(c.Context, pprof.Labels("blueprint", "PrepareBuildActions"), func(ctx context.Context) {
c.buildActionsReady = false
if !c.dependenciesReady {
var extraDeps []string
extraDeps, errs = c.resolveDependencies(ctx, config)
if len(errs) > 0 {
return
}
deps = append(deps, extraDeps...)
}
var depsModules []string
depsModules, errs = c.generateModuleBuildActions(config, c.liveGlobals)
if len(errs) > 0 {
return
}
var depsSingletons []string
depsSingletons, errs = c.generateSingletonBuildActions(config, c.singletonInfo, c.liveGlobals)
if len(errs) > 0 {
return
}
deps = append(deps, depsModules...)
deps = append(deps, depsSingletons...)
if c.ninjaBuildDir != nil {
err := c.liveGlobals.addNinjaStringDeps(c.ninjaBuildDir)
if err != nil {
errs = []error{err}
return
}
}
pkgNames, depsPackages := c.makeUniquePackageNames(c.liveGlobals)
deps = append(deps, depsPackages...)
// This will panic if it finds a problem since it's a programming error.
c.checkForVariableReferenceCycles(c.liveGlobals.variables, pkgNames)
c.pkgNames = pkgNames
c.globalVariables = c.liveGlobals.variables
c.globalPools = c.liveGlobals.pools
c.globalRules = c.liveGlobals.rules
c.buildActionsReady = true
})
if len(errs) > 0 {
return nil, errs
}
return deps, nil
}
3.3.6 WriteBuildFile()
??WriteBuildFile將生成的構(gòu)建操作的Ninja manifest文本寫入文件。如果在PrepareBuildActions成功完成之前調(diào)用此函數(shù)烦磁,則返回errBuildActionsOnTready驾孔。
// /build/blueprint/context.go
func (c *Context) WriteBuildFile(w io.Writer) error {
var err error
pprof.Do(c.Context, pprof.Labels("blueprint", "WriteBuildFile"), func(ctx context.Context) {
if !c.buildActionsReady {
err = ErrBuildActionsNotReady
return
}
nw := newNinjaWriter(w)
err = c.writeBuildFileHeader(nw)
if err != nil {
return
}
err = c.writeNinjaRequiredVersion(nw)
if err != nil {
return
}
err = c.writeSubninjas(nw)
if err != nil {
return
}
// TODO: Group the globals by package.
err = c.writeGlobalVariables(nw)
if err != nil {
return
}
err = c.writeGlobalPools(nw)
if err != nil {
return
}
err = c.writeBuildDir(nw)
if err != nil {
return
}
err = c.writeGlobalRules(nw)
if err != nil {
return
}
err = c.writeAllModuleActions(nw)
if err != nil {
return
}
err = c.writeAllSingletonActions(nw)
if err != nil {
return
}
})
if err != nil {
return err
}
return nil
}
4.總結(jié)
根據(jù)上面的總結(jié),我們明白了Blueprint到ninja的流程炸庞,詳細流程如下:
執(zhí)行build/blueprint/bootstrap.bash 生成.minibootstrap/build.ninja 和.bootstrap/build.ninja钱床;
編譯生成minibp\bpglob 兩個可執(zhí)行程序;
-
通過minibp 解析并生成out/soong/.bootstrap/build.ninja埠居;
1)注冊一些bp模塊類型查牌、依賴和類型
2)解析Blueprints文件;
3)檢查解析的Blueprint文件中定義的所有模塊指定的依賴項是否有效滥壕;
4)生成需要執(zhí)行的所有生成操作的內(nèi)部表示纸颜;
5)將生成的構(gòu)建操作的Ninja manifest文本寫入文件。
通過ninja來編譯.minibootstrap/build.ninja 和.bootstrap/build.ninja绎橘,最終生成out/soong/build.ninja胁孙。
??下面我們再進一步探討一些Android.bp的配置,以及ninja的編譯過程。