一简介
之前有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, ¶ms)
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 内容是已有属性
网友评论