美文网首页
Android soong 代码分析

Android soong 代码分析

作者: Little熊猫 | 来源:发表于2018-06-26 12:53 被阅读0次

    一简介

    之前有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 内容是已有属性

    相关文章

      网友评论

          本文标题:Android soong 代码分析

          本文链接:https://www.haomeiwen.com/subject/wpifjftx.html