Android soong 代碼分析

一簡介

之前有android blueprint分析然走,soong只不過對blueprint進(jìn)行了擴(kuò)展携兵,可以識別各種Android.bp文件以及各種模塊辅斟,剛開始relase的soong只支持c/c++靖诗,后面跟著9.0支持 java/kotlin python徊都,各種新的特性加入沪斟。

二 soong_build代碼分析

soong_java.png

從根目錄的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, &params)
    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

soong_ninja.png

在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)容是已有屬性

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末省古,一起剝皮案震驚了整個(gè)濱河市粥庄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌豺妓,老刑警劉巖惜互,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件布讹,死亡現(xiàn)場離奇詭異,居然都是意外死亡训堆,警方通過查閱死者的電腦和手機(jī)描验,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蔫慧,“玉大人挠乳,你說我怎么就攤上這事」枚悖” “怎么了睡扬?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長黍析。 經(jīng)常有香客問我卖怜,道長,這世上最難降的妖魔是什么阐枣? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任马靠,我火速辦了婚禮,結(jié)果婚禮上蔼两,老公的妹妹穿的比我還像新娘甩鳄。我一直安慰自己,他們只是感情好额划,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布妙啃。 她就那樣靜靜地躺著,像睡著了一般俊戳。 火紅的嫁衣襯著肌膚如雪揖赴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天抑胎,我揣著相機(jī)與錄音燥滑,去河邊找鬼。 笑死阿逃,一個(gè)胖子當(dāng)著我的面吹牛铭拧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播盆昙,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼羽历,長吁一口氣:“原來是場噩夢啊……” “哼淡喜!你這毒婦竟也來了澎嚣?” 一聲冷哼從身側(cè)響起褥琐,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤造寝,失蹤者是張志新(化名)和其女友劉穎诫龙,沒想到半個(gè)月后签赃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體歹嘹,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡史飞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年陨簇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了己单。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蔓涧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鸠姨,我是刑警寧澤享怀,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站鳞贷,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏咱筛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一撤蟆、第九天 我趴在偏房一處隱蔽的房頂上張望家肯。 院中可真熱鬧扒披,春花似錦颇蜡、人聲如沸鳖目。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽领迈。三九已至,卻和暖如春碍沐,著一層夾襖步出監(jiān)牢的瞬間狸捅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工累提, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留尘喝,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓斋陪,卻偏偏與公主長得像朽褪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子无虚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容