? ?前面的文檔已經(jīng)大體介紹了blueprint苟呐,本篇則通過代碼分析blueprint讓大家更清楚的了解blueprint以及其語法讲弄。以及基于blueprint的語法生成的minibp和soong_build断医。
? blueprint主要是解析Blueprint文件翻譯成ninja語法文件萌腿。以build/blueprint/Blueprints文件內(nèi)容為例嘉竟,我們要解析的語法如下:
bootstrap_core_go_binary(
? ? name = "bpglob",
? ? deps = ["blueprint-pathtools"],
? ? srcs = ["bootstrap/bpglob/bpglob.go"],
)
生成的對應(yīng)的ninja文件如下:
? 可以看到最終策劃你改成的bpglob需要三部生成蓄坏,第一部編譯成bpblob.a其中依賴于bluprint-pathtools和bpglob.go,第二部是連接成a.out,第三部通過cp到對應(yīng)的bin的目錄下枫慷,完成一個module的解析让蕾。
? 解析一個Blueprint文件需要三步:
?第一步:注冊module也就是bootstrap_core_go_binary和內(nèi)容處理規(guī)則,如deps等或听。
? 第二步:解析Blueprint文件探孝,進行語法分析
? 第三步:分析module之間的依賴規(guī)則
? 第四步: 規(guī)則生成
? 第五步: 寫ninjia文件完成文件生成
第一步:注冊
? 整個解析過程需要一個context結(jié)構(gòu)體進行跟蹤,所以minibp和soong_build的入口都是如下誉裆,創(chuàng)建一個context
在build/blueprint/bootstrap/bootstrap.go下
ctx := blueprint.NewContext()
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),
fs:? ? ? ? ? ? ? pathtools.OsFs,
}
ctx.RegisterBottomUpMutator("blueprint_deps", blueprintDepsMutator)
return ctx
}
moduleFactories: 存儲著我們后面注冊的module如“bootstrap_core_go_binary”
moduleNames:
moduleInfo:
moduleNinjaNames:
globs: glob文件
下一步進入build/blueprint/bootstrap/command.go的Main中
? ? 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))
上面這些對應(yīng)的是blueprint的第一步顿颅,將bootstrap_core_go_binary注冊成modulefactory到config中去,具體的module結(jié)構(gòu)如下:
? ? ? 在這個過程中還有幾個注冊要注意一下RegisterBottomUpMutator和RegisterTopDownMutator足丢,從這兩個函數(shù)的字面意思看一個是TopDown從上往下粱腻,一個是BootomUP從下往上,初步推斷TopDown可能是我們解決moudle依賴斩跌,從上往下定義我們的mutator(變異)绍些,也就是我們的特殊規(guī)則,“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.”從解釋看應(yīng)該是處理依賴規(guī)則而產(chǎn)生的耀鸦。而BottomUP柬批,從函數(shù)的解釋來看 “ 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使用中可以根據(jù)需要編譯成不同的指令袖订,這個應(yīng)該是針對一個module分成幾個目標時使用氮帐。還有一個RegisterSingletonType,這個主要是在生成build規(guī)則時使用 “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.”需要實現(xiàn)GenerateBuildActions洛姑,用來生成具體的規(guī)則
第二步 分析Blueprint文件
deps, errs := ctx.ParseBlueprintsFiles(bootstrapConfig.topLevelBlueprintsFile)
這個函數(shù)的主要作用是分析blueprint文件上沐,為每個模塊創(chuàng)建var module *moduleInfo,最后通過newErrs := c.addModule(module)添加到config中去楞艾。addModule主要實現(xiàn)如下参咙,可以看到
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的主要作用是分析依賴的有效性,不要有循環(huán)依賴产徊,runMutators運行之前注冊的mutators
func (c *Context) ResolveDependencies(config interface{}) []error {
errs := c.updateDependencies()
errs = c.runMutators(config)
c.cloneModules()
c.dependenciesReady = true
return nil
}
這個函數(shù)包含三個調(diào)用昂勒,updateDependencies()這個函數(shù)主要是編譯每個module以及其依賴,檢查是否有循環(huán)依賴舟铜,如果有循環(huán)報錯。
在moduleinfo中通過一個雙向鏈表奠衔,建立之間的依賴關(guān)系
第二個調(diào)用就是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 {
}
主要是調(diào)用topDownMutator和bottomUpMutator塘娶。這個比較難理解但是可以通過舉例來幫助理解,在Android.bp中“arch”屬于bootomupmutator痊夭,通過規(guī)則將目標分成幾個variants刁岸。 而topDownMutator則是從上往下依照dep訪問各個模塊并運行對應(yīng)規(guī)則。
第三個調(diào)用是cloneModules()她我,為每個module創(chuàng)建備份虹曙,這個比較簡單。
第四步:生成規(guī)則
生成規(guī)則函數(shù)在PrepareBuildActions()中番舆,其中規(guī)則的生成主要是由下面這個函數(shù)生成
depsModules, errs := c.generateModuleBuildActions(config, liveGlobals)
在generateModuleBuildActions中酝碳,通過調(diào)用具體的Module里面的mctx.module.logicModule.GenerateBuildActions(mctx)生成具體的規(guī)則,比較C語言 go語言的編譯規(guī)則等恨狈。
第五步: 生成目標文件(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對我們的工程進行編譯了。