Android快速阅读依赖的代码,Android blueprint 代码分析

前面的文档已经大体介绍了blueprint,本篇则通过代码分析blueprint让大家更清楚的了解blueprint以及其语法。以及基于blueprint的语法生成的minibp和soong_build。

blueprint主要是解析Blueprint文件翻译成ninja语法文件。以build/blueprint/Blueprints文件内容为例,我们要解析的语法如下:

bootstrap_core_go_binary(

name = "bpglob",

deps = ["blueprint-pathtools"],

srcs = ["bootstrap/bpglob/bpglob.go"],

)

生成的对应的ninja文件如下:

d357345e2bb0

可以看到最终策划你改成的bpglob需要三部生成,第一部编译成bpblob.a其中依赖于bluprint-pathtools和bpglob.go,第二部是连接成a.out,第三部通过cp到对应的bin的目录下,完成一个module的解析。

d357345e2bb0

解析一个Blueprint文件需要三步:

第一步:注册module也就是bootstrap_core_go_binary和内容处理规则,如deps等。

第二步:解析Blueprint文件,进行语法分析

第三步:分析module之间的依赖规则

第四步: 规则生成

第五步: 写ninjia文件完成文件生成

d357345e2bb0

第一步:注册

整个解析过程需要一个context结构体进行跟踪,所以minibp和soong_build的入口都是如下,创建一个context

func NewContext() *Context {

ctx := &Context{

moduleFactories:  make(map[string]ModuleFactory),

moduleNames:      make(map[string]*moduleGroup),

moduleInfo:      make(map[Module]*moduleInfo),

moduleNinjaNames: make(map[string]*moduleGroup),

globs:            make(map[string]GlobPath),

}

ctx.RegisterBottomUpMutator("blueprint_deps", blueprintDepsMutator)

return ctx

}

moduleFactories: 存储着我们后面注册的module如“bootstrap_core_go_binary”

moduleNames:

moduleInfo:

moduleNinjaNames:

globs: glob文件

ctx.RegisterBottomUpMutator("bootstrap_plugin_deps", pluginDeps)

ctx.RegisterModuleType("bootstrap_go_package",newGoPackageModuleFactory(bootstrapConfig))

ctx.RegisterModuleType("bootstrap_core_go_binary",newGoBinaryModuleFactory(bootstrapConfig, StageBootstrap))

ctx.RegisterModuleType("bootstrap_go_binary", newGoBinaryModuleFactory(bootstrapConfig, StagePrimary))

ctx.RegisterModuleType("blueprint_go_binary", newGoBinaryModuleFactory(bootstrapConfig, StageMain))

ctx.RegisterTopDownMutator("bootstrap_stage", propagateStageBootstrap)

ctx.RegisterSingletonType("bootstrap", newSingletonFactory(bootstrapConfig))

上面这些对应的是blueprint的第一步,将bootstrap_core_go_binary注册成modulefactory到config中去,具体的module结构如下:

d357345e2bb0

在这个过程中还有几个注册要注意一下RegisterBottomUpMutator和RegisterTopDownMutator,从这两个函数的字面意思看一个是TopDown从上往下,一个是BootomUP从下往上,初步推断TopDown可能是我们解决moudle依赖,从上往下定义我们的mutator(变异),也就是我们的特殊规则,“RegisterTopDownMutator registers a mutator that will be invoked to propagate dependency info top-down between Modules. Each registered mutator is invoked in registration order (mixing TopDownMutators and BottomUpMutators) once per Module, and the invocation on any module willhave returned before it is in invoked on any of its dependencies.”从解释看应该是处理依赖规则而产生的。而BottomUP,从函数的解释来看 “ RegisterBottomUpMutator registers a mutator that will be invoked to split Modules into variants. Each registered mutator is invoked in registration order (mixing TopDownMutators and BottomUpMutators) once per Module, will not be invoked on a module until the invocations on all of the modules dependencies have returned.”比如在Android.bp中大名鼎鼎的arch就属于BotoomUP mutator,在arch使用中可以根据需要编译成不同的指令,这个应该是针对一个module分成几个目标时使用。还有一个RegisterSingletonType,这个主要是在生成build规则时使用 “RegisterSingletonType registers a singleton type that will be invoked to generate build actions. Each registered singleton type is instantiated and and invoked exactly once as part of the generate phase. Each registered singleton is invoked in registration order.”需要实现GenerateBuildActions,用来生成具体的规则

第二步 分析Blueprint文件

deps, errs := ctx.ParseBlueprintsFiles(bootstrapConfig.topLevelBlueprintsFile)

这个函数的主要作用是分析blueprint文件,为每个模块创建var module *moduleInfo,最后通过newErrs := c.addModule(module)添加到config中去。addModule主要实现如下,可以看到

name := module.logicModule.Name()

c.moduleInfo[module.logicModule] = module

group := &moduleGroup{

name:      name,

ninjaName: ninjaName,

modules:  []*moduleInfo{module},

}

module.group = group

c.moduleNames[name] = group

c.moduleNinjaNames[ninjaName] = group

c.moduleGroups = append(c.moduleGroups, group)

第三步: 分析依赖

ResolveDependencies的主要作用是分析依赖的有效性,不要有循环依赖,runMutators运行之前注册的mutators

func (c *Context) ResolveDependencies(config interface{}) []error {

errs := c.updateDependencies()

errs = c.runMutators(config)

c.cloneModules()

c.dependenciesReady = true

return nil

}

这个函数包含三个调用,updateDependencies()这个函数主要是编译每个module以及其依赖,检查是否有循环依赖,如果有循环报错。

在moduleinfo中通过一个双向链表,建立之间的依赖关系

d357345e2bb0

第二个调用就是runMutators,这个比较重要

func (c *Context) runMutators(config interface{}) (errs []error) {

for _, mutator := range mutators {

if mutator.topDownMutator != nil {

errs = c.runMutator(config, mutator, topDownMutator)

} else if mutator.bottomUpMutator != nil {

errs = c.runMutator(config, mutator, bottomUpMutator)

} else {

}

主要是调用topDownMutator和bottomUpMutator。这个比较难理解但是可以通过举例来帮助理解,在Android.bp中“arch”属于bootomupmutator,通过规则将目标分成几个variants。 而topDownMutator则是从上往下依照dep访问各个模块并运行对应规则。

第三个调用是cloneModules(),为每个module创建备份,这个比较简单。

第四步:生成规则

生成规则函数在PrepareBuildActions()中,其中规则的生成主要是由下面这个函数生成

在generateModuleBuildActions中,通过调用具体的Module里面的mctx.module.logicModule.GenerateBuildActions(mctx)生成具体的规则,比较C语言 go语言的编译规则等。

第五步: 生成目标文件(ninja文件)

最后一步就是写ninjia文件,这个比较简单,具体代码如下

buf := bytes.NewBuffer(nil)

err := ctx.WriteBuildFile(buf)

if err != nil {

fatalf("error generating Ninja file contents: %s", err)

}

const outFilePermissions = 0666

err = ioutil.WriteFile(outFile, buf.Bytes(), outFilePermissions)

if err != nil {

fatalf("error writing %s: %s", outFile, err)

}

至此,一个blueprintfile到ninja文件就生成了,我们可以使用ninja对我们的工程进行编译了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值