摘要:Blueprint解析Android.bp到ninja的代码流程时如何走的?
阅读本文大约需要花费18分钟。
文章首发微信公众号:IngresGe
专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢!
欢迎关注我的公众号!
[Android取经之路] 的源码都基于Android-Q(10.0) 进行分析
[Android取经之路] 系列文章:
《系统启动篇》
- Android系统架构
- Android是怎么启动的
- Android 10.0系统启动之init进程
- Android10.0系统启动之Zygote进程
- Android 10.0 系统启动之SystemServer进程
- Android 10.0 系统服务之ActivityMnagerService
- Android10.0系统启动之Launcher(桌面)启动流程
- Android10.0应用进程创建过程以及Zygote的fork流程
- Android 10.0 PackageManagerService(一)工作原理及启动流程
- Android 10.0 PackageManagerService(二)权限扫描
- Android 10.0 PackageManagerService(三)APK扫描
- Android 10.0 PackageManagerService(四)APK安装流程
《日志系统篇》
- Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性
- Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化
- Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析
- Android10.0 日志系统分析(四)-selinux、kernel日志在logd中的实现
《Binder通信原理》:
- Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要
- Android10.0 Binder通信原理(二)-Binder入门篇
- Android10.0 Binder通信原理(三)-ServiceManager篇
- Android10.0 Binder通信原理(四)-Native-C\C++实例分析
- Android10.0 Binder通信原理(五)-Binder驱动分析
- Android10.0 Binder通信原理(六)-Binder数据如何完成定向打击
- Android10.0 Binder通信原理(七)-Framework binder示例
- Android10.0 Binder通信原理(八)-Framework层分析
- Android10.0 Binder通信原理(九)-AIDL Binder示例
- Android10.0 Binder通信原理(十)-AIDL原理分析-Proxy-Stub设计模式
- Android10.0 Binder通信原理(十一)-Binder总结
《HwBinder通信原理》
- HwBinder入门篇-Android10.0 HwBinder通信原理(一)
- HIDL详解-Android10.0 HwBinder通信原理(二)
- HIDL示例-C++服务创建Client验证-Android10.0 HwBinder通信原理(三)
- HIDL示例-JAVA服务创建-Client验证-Android10.0 HwBinder通信原理(四)
- HwServiceManager篇-Android10.0 HwBinder通信原理(五)
- Native层HIDL服务的注册原理-Android10.0 HwBinder通信原理(六)
- Native层HIDL服务的获取原理-Android10.0 HwBinder通信原理(七)
- JAVA层HIDL服务的注册原理-Android10.0 HwBinder通信原理(八)
- JAVA层HIDL服务的获取原理-Android10.0 HwBinder通信原理(九)
- HwBinder驱动篇-Android10.0 HwBinder通信原理(十)
- HwBinder原理总结-Android10.0 HwBinder通信原理(十一)
《编译原理》
- 编译系统入门篇-Android10.0编译系统(一)
- 编译环境初始化-Android10.0编译系统(二)
- make编译过程-Android10.0编译系统(三)
- Image打包流程-Android10.0编译系统(四)
- Kati详解-Android10.0编译系统(五)
- Blueprint简介-Android10.0编译系统(六)
- Blueprint代码详细分析-Android10.0编译系统(七)
1 概述
上一节,我们介绍了blueprint的作用和执行过程,这一节我们从代码的层面来分析blueprint的具体执行流程。
启用Soong以后,在Android编译最开始的准备阶段,会执行build/soong/soong.bash进行环境准备。其中会先编译、安装Blueprint到out目录下。也就是说,在编译Android项目时,Android.bp相关工具链会自动编译,无需费神。
Soong是与Android强关联的一个项目,而Blueprint则相对比较独立,可以单独编译、使用。
2 blueprint相关编译链生成
通过前面的MAKE编译,我们了解到blueprint等工具链的生成,是在runSoong()中编出来的,因此我们就从runSoong()为突破口进行分析。
2.1 Build调用栈
make后,走的是Soong的构建,其中会把一些编译工具给编出来,例如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调用栈
runSoong执行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()的执行过程:
-
执行build/blueprint/bootstrap.bash 生成.minibootstrap/build.ninja 和.bootstrap/build.ninja
-
生成minibp\bpglob
-
通过ninja来编译.minibootstrap/build.ninja 和.bootstrap/build.ninja
首先执行build/blueprint/bootstrap.bash
bootstrap.bash的作用:
-
它可以引导独立的blueprint来生成minibp二进制文件,
可以直接运行 ./build/blueprint/bootstrap.bash。
-
也可以从另一个脚本调用它来引导基于Bleprint的自定义构建系统。
[/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
//执行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中模块的相关规则如下:
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# 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和内容处理规则,如deps等。
第二步:解析阶段读取一个或多个Blueprint文件,并根据已注册的模块类型验证其内容。
第三步:generate阶段分析解析的Blueprint内容,为必须执行的构建操作创建一个内部表示。此 阶段还执行模块依赖项和已解析Bleprint文件中定义的属性值的验证。
第四步:写入阶段根据生成的构建操作生成Ninja文件。
3.1 minibp编译入口
主要步骤:
1.配置一个Context结构
2.执行 command.go 的Main() 进行最终编译
[/build/blueprint/bootstrap/minibp/main.go]
func main() {
flag.Parse()
ctx := blueprint.NewContext() //配置一个Context结构
if !runAsPrimaryBuilder {
ctx.SetIgnoreUnknownModuleTypes(true)
}
config := Config{
generatingPrimaryBuilder: !runAsPrimaryBuilder,
}
//执行 command.go 的Main() 进行最终编译
bootstrap.Main(ctx, config)
}
3.2 Context配置
返回一个Context的结构,存储了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 执行 command.go Main()
3.3.1 调用栈
[/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)
...
}
步骤:
1.注册一些bp模块类型、依赖和类型
2.解析Blueprints文件
3.检查解析的Blueprint文件中定义的所有模块指定的依赖项是否有效
4.生成需要执行的所有生成操作的内部表示
5.将生成的构建操作的Ninja manifest文本写入文件
3.3.2 RegisterBottomUpMutator()
RegisterBottomUpMutator注册一个mutator,它将被调用来将模块拆分为变量。每个注册的mutator按注册顺序调用(混合使用toppownmutator和BottomUpMutators)每个模块一次,在所有模块依赖项的调用返回之前,不会在模块上调用。
此处给定的赋值函数类型名称对于上下文中所有自下而上或早期的赋值函数必须是唯一的。
返回一个MutatorHandle,在这个句柄上可以调用Parallel来设置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注册将被调用以生成生成操作的单例类型。作为生成阶段的一部分,每个注册的单例类型被实例化和调用一次。按照注册顺序调用每个已注册的单例。
此处给定的单例类型名称对于上下文必须是唯一的。factory函数应该是一个命名函数,这样它的包和名称就可以包含在生成的Ninja文件中,以便进行调试。
[/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文件中定义的所有模块指定的依赖项是否有效。这意味着依赖的模块已经定义,并且不存在循环依赖。
[/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生成需要执行的所有生成操作的内部表示。
这个过程包括对在解析阶段创建的每个Module对象调用GenerateBuildActions方法,然后对每个注册的单例对象调用GenerateBuildActions方法。
如果尚未调用ResolveDependencies方法,则此方法会自动调用该方法。
通过传递给GenerateBuildActions的ModuleContext和SingletonContext对象上的config方法,config参数可用于所有Module和Singleton对象。
它还传递给通过PoolFunc、RuleFunc和VariableFunc指定的函数,以便它们可以计算特定配置的值。
返回的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将生成的构建操作的Ninja manifest文本写入文件。
如果在PrepareBuildActions成功完成之前调用此函数,则返回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. 总结
根据上面的总结,我们明白了 Blueprint到ninja的流程,详细流程如下:
1.执行build/blueprint/bootstrap.bash 生成.minibootstrap/build.ninja 和.bootstrap/build.ninja
2.编译生成minibp\bpglob 两个可执行程序
3.通过minibp 解析并生成out/soong/.bootstrap/build.ninja
1)注册一些bp模块类型、依赖和类型
2)解析Blueprints文件
3)检查解析的Blueprint文件中定义的所有模块指定的依赖项是否有效
4)生成需要执行的所有生成操作的内部表示
5)将生成的构建操作的Ninja manifest文本写入文件
4.通过ninja来编译.minibootstrap/build.ninja 和.bootstrap/build.ninja,最终生成out/soong/build.ninja
下面我们再进一步探讨一些Android.bp的配置,以及ninja的编译过程。