Implement code-generation step for bp2build.
Implement bp2build codegen as a discrete step that runs after an
alternatively registered pipeline of mutators, instead of a
presingleton.
bp2build codegen requires a Context that supports VisitAllModules and
PathContext, so this CL also makes a BpToBuildWrapperContext that
conforms to PathContext by adding two method implementations.
Test: GENERATE_BAZEL_FILES=true m nothing && bazel query //... --config=bp2build | wc -l # 31433
Test: m queryview && bazel query //... --config=queryview # 63638
Change-Id: I0dd359746584b228046d2d0ff00895f28f9bdfc3
diff --git a/android/register.go b/android/register.go
index ca658f5..f84acad 100644
--- a/android/register.go
+++ b/android/register.go
@@ -37,9 +37,6 @@
var singletons []singleton
var preSingletons []singleton
-var bazelConverterSingletons []singleton
-var bazelConverterPreSingletons []singleton
-
type mutator struct {
name string
bottomUpMutator blueprint.BottomUpMutator
@@ -94,14 +91,6 @@
preSingletons = append(preSingletons, singleton{name, factory})
}
-func RegisterBazelConverterSingletonType(name string, factory SingletonFactory) {
- bazelConverterSingletons = append(bazelConverterSingletons, singleton{name, factory})
-}
-
-func RegisterBazelConverterPreSingletonType(name string, factory SingletonFactory) {
- bazelConverterPreSingletons = append(bazelConverterPreSingletons, singleton{name, factory})
-}
-
type Context struct {
*blueprint.Context
config Config
@@ -117,21 +106,20 @@
// singletons, module types and mutators to register for converting Blueprint
// files to semantically equivalent BUILD files.
func (ctx *Context) RegisterForBazelConversion() {
- for _, t := range bazelConverterPreSingletons {
- ctx.RegisterPreSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
- }
-
for _, t := range moduleTypes {
ctx.RegisterModuleType(t.name, ModuleFactoryAdaptor(t.factory))
}
- for _, t := range bazelConverterSingletons {
+ // Required for SingletonModule types, even though we are not using them.
+ for _, t := range singletons {
ctx.RegisterSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
}
registerMutatorsForBazelConversion(ctx.Context)
}
+// Register the pipeline of singletons, module types, and mutators for
+// generating build.ninja and other files for Kati, from Android.bp files.
func (ctx *Context) Register() {
for _, t := range preSingletons {
ctx.RegisterPreSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index 30f298a..49729e0 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -16,52 +16,34 @@
import (
"android/soong/android"
+ "fmt"
"os"
)
-// The Bazel bp2build singleton is responsible for writing .bzl files that are equivalent to
+// The Bazel bp2build code generator is responsible for writing .bzl files that are equivalent to
// Android.bp files that are capable of being built with Bazel.
-func init() {
- android.RegisterBazelConverterPreSingletonType("androidbp_to_build", AndroidBpToBuildSingleton)
-}
-
-func AndroidBpToBuildSingleton() android.Singleton {
- return &androidBpToBuildSingleton{
- name: "bp2build",
- }
-}
-
-type androidBpToBuildSingleton struct {
- name string
- outputDir android.OutputPath
-}
-
-func (s *androidBpToBuildSingleton) GenerateBuildActions(ctx android.SingletonContext) {
- s.outputDir = android.PathForOutput(ctx, s.name)
- android.RemoveAllOutputDir(s.outputDir)
-
- if !ctx.Config().IsEnvTrue("CONVERT_TO_BAZEL") {
- return
- }
+func Codegen(ctx CodegenContext) {
+ outputDir := android.PathForOutput(ctx, "bp2build")
+ android.RemoveAllOutputDir(outputDir)
ruleShims := CreateRuleShims(android.ModuleTypeFactories())
- buildToTargets := GenerateSoongModuleTargets(ctx)
+ buildToTargets := GenerateSoongModuleTargets(ctx.Context())
filesToWrite := CreateBazelFiles(ruleShims, buildToTargets)
for _, f := range filesToWrite {
- if err := s.writeFile(ctx, f); err != nil {
- ctx.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err)
+ if err := writeFile(outputDir, ctx, f); err != nil {
+ fmt.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err)
}
}
}
-func (s *androidBpToBuildSingleton) getOutputPath(ctx android.PathContext, dir string) android.OutputPath {
- return s.outputDir.Join(ctx, dir)
+func writeFile(outputDir android.OutputPath, ctx android.PathContext, f BazelFile) error {
+ return writeReadOnlyFile(ctx, getOutputPath(outputDir, ctx, f.Dir), f.Basename, f.Contents)
}
-func (s *androidBpToBuildSingleton) writeFile(ctx android.PathContext, f BazelFile) error {
- return writeReadOnlyFile(ctx, s.getOutputPath(ctx, f.Dir), f.Basename, f.Contents)
+func getOutputPath(outputDir android.OutputPath, ctx android.PathContext, dir string) android.OutputPath {
+ return outputDir.Join(ctx, dir)
}
// The auto-conversion directory should be read-only, sufficient for bazel query. The files
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 0329685..bece8f6 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -39,8 +39,26 @@
ModuleSubDir(module blueprint.Module) string
ModuleType(module blueprint.Module) string
- VisitAllModulesBlueprint(visit func(blueprint.Module))
- VisitDirectDeps(module android.Module, visit func(android.Module))
+ VisitAllModules(visit func(blueprint.Module))
+ VisitDirectDeps(module blueprint.Module, visit func(blueprint.Module))
+}
+
+type CodegenContext struct {
+ config android.Config
+ context android.Context
+}
+
+func (ctx CodegenContext) AddNinjaFileDeps(...string) {}
+func (ctx CodegenContext) Config() android.Config { return ctx.config }
+func (ctx CodegenContext) Context() android.Context { return ctx.context }
+
+// NewCodegenContext creates a wrapper context that conforms to PathContext for
+// writing BUILD files in the output directory.
+func NewCodegenContext(config android.Config, context android.Context) CodegenContext {
+ return CodegenContext{
+ context: context,
+ config: config,
+ }
}
// props is an unsorted map. This function ensures that
@@ -57,7 +75,7 @@
func GenerateSoongModuleTargets(ctx bpToBuildContext) map[string][]BazelTarget {
buildFileToTargets := make(map[string][]BazelTarget)
- ctx.VisitAllModulesBlueprint(func(m blueprint.Module) {
+ ctx.VisitAllModules(func(m blueprint.Module) {
dir := ctx.ModuleDir(m)
t := generateSoongModuleTarget(ctx, m)
buildFileToTargets[ctx.ModuleDir(m)] = append(buildFileToTargets[dir], t)
@@ -75,7 +93,7 @@
// out the implications of that.
depLabels := map[string]bool{}
if aModule, ok := m.(android.Module); ok {
- ctx.VisitDirectDeps(aModule, func(depModule android.Module) {
+ ctx.VisitDirectDeps(aModule, func(depModule blueprint.Module) {
depLabels[qualifiedTargetLabel(ctx, depModule)] = true
})
}
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 8230ad8..4e31aa7 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -200,11 +200,7 @@
_, errs = ctx.PrepareBuildActions(config)
android.FailIfErrored(t, errs)
- bp2BuildCtx := bp2buildBlueprintWrapContext{
- bpCtx: ctx.Context.Context,
- }
-
- bazelTargets := GenerateSoongModuleTargets(&bp2BuildCtx)[dir]
+ bazelTargets := GenerateSoongModuleTargets(ctx.Context.Context)[dir]
if g, w := len(bazelTargets), 1; g != w {
t.Fatalf("Expected %d bazel target, got %d", w, g)
}
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 160412d..2da32c6 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -2,8 +2,6 @@
import (
"android/soong/android"
-
- "github.com/google/blueprint"
)
type nestedProps struct {
@@ -102,35 +100,3 @@
android.InitDefaultsModule(m)
return m
}
-
-type bp2buildBlueprintWrapContext struct {
- bpCtx *blueprint.Context
-}
-
-func (ctx *bp2buildBlueprintWrapContext) ModuleName(module blueprint.Module) string {
- return ctx.bpCtx.ModuleName(module)
-}
-
-func (ctx *bp2buildBlueprintWrapContext) ModuleDir(module blueprint.Module) string {
- return ctx.bpCtx.ModuleDir(module)
-}
-
-func (ctx *bp2buildBlueprintWrapContext) ModuleSubDir(module blueprint.Module) string {
- return ctx.bpCtx.ModuleSubDir(module)
-}
-
-func (ctx *bp2buildBlueprintWrapContext) ModuleType(module blueprint.Module) string {
- return ctx.bpCtx.ModuleType(module)
-}
-
-func (ctx *bp2buildBlueprintWrapContext) VisitAllModulesBlueprint(visit func(blueprint.Module)) {
- ctx.bpCtx.VisitAllModules(visit)
-}
-
-func (ctx *bp2buildBlueprintWrapContext) VisitDirectDeps(module android.Module, visit func(android.Module)) {
- ctx.bpCtx.VisitDirectDeps(module, func(m blueprint.Module) {
- if aModule, ok := m.(android.Module); ok {
- visit(aModule)
- }
- })
-}
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 907bed3..1d5e7b3 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -23,6 +23,7 @@
"github.com/google/blueprint/bootstrap"
"android/soong/android"
+ "android/soong/bp2build"
)
var (
@@ -54,18 +55,12 @@
// bazelConversionRequested checks that the user is intending to convert
// Blueprint to Bazel BUILD files.
func bazelConversionRequested(configuration android.Config) bool {
- return configuration.IsEnvTrue("CONVERT_TO_BAZEL")
+ return configuration.IsEnvTrue("GENERATE_BAZEL_FILES")
}
-func newContext(srcDir string, configuration android.Config) *android.Context {
+func newContext(configuration android.Config) *android.Context {
ctx := android.NewContext(configuration)
- if bazelConversionRequested(configuration) {
- // Register an alternate set of singletons and mutators for bazel
- // conversion for Bazel conversion.
- ctx.RegisterForBazelConversion()
- } else {
- ctx.Register()
- }
+ ctx.Register()
if !shouldPrepareBuildActions(configuration) {
configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
}
@@ -100,13 +95,22 @@
// enabled even if it completed successfully.
extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.BuildDir(), "always_rerun_for_delve"))
}
+
+ if bazelConversionRequested(configuration) {
+ // Run the alternate pipeline of bp2build mutators and singleton to convert Blueprint to BUILD files
+ // before everything else.
+ runBp2Build(configuration, extraNinjaDeps)
+ // Short-circuit and return.
+ return
+ }
+
if configuration.BazelContext.BazelEnabled() {
// Bazel-enabled mode. Soong runs in two passes.
// First pass: Analyze the build tree, but only store all bazel commands
// needed to correctly evaluate the tree in the second pass.
// TODO(cparsons): Don't output any ninja file, as the second pass will overwrite
// the incorrect results from the first pass, and file I/O is expensive.
- firstCtx := newContext(srcDir, configuration)
+ firstCtx := newContext(configuration)
configuration.SetStopBefore(bootstrap.StopBeforeWriteNinja)
bootstrap.Main(firstCtx.Context, configuration, extraNinjaDeps...)
// Invoke bazel commands and save results for second pass.
@@ -120,10 +124,10 @@
fmt.Fprintf(os.Stderr, "%s", err)
os.Exit(1)
}
- ctx = newContext(srcDir, secondPassConfig)
+ ctx = newContext(secondPassConfig)
bootstrap.Main(ctx.Context, secondPassConfig, extraNinjaDeps...)
} else {
- ctx = newContext(srcDir, configuration)
+ ctx = newContext(configuration)
bootstrap.Main(ctx.Context, configuration, extraNinjaDeps...)
}
@@ -154,6 +158,22 @@
}
}
+// Run Soong in the bp2build mode. This creates a standalone context that registers
+// an alternate pipeline of mutators and singletons specifically for generating
+// Bazel BUILD files instead of Ninja files.
+func runBp2Build(configuration android.Config, extraNinjaDeps []string) {
+ // Register an alternate set of singletons and mutators for bazel
+ // conversion for Bazel conversion.
+ bp2buildCtx := android.NewContext(configuration)
+ bp2buildCtx.RegisterForBazelConversion()
+ configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
+ bp2buildCtx.SetNameInterface(newNameResolver(configuration))
+ bootstrap.Main(bp2buildCtx.Context, configuration, extraNinjaDeps...)
+
+ codegenContext := bp2build.NewCodegenContext(configuration, *bp2buildCtx)
+ bp2build.Codegen(codegenContext)
+}
+
// shouldPrepareBuildActions reads configuration and flags if build actions
// should be generated.
func shouldPrepareBuildActions(configuration android.Config) bool {
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index 3657ea8..79ea94a 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -20,48 +20,11 @@
"io/ioutil"
"os"
"path/filepath"
-
- "github.com/google/blueprint"
)
-type queryviewContext struct {
- bpCtx *blueprint.Context
-}
-
-func (ctx *queryviewContext) ModuleName(module blueprint.Module) string {
- return ctx.bpCtx.ModuleName(module)
-}
-
-func (ctx *queryviewContext) ModuleDir(module blueprint.Module) string {
- return ctx.bpCtx.ModuleDir(module)
-}
-
-func (ctx *queryviewContext) ModuleSubDir(module blueprint.Module) string {
- return ctx.bpCtx.ModuleSubDir(module)
-}
-
-func (ctx *queryviewContext) ModuleType(module blueprint.Module) string {
- return ctx.bpCtx.ModuleType(module)
-}
-
-func (ctx *queryviewContext) VisitAllModulesBlueprint(visit func(blueprint.Module)) {
- ctx.bpCtx.VisitAllModules(visit)
-}
-
-func (ctx *queryviewContext) VisitDirectDeps(module android.Module, visit func(android.Module)) {
- ctx.bpCtx.VisitDirectDeps(module, func(m blueprint.Module) {
- if aModule, ok := m.(android.Module); ok {
- visit(aModule)
- }
- })
-}
-
func createBazelQueryView(ctx *android.Context, bazelQueryViewDir string) error {
- qvCtx := queryviewContext{
- bpCtx: ctx.Context,
- }
ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
- buildToTargets := bp2build.GenerateSoongModuleTargets(&qvCtx)
+ buildToTargets := bp2build.GenerateSoongModuleTargets(*ctx)
filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets)
for _, f := range filesToWrite {