Android soong 代码分析

一简介

之前有android blueprint分析,soong只不过对blueprint进行了扩展,可以识别各种Android.bp文件以及各种模块,刚开始relase的soong只支持c/c++,后面跟着9.0支持 java/kotlin python,各种新的特性加入。

二 soong_build代码分析

soong_java.png

从根目录的root.bp进行解析,分析各个子目录下的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)
    }
}

第一步创建context
之前也有介绍,context是整个bp到ninja文件的管理单元,控制着整个转换过程

type Context struct {
    // set at instantiation
    moduleFactories     map[string]ModuleFactory  //支持的module类型,比如cc_library java_library
    nameInterface       NameInterface
    moduleGroups        []*moduleGroup     //扫描后的Module结构
    moduleInfo          map[Module]*moduleInfo  //Module的哈希映射
    modulesSorted       []*moduleInfo   //经过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
}

然后执行Register(),比如一个cc_library是一个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))
}

因为init是在main之前执行的,所以这些Module都是提前注册号的,在main进入后,通过RegisterModuleType注册到blueprint中去即可,放到moduleFactories中去还有一个重要的步骤是registerMutators,比如我们在java_library中,有static_libs,jar包库依赖,需要建立两个库之间的依赖关系,就需要在mutator扫描每个module中处理,后面的runMutators中会做介绍
比如我们要将一个java_library通过blueprint分析后,转换成将java文件通过对应的javac进行编译语句,javac命令的设置也是在init中设置好的,后面生成中会

    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 保存了我们要设置的编译参数和编译使用的编译命令等,以便后面的rule使用。后面会把这些变量解析卸载ninja文件的前面,作为变量使用。比如我们要使用java的话,需要定义javac以及其他一些编译参数,支持cc的话需要定义clang的命令以及clang编译中使用的参数。其他语言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作为参数在Build中传入,并将参数展开,最后变成一条条具体的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建立完毕,后面进入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)

并且按照能访问的次序重新进行排列
c.modulesSorted = sorted

第三步:resolveDependencies()
从图中可以看出,主要执行四个步骤,其中updateDependencies()执行了三次,updateDependencies根据module之间的directDeps 和module.group.modules 建立module之间的direct和reverse之间的关系,并且根据依赖关系排序。后面

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,判断执行topDownMutator还是bootomUpmutator,在runMutator,

direction.orderer().visit(c.modulesSorted, visit)

从数组的结尾到数组的开始,依次执行注册的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
        }
    }
}

从数组的0开始到数组的结尾,依次执行注册的bottomupmuator
func (bottomUpVisitorImpl) visit(modules []*moduleInfo, visit func(*moduleInfo) bool) {
    for _, module := range modules {
        if visit(module) {
            return
        }
    }
}

通过updateDependencies,现将module依赖的dep添加到sorted中去,最后放入的是最终的Module也就是root头的module,然后看bottomup和topdown算法,topdown从末尾开始,也就是最顶上的目标依赖开始,而bottomup则是从最底下的依赖开始。如果先从module的依赖开始处理的话使用bottomup,如果从module的目标头开始的话使用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主要是调用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
        }
    }
}

最终调用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为例,将一个目标通过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主要调用parseBuildParams提取参数将生成最终的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)
}

经过parseBuildParams 解析参数生成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中调用buildDef的WriteTo将一条命令生成成ninja文件

func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*packageContext]string) error { 

第五步:writeBuildFile()

经过前面的分析后,生成最终的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 会调用上面的def.WriteTo写rule到ninja语句下面是一个ninja文件内容

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

三添加自定义的类型

如果我们要实现自己定义的module,比如支持新的语言的话,借鉴java的实现
定义一个新的module
type Module struct {
android.ModuleBase //包含android.ModuleBase

}
实现
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步骤中,首先执行generateSingletonBuildActions(config, c.preSingletonInfo, c.liveGlobals) ,在PrepareBuildActions最后执行 c.generateSingletonBuildActions(config, c.singletonInfo, c.liveGlobals),后者依次执行注册的singleton中的GenerateBuildActions,info.singleton.GenerateBuildActions(sctx)。在out/soong的目录下有make_vars-.mk,这个里面定义了编译使用的各种变量,此mk会被外部的mk引用,主要是验证当前使用的 javac clang的是否与外部的版本匹配如无变量则定义变量,如果不匹配的话会报warning。而make_vars-.mk makeVarsSingleton注册成singleton,其中provider.call(mctx) 是在最后执行的,也就是makeVarsProvider 执行中可以搜集soong产生的中间变量比如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参数注册到makeVarsProviders中,通过makeVarsSingleton写入到make_vars中进行参数比对

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内容如下:

SOONG_MIN_SUPPORTED_SDK_VERSION := 14
$(eval $(call soong-compare-var,MIN_SUPPORTED_SDK_VERSION,,my_check_failed := true))

soong-compare-var如果无变量定义,则定义变量同soong参数内容相同,如果两个不同的话,则打印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

其实这种机制是在soong中定义变量,同时传输到makefile中使用

五 添加条件编译到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运行,而且注册靠前没有先执行,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)
}

有一点要注意Properties添加需要原module有这个定义后才可以添加进去。可以通过设置CUSTOM_TARGET_LINKER来定义linker

方法二:

还有一种方法是通过makefile定义的变量传输到soong中,soong根据变量条件执行。具体方法:
在build/core/soong_config.mk中,有这么一个参数列表,比如如下:

$(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在执行时加载out/soong/soong.variables到productVariables 结构体中去
同时在build/soong/android/variable.go 有两处定义

type productVariables struct {
Treble_linker_namespaces *bool json:",omitempty"
}
这个是在module中定义,每个module都可以定义这个变量
type variableProperties struct {
Product_variables struct {
Treble_linker_namespaces struct {
Cflags []string
}
在build/soong/android/variable.go的variableMutator执行过程中 如果参数值不同的话会将参数Cflags添加到generalProperties中去
这个方法要注意的是在variableProperties productVariables 以及soong_config.mk都要进行定义,并且variableProperties 内容是已有属性

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容