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/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)
-		}
-	})
-}