一簡介
之前有android blueprint分析然走,soong只不過對blueprint進(jìn)行了擴(kuò)展携兵,可以識別各種Android.bp文件以及各種模塊辅斟,剛開始relase的soong只支持c/c++靖诗,后面跟著9.0支持 java/kotlin python徊都,各種新的特性加入沪斟。
二 soong_build代碼分析
從根目錄的root.bp進(jìn)行解析,分析各個(gè)子目錄下的android.bp文件暇矫,最后輸出.ninja文件主之,soong_build是代碼的入口.
func main() {
flag.Parse()
// The top-level Blueprints file is passed as the first argument.
srcDir := filepath.Dir(flag.Arg(0))
ctx := android.NewContext()
ctx.Register()
configuration, err := android.NewConfig(srcDir, bootstrap.BuildDir)
if err != nil {
fmt.Fprintf(os.Stderr, "%s", err)
os.Exit(1)
}
if docFile != "" {
configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
}
ctx.SetNameInterface(newNameResolver(configuration))
ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
bootstrap.Main(ctx.Context, configuration, configuration.ConfigFileName, configuration.ProductVariablesFileName)
if docFile != "" {
writeDocs(ctx, docFile)
}
}
第一步創(chuàng)建context
之前也有介紹,context是整個(gè)bp到ninja文件的管理單元李根,控制著整個(gè)轉(zhuǎn)換過程
type Context struct {
// set at instantiation
moduleFactories map[string]ModuleFactory //支持的module類型槽奕,比如cc_library java_library
nameInterface NameInterface
moduleGroups []*moduleGroup //掃描后的Module結(jié)構(gòu)
moduleInfo map[Module]*moduleInfo //Module的哈希映射
modulesSorted []*moduleInfo //經(jīng)過mutator處理后的所有moduleinfo
preSingletonInfo []*singletonInfo
singletonInfo []*singletonInfo
mutatorInfo []*mutatorInfo //bootup topdown,mutator存儲
earlyMutatorInfo []*mutatorInfo //注冊的early mutator
variantMutatorNames []string
depsModified uint32 // positive if a mutator modified the dependencies
dependenciesReady bool // set to true on a successful ResolveDependencies
buildActionsReady bool // set to true on a successful PrepareBuildActions
// set by SetIgnoreUnknownModuleTypes
ignoreUnknownModuleTypes bool
// set by SetAllowMissingDependencies
allowMissingDependencies bool
// set during PrepareBuildActions
pkgNames map[*packageContext]string
liveGlobals *liveTracker
globalVariables map[Variable]*ninjaString
globalPools map[Pool]*poolDef
globalRules map[Rule]*ruleDef
// set during PrepareBuildActions
ninjaBuildDir *ninjaString // The builddir special Ninja variable
requiredNinjaMajor int // For the ninja_required_version variable
requiredNinjaMinor int // For the ninja_required_version variable
requiredNinjaMicro int // For the ninja_required_version variable
// set lazily by sortedModuleGroups
cachedSortedModuleGroups []*moduleGroup
globs map[string]GlobPath
globLock sync.Mutex
fs pathtools.FileSystem
moduleListFile string
}
然后執(zhí)行Register(),比如一個(gè)cc_library是一個(gè)module朱巨,那么需要提前將其注冊到blueprint中去史翘,以便識別。而moduleTypes 包含的支持的module類型是在init中體檢注冊好的,比如在
func init() {
android.RegisterModuleType("cc_library_static", LibraryStaticFactory)
android.RegisterModuleType("cc_library_shared", LibrarySharedFactory)
android.RegisterModuleType("cc_library", LibraryFactory)
android.RegisterModuleType("cc_library_host_static", LibraryHostStaticFactory)
android.RegisterModuleType("cc_library_host_shared", LibraryHostSharedFactory)
android.RegisterModuleType("cc_library_headers", LibraryHeaderFactory)
}
func (ctx *Context) Register() {
for _, t := range preSingletons {
ctx.RegisterPreSingletonType(t.name, t.factory)
}
for _, t := range moduleTypes {
ctx.RegisterModuleType(t.name, t.factory)
}
for _, t := range singletons {
ctx.RegisterSingletonType(t.name, t.factory)
}
registerMutators(ctx.Context, preArch, preDeps, postDeps)
ctx.RegisterSingletonType("env", SingletonFactoryAdaptor(EnvSingleton))
}
因?yàn)閕nit是在main之前執(zhí)行的琼讽,所以這些Module都是提前注冊號的必峰,在main進(jìn)入后,通過RegisterModuleType注冊到blueprint中去即可钻蹬,放到moduleFactories中去還有一個(gè)重要的步驟是registerMutators吼蚁,比如我們在java_library中,有static_libs问欠,jar包庫依賴肝匆,需要建立兩個(gè)庫之間的依賴關(guān)系,就需要在mutator掃描每個(gè)module中處理,后面的runMutators中會做介紹
比如我們要將一個(gè)java_library通過blueprint分析后顺献,轉(zhuǎn)換成將java文件通過對應(yīng)的javac進(jìn)行編譯語句旗国,javac命令的設(shè)置也是在init中設(shè)置好的,后面生成中會
pctx.SourcePathVariable("JavaToolchain", "${JavaHome}/bin")
pctx.SourcePathVariableWithEnvOverride("JavacCmd",
"${JavaToolchain}/javac", "ALTERNATE_JAVAC")
pctx.SourcePathVariable("JavaCmd", "${JavaToolchain}/java")
pctx.SourcePathVariable("JarCmd", "${JavaToolchain}/jar")
pctx.SourcePathVariable("JavadocCmd", "${JavaToolchain}/javadoc")
pctx.SourcePathVariable("JlinkCmd", "${JavaToolchain}/jlink")
pctx.SourcePathVariable("JmodCmd", "${JavaToolchain}/jmod")
pctx.SourcePathVariable("JrtFsJar", "${JavaHome}/lib/jrt-fs.jar")
pctx.SourcePathVariable("Ziptime", "prebuilts/build-tools/${hostPrebuiltTag}/bin/ziptime")
packageContext 保存了我們要設(shè)置的編譯參數(shù)和編譯使用的編譯命令等注整,以便后面的rule使用能曾。后面會把這些變量解析卸載ninja文件的前面,作為變量使用肿轨。比如我們要使用java的話寿冕,需要定義javac以及其他一些編譯參數(shù),支持cc的話需要定義clang的命令以及clang編譯中使用的參數(shù)椒袍。其他語言python也是同樣的原理
type basicScope struct {
parent *basicScope
variables map[string]Variable
pools map[string]Pool
rules map[string]Rule
imports map[string]*basicScope
}
type PackageContext interface {
Import(pkgPath string)
ImportAs(as, pkgPath string)
StaticVariable(name, value string) Variable
VariableFunc(name string, f func(config interface{}) (string, error)) Variable
VariableConfigMethod(name string, method interface{}) Variable
StaticPool(name string, params PoolParams) Pool
PoolFunc(name string, f func(interface{}) (PoolParams, error)) Pool
StaticRule(name string, params RuleParams, argNames ...string) Rule
RuleFunc(name string, f func(interface{}) (RuleParams, error), argNames ...string) Rule
AddNinjaFileDeps(deps ...string)
getScope() *basicScope
}
type packageContext struct {
fullName string
shortName string
pkgPath string
scope *basicScope
ninjaFileDeps []string
}
javac和kotlinc作為參數(shù)在Build中傳入驼唱,并將參數(shù)展開,最后變成一條條具體的ninja命令
javac = pctx.AndroidGomaStaticRule("javac",
blueprint.RuleParams{
Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
`${config.SoongJavacWrapper} ${config.JavacWrapper}${config.JavacCmd} ${config.JavacHeapFlags} ${config.CommonJdkFlags} ` +
`$javacFlags $bootClasspath $classpath ` +
`-source $javaVersion -target $javaVersion ` +
`-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list && ` +
`${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
CommandDeps: []string{
"${config.JavacCmd}",
"${config.SoongZipCmd}",
"${config.ZipSyncCmd}",
},
CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
Rspfile: "$out.rsp",
RspfileContent: "$in",
},
"javacFlags", "bootClasspath", "classpath", "srcJars", "srcJarDir",
"outDir", "annoDir", "javaVersion")
kotlinc = pctx.AndroidGomaStaticRule("kotlinc",
blueprint.RuleParams{
Command: `rm -rf "$outDir" "$srcJarDir" && mkdir -p "$outDir" "$srcJarDir" && ` +
`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
`${config.GenKotlinBuildFileCmd} $classpath $outDir $out.rsp $srcJarDir/list > $outDir/kotlinc-build.xml &&` +
`${config.KotlincCmd} $kotlincFlags ` +
`-jvm-target $kotlinJvmTarget -Xbuild-file=$outDir/kotlinc-build.xml && ` +
`${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
CommandDeps: []string{
"${config.KotlincCmd}",
"${config.KotlinCompilerJar}",
"${config.GenKotlinBuildFileCmd}",
"${config.SoongZipCmd}",
"${config.ZipSyncCmd}",
},
Rspfile: "$out.rsp",
RspfileContent: `$in`,
},
"kotlincFlags", "classpath", "srcJars", "srcJarDir", "outDir", "kotlinJvmTarget")
上面完成后context建立完畢驹暑,后面進(jìn)入bootstrap.Main后玫恳,具體的分析bp文件。
第二步: parseFileList()
通過第一部后建立Context.moduleInfo對Module的哈希映射
c.moduleInfo[module.logicModule] = module
添加module到group中
group := &moduleGroup{
name: name,
modules: []*moduleInfo{module},
}
module.group = group
c.moduleGroups = append(c.moduleGroups, group)
并且按照能訪問的次序重新進(jìn)行排列
c.modulesSorted = sorted
第三步:resolveDependencies()
從圖中可以看出岗钩,主要執(zhí)行四個(gè)步驟纽窟,其中updateDependencies()執(zhí)行了三次,updateDependencies根據(jù)module之間的directDeps 和module.group.modules 建立module之間的direct和reverse之間的關(guān)系兼吓,并且根據(jù)依賴關(guān)系排序臂港。后面
type TopDownMutatorContext interface {
BaseModuleContext
androidBaseContext
OtherModuleExists(name string) bool
Rename(name string)
Module() Module
OtherModuleName(m blueprint.Module) string
OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{})
OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
CreateModule(blueprint.ModuleFactory, ...interface{})
GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module
GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
VisitDirectDeps(visit func(Module))
VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module))
VisitDirectDepsIf(pred func(Module) bool, visit func(Module))
VisitDepsDepthFirst(visit func(Module))
VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module))
WalkDeps(visit func(Module, Module) bool)
}
type BottomUpMutatorContext interface {
BaseModuleContext
androidBaseContext
OtherModuleExists(name string) bool
Rename(name string)
Module() blueprint.Module
AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string)
AddReverseDependency(module blueprint.Module, tag blueprint.DependencyTag, name string)
CreateVariations(...string) []blueprint.Module
CreateLocalVariations(...string) []blueprint.Module
SetDependencyVariation(string)
AddVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string)
AddFarVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string)
AddInterVariantDependency(tag blueprint.DependencyTag, from, to blueprint.Module)
ReplaceDependencies(string)
}
上面是topdown和bottomup支持的func
func (c *Context) runMutators(config interface{}) (deps []string, errs []error) {
var mutators []*mutatorInfo
mutators = append(mutators, c.earlyMutatorInfo...)
mutators = append(mutators, c.mutatorInfo...)
for _, mutator := range mutators {
var newDeps []string
if mutator.topDownMutator != nil {
newDeps, errs = c.runMutator(config, mutator, topDownMutator)
} else if mutator.bottomUpMutator != nil {
newDeps, errs = c.runMutator(config, mutator, bottomUpMutator)
} else {
panic("no mutator set on " + mutator.name)
}
if len(errs) > 0 {
return nil, errs
}
deps = append(deps, newDeps...)
}
return deps, nil
}
type BottomUpMutator func(mctx BottomUpMutatorContext)
按照依次注冊的mutators,判斷執(zhí)行topDownMutator還是bootomUpmutator视搏,在runMutator审孽,
direction.orderer().visit(c.modulesSorted, visit)
從數(shù)組的結(jié)尾到數(shù)組的開始,依次執(zhí)行注冊的topdownmutator
func (topDownVisitorImpl) visit(modules []*moduleInfo, visit func(*moduleInfo) bool) {
for i := 0; i < len(modules); i++ {
module := modules[len(modules)-1-i]
if visit(module) {
return
}
}
}
從數(shù)組的0開始到數(shù)組的結(jié)尾浑娜,依次執(zhí)行注冊的bottomupmuator
func (bottomUpVisitorImpl) visit(modules []*moduleInfo, visit func(*moduleInfo) bool) {
for _, module := range modules {
if visit(module) {
return
}
}
}
通過updateDependencies佑力,現(xiàn)將module依賴的dep添加到sorted中去,最后放入的是最終的Module也就是root頭的module筋遭,然后看bottomup和topdown算法打颤,topdown從末尾開始暴拄,也就是最頂上的目標(biāo)依賴開始,而bottomup則是從最底下的依賴開始编饺。如果先從module的依賴開始處理的話使用bottomup乖篷,如果從module的目標(biāo)頭開始的話使用topdown算法
比如我們的java_library 中的static_libs是依賴的jar類
func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) {
j.deps(ctx)
}
func (j *Module) deps(ctx android.BottomUpMutatorContext) {
ctx.AddDependency(ctx.Module(), libTag, j.properties.Libs...)
ctx.AddDependency(ctx.Module(), staticLibTag, j.properties.Static_libs...)
ctx.AddDependency(ctx.Module(), libTag, j.properties.Annotation_processors...)
}
第四步: prepareBuildActions()
PrepareBuildActions主要是調(diào)用generateModuleBuildActions
c.parallelVisit(bottomUpVisitor, func(module *moduleInfo) bool {
}
func (c *Context) parallelVisit(order visitOrderer, visit func(group *moduleInfo) bool) {
}
func (bottomUpVisitorImpl) visit(modules []*moduleInfo, visit func(*moduleInfo) bool) {
for _, module := range modules {
if visit(module) {
return
}
}
}
最終調(diào)用java_library的 GenerateAndroidBuildActions
func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) {
j.compile(ctx)
if j.installable() {
j.installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
ctx.ModuleName()+".jar", j.outputFile)
}
}
以kotlin為例,將一個(gè)目標(biāo)通過ctx.Build翻譯成ninjia文件
func TransformKotlinToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
srcFiles, srcJars android.Paths,
flags javaBuilderFlags) {
inputs := append(android.Paths(nil), srcFiles...)
var deps android.Paths
deps = append(deps, flags.kotlincClasspath...)
deps = append(deps, srcJars...)
ctx.Build(pctx, android.BuildParams{
Rule: kotlinc,
Description: "kotlinc",
Output: outputFile,
Inputs: inputs,
Implicits: deps,
Args: map[string]string{
"classpath": flags.kotlincClasspath.FormJavaClassPath("-classpath"),
"kotlincFlags": flags.kotlincFlags,
"srcJars": strings.Join(srcJars.Strings(), " "),
"outDir": android.PathForModuleOut(ctx, "kotlinc", "classes").String(),
"srcJarDir": android.PathForModuleOut(ctx, "kotlinc", "srcJars").String(),
// http://b/69160377 kotlinc only supports -jvm-target 1.6 and 1.8
"kotlinJvmTarget": "1.8",
},
})
}
ctx.Build主要調(diào)用parseBuildParams提取參數(shù)將生成最終的ninja文件
func (m *moduleContext) Build(pctx PackageContext, params BuildParams) {
m.scope.ReparentTo(pctx)
def, err := parseBuildParams(m.scope, ¶ms)
if err != nil {
panic(err)
}
m.actionDefs.buildDefs = append(m.actionDefs.buildDefs, def)
}
經(jīng)過parseBuildParams 解析參數(shù)生成buildDef
// A buildDef describes a build target definition.
type buildDef struct {
Comment string
Rule Rule
RuleDef *ruleDef
Outputs []*ninjaString
ImplicitOutputs []*ninjaString
Inputs []*ninjaString
Implicits []*ninjaString
OrderOnly []*ninjaString
Args map[Variable]*ninjaString
Variables map[string]*ninjaString
Optional bool
}
在后面的writeBuildFile中調(diào)用buildDef的WriteTo將一條命令生成成ninja文件
func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*packageContext]string) error {
第五步:writeBuildFile()
經(jīng)過前面的分析后透且,生成最終的ninja文件在writeBuildFile中完成
func (c *Context) WriteBuildFile(w io.Writer) error {
if !c.buildActionsReady {
return ErrBuildActionsNotReady
}
nw := newNinjaWriter(w)
err := c.writeBuildFileHeader(nw)
if err != nil {
return err
}
err = c.writeNinjaRequiredVersion(nw)
if err != nil {
return err
}
// TODO: Group the globals by package.
err = c.writeGlobalVariables(nw)
if err != nil {
return err
}
err = c.writeGlobalPools(nw)
if err != nil {
return err
}
err = c.writeBuildDir(nw)
if err != nil {
return err
}
err = c.writeGlobalRules(nw)
if err != nil {
return err
}
err = c.writeAllModuleActions(nw)
if err != nil {
return err
}
err = c.writeAllSingletonActions(nw)
if err != nil {
return err
}
return nil
}
writeAllModuleActions 和 writeAllSingletonActions 會調(diào)用上面的def.WriteTo寫rule到ninja語句下面是一個(gè)ninja文件內(nèi)容
ninja_required_version = 1.7.0
g.android.soong.cc.config.Arm64Cflags = -Werror=implicit-function-declaration
g.android.soong.cc.config.Arm64ClangArmv8ACflags = -march=armv8-a
g.android.soong.cc.config.Arm64ClangCflags = -Werror=implicit-function-declaration
g.android.soong.cc.config.Arm64ClangCppflags =
g.android.soong.cc.config.Arm64Cppflags =
g.android.soong.cc.config.HostPrebuiltTag = linux-x86
g.android.soong.cc.config.arm64GccVersion = 4.9
g.android.soong.java.config.KotlincCmd = external/kotlinc/bin/kotlinc
rule g.java.javac
command = rm -rf "${outDir}" "${annoDir}" "${srcJarDir}" && mkdir -p "${outDir}" "${annoDir}" "${srcJarDir}" && ${g.android.soong.java.config.ZipSyncCmd}
-d ${srcJarDir} -l ${srcJarDir}/list -f "*.java" ${srcJars} && ${g.android.soong.java.config.SoongJavacWrapper}
${g.android.soong.java.config.JavacWrapper}${g.android.soong.java.config.JavacCmd} ${g.android.soong.java.config.JavacHeapFlags}
${g.android.soong.java.config.CommonJdkFlags} ${javacFlags} ${bootClasspath} ${classpath} -source ${javaVersion} -target ${javaVersion} -d ${outDir} -s
${annoDir} @${out}.rsp @${srcJarDir}/list && ${g.android.soong.java.config.SoongZipCmd} -jar -o ${out} -C ${outDir} -D ${outDir}
rspfile = ${out}.rsp
rspfile_content = ${in}
build $
out/soong/.intermediates/frameworks/base/framework/android_common/javac/framework.jar12 $
: g.java.javac $
frameworks/base/core/java/android/view/RemotableViewMethod.java $
frameworks/base/core/java/android/view/RemoteAnimationAdapter.java $
frameworks/base/core/java/android/view/RemoteAnimationDefinition.java $
....
description = ${m.BugReport_linux_glibc_common.moduleDesc}javac${m.BugReport_linux_glibc_common.moduleDescSuffix}
annoDir = out/soong/.intermediates/development/tools/bugreport/BugReport/linux_glibc_common/javac/anno
bootClasspath = -bootclasspath prebuilts/jdk/jdk8/linux-x86/jre/lib/jce.jar:prebuilts/jdk/jdk8/linux-x86/jre/lib/rt.jar
classpath = -classpath out/soong/.intermediates/external/jsilver/jsilver/linux_glibc_common/combined/jsilver.jar
javaVersion = 1.8
javacFlags =
outDir = out/soong/.intermediates/development/tools/bugreport/BugReport/linux_glibc_common/javac/classes
srcJarDir = out/soong/.intermediates/development/tools/bugreport/BugReport/linux_glibc_common/javac/srcjars
三添加自定義的類型
如果我們要實(shí)現(xiàn)自己定義的module撕蔼,比如支持新的語言的話,借鑒java的實(shí)現(xiàn)
定義一個(gè)新的module
type Module struct {
android.ModuleBase //包含android.ModuleBase
}
實(shí)現(xiàn)
func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
}
func (g *Module) DepsMutator(ctx android.BottomUpMutatorContext) {
}
在init注冊
func init() {
android.RegisterModuleType("java_library", javalibFactory)
}
四 soong傳輸變量給makefile
在resolveDependencies步驟中,首先執(zhí)行g(shù)enerateSingletonBuildActions(config, c.preSingletonInfo, c.liveGlobals) ,在PrepareBuildActions最后執(zhí)行 c.generateSingletonBuildActions(config, c.singletonInfo, c.liveGlobals)秽誊,后者依次執(zhí)行注冊的singleton中的GenerateBuildActions,info.singleton.GenerateBuildActions(sctx)鲸沮。在out/soong的目錄下有make_vars-.mk,這個(gè)里面定義了編譯使用的各種變量锅论,此mk會被外部的mk引用讼溺,主要是驗(yàn)證當(dāng)前使用的 javac clang的是否與外部的版本匹配如無變量則定義變量,如果不匹配的話會報(bào)warning最易。而make_vars-.mk makeVarsSingleton注冊成singleton,其中provider.call(mctx) 是在最后執(zhí)行的,也就是makeVarsProvider 執(zhí)行中可以搜集soong產(chǎn)生的中間變量比如vndkSpLibraries 供makefile使用
func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) {
if !ctx.Config().EmbeddedInMake() {
return
}
outFile := PathForOutput(ctx, "make_vars"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String()
if ctx.Failed() {
return
}
vars := []makeVarsVariable{}
for _, provider := range makeVarsProviders {
mctx := &makeVarsContext{
config: ctx.Config(),
ctx: ctx,
pctx: provider.pctx,
}
provider.call(mctx)
vars = append(vars, mctx.vars...)
}
比如我們在build/soong/java/config/makevars.go中,將TARGET_DEFAULT_JAVA_LIBRARIES參數(shù)注冊到makeVarsProviders中肾胯,通過makeVarsSingleton寫入到make_vars中進(jìn)行參數(shù)比對
func init() {
android.RegisterMakeVarsProvider(pctx, makeVarsProvider)
}
func makeVarsProvider(ctx android.MakeVarsContext) {
ctx.Strict("TARGET_DEFAULT_JAVA_LIBRARIES", strings.Join(DefaultLibraries, " "))
ctx.Strict("TARGET_DEFAULT_BOOTCLASSPATH_LIBRARIES", strings.Join(DefaultBootclasspathLibraries, " "))
ctx.Strict("DEFAULT_SYSTEM_MODULES", DefaultSystemModules)
if ctx.Config().TargetOpenJDK9() {
ctx.Strict("DEFAULT_JAVA_LANGUAGE_VERSION", "1.9")
} else {
ctx.Strict("DEFAULT_JAVA_LANGUAGE_VERSION", "1.8")
make_vars-*.mk內(nèi)容如下:
SOONG_MIN_SUPPORTED_SDK_VERSION := 14
$(eval $(call soong-compare-var,MIN_SUPPORTED_SDK_VERSION,,my_check_failed := true))
soong-compare-var如果無變量定義,則定義變量同soong參數(shù)內(nèi)容相同耘纱,如果兩個(gè)不同的話,則打印warning
define soong-compare-var
ifneq ($$($(1)),)
my_val_make := $$(strip $(if $(2),$$(sort $$($(1))),$$($(1))))
my_val_soong := $(if $(2),$$(sort $$(SOONG_$(1))),$$(SOONG_$(1)))
ifneq ($$(my_val_make),$$(my_val_soong))
$$(warning $(1) does not match between Make and Soong:)
$(if $(2),$$(warning Make adds: $$(filter-out $$(my_val_soong),$$(my_val_make))),$$(warning Make : $$(my_val_make)))
$(if $(2),$$(warning Soong adds: $$(filter-out $$(my_val_make),$$(my_val_soong))),$$(warning Soong: $$(my_val_soong)))
$(3)
endif
my_val_make :=
my_val_soong :=
else
$(1) := $$(SOONG_$(1))
endif
.KATI_READONLY := $(1) SOONG_$(1)
endef
其實(shí)這種機(jī)制是在soong中定義變量毕荐,同時(shí)傳輸?shù)絤akefile中使用
五 添加條件編譯到bp
方法一
bp文件不能條件編譯束析,但是有些情況下又不得不要使用條件編譯,就要使用到android.AddLoadHook
func AddLoadHook(m blueprint.Module, hook func(LoadHookContext)) {
h := &m.(Module).base().hooks
h.load = append(h.load, hook)
}
func loadHookMutator(ctx TopDownMutatorContext) {
if m, ok := ctx.Module().(Module); ok {
// Cast through *androidTopDownMutatorContext because AppendProperties is implemented
// on *androidTopDownMutatorContext but not exposed through TopDownMutatorContext
var loadHookCtx LoadHookContext = ctx.(*androidTopDownMutatorContext)
m.base().hooks.runLoadHooks(loadHookCtx, m.base())
}
}
func (x *hooks) runLoadHooks(ctx LoadHookContext, m *ModuleBase) {
if len(x.load) > 0 {
for _, x := range x.load {
x(ctx)
if ctx.Failed() {
return
}
}
}
}
var preArch = []RegisterMutatorFunc{
func(ctx RegisterMutatorsContext) {
ctx.TopDown("load_hooks", loadHookMutator).Parallel()
},
LoadHook作為topdown運(yùn)行憎亚,而且注冊靠前沒有先執(zhí)行,customLinker 判斷有無CUSTOM_TARGET_LINKER全局變量定義员寇,如果有的話,就添加到模塊的properties中去第美。
func artBinary() android.Module {
binary, _ := cc.NewBinary(android.HostAndDeviceSupported)
module := binary.Init()
android.AddLoadHook(module, customLinker)
android.AddLoadHook(module, prefer32Bit)
return module
}
func customLinker(ctx android.LoadHookContext) {
linker := envDefault(ctx, "CUSTOM_TARGET_LINKER", "")
type props struct {
DynamicLinker string
}
p := &props{}
if linker != "" {
p.DynamicLinker = linker
}
ctx.AppendProperties(p)
}
有一點(diǎn)要注意Properties添加需要原module有這個(gè)定義后才可以添加進(jìn)去蝶锋。可以通過設(shè)置CUSTOM_TARGET_LINKER來定義linker
方法二:
還有一種方法是通過makefile定義的變量傳輸?shù)絪oong中什往,soong根據(jù)變量條件執(zhí)行扳缕。具體方法:
在build/core/soong_config.mk中,有這么一個(gè)參數(shù)列表别威,比如如下:
$(call add_json_bool, Treble_linker_namespaces, $(filter true,$(PRODUCT_TREBLE_LINKER_NAMESPACES)))
如果定義PRODUCT_TREBLE_LINKER_NAMESPACES為true的話Treble_linker_namespaces也為true
上面定義的這些變量會被生成在out/soong/soong.variables中
{
"Treble_linker_namespaces": true,
}
soong在執(zhí)行時(shí)加載out/soong/soong.variables到productVariables 結(jié)構(gòu)體中去
同時(shí)在build/soong/android/variable.go 有兩處定義
type productVariables struct {
Treble_linker_namespaces *bool json:",omitempty"
}
這個(gè)是在module中定義躯舔,每個(gè)module都可以定義這個(gè)變量
type variableProperties struct {
Product_variables struct {
Treble_linker_namespaces struct {
Cflags []string
}
在build/soong/android/variable.go的variableMutator執(zhí)行過程中 如果參數(shù)值不同的話會將參數(shù)Cflags添加到generalProperties中去
這個(gè)方法要注意的是在variableProperties productVariables 以及soong_config.mk都要進(jìn)行定義,并且variableProperties 內(nèi)容是已有屬性