Bp2build support for multiple product configs

Create a
build/bazel/product_config/generated/products/<product_name>/BUILD
file that contains the platform definitions needed for
a particular product. Currently we just create it for the
current lunch target, but the idea is that eventually when
all product config is in starlark, all the products will
have their platform definitions in the tree at once.

Bug: 249685973
Test: Presubmits
Change-Id: I08c82ff28dcf62f09d3b1d2e3186a6b961e12f6e
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 72d16fa..6edd78a 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -8,6 +8,7 @@
     srcs: [
         "androidbp_to_build_templates.go",
         "bp2build.go",
+        "bp2build_product_config.go",
         "build_conversion.go",
         "bzl_conversion.go",
         "configurability.go",
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index a75a84e..5dc9612 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -45,7 +45,14 @@
 	bp2buildFiles := CreateBazelFiles(ctx.Config(), nil, res.buildFileToTargets, ctx.mode)
 	writeFiles(ctx, bp2buildDir, bp2buildFiles)
 
+	productConfigFiles, err := CreateProductConfigFiles(ctx)
+	if err != nil {
+		fmt.Printf("ERROR: %s", err.Error())
+		os.Exit(1)
+	}
+
 	soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName)
+	writeFiles(ctx, soongInjectionDir, productConfigFiles)
 	writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(ctx.Config(), res.metrics))
 
 	return &res.metrics
diff --git a/bp2build/bp2build_product_config.go b/bp2build/bp2build_product_config.go
new file mode 100644
index 0000000..e343a05
--- /dev/null
+++ b/bp2build/bp2build_product_config.go
@@ -0,0 +1,124 @@
+package bp2build
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+func CreateProductConfigFiles(
+	ctx *CodegenContext) ([]BazelFile, error) {
+	cfg := &ctx.config
+	targetProduct := "unknown"
+	if cfg.HasDeviceProduct() {
+		targetProduct = cfg.DeviceProduct()
+	}
+	targetBuildVariant := "user"
+	if cfg.Eng() {
+		targetBuildVariant = "eng"
+	} else if cfg.Debuggable() {
+		targetBuildVariant = "userdebug"
+	}
+
+	productVariablesFileName := cfg.ProductVariablesFileName
+	if !strings.HasPrefix(productVariablesFileName, "/") {
+		productVariablesFileName = filepath.Join(ctx.topDir, productVariablesFileName)
+	}
+	bytes, err := os.ReadFile(productVariablesFileName)
+	if err != nil {
+		return nil, err
+	}
+
+	// TODO(b/249685973): the name is product_config_platforms because product_config
+	// was already used for other files. Deduplicate them.
+	currentProductFolder := fmt.Sprintf("product_config_platforms/products/%s-%s", targetProduct, targetBuildVariant)
+
+	productReplacer := strings.NewReplacer(
+		"{PRODUCT}", targetProduct,
+		"{VARIANT}", targetBuildVariant,
+		"{PRODUCT_FOLDER}", currentProductFolder)
+
+	result := []BazelFile{
+		newFile(
+			currentProductFolder,
+			"soong.variables.bzl",
+			`variables = json.decode("""`+strings.ReplaceAll(string(bytes), "\\", "\\\\")+`""")`),
+		newFile(
+			currentProductFolder,
+			"BUILD",
+			productReplacer.Replace(`
+package(default_visibility=[
+    "@soong_injection//product_config_platforms:__subpackages__",
+    "@//build/bazel/product_config:__subpackages__",
+])
+load(":soong.variables.bzl", _soong_variables = "variables")
+load("@//build/bazel/product_config:utils.bzl", "android_product")
+
+android_product(
+    name = "{PRODUCT}-{VARIANT}",
+    soong_variables = _soong_variables,
+)
+`)),
+		newFile(
+			"product_config_platforms",
+			"BUILD.bazel",
+			productReplacer.Replace(`
+package(default_visibility = [
+	"@//build/bazel/product_config:__subpackages__",
+	"@soong_injection//product_config_platforms:__subpackages__",
+])
+
+# TODO(b/249685973): Remove this. It was only added for a platform_mappings file,
+# which can possibly be replaced with autogenerating the platform_mappings file,
+# or removing that file entirely.
+alias(
+	name = "current_android_platform",
+	# TODO: When we start generating the platforms for more than just the
+	# currently lunched, product, turn this into a select with an arm for each product.
+	actual = "@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}",
+)
+
+alias(
+	name = "product_vars",
+	actual = select({
+		# TODO: When we start generating the platforms for more than just the
+		# currently lunched, product, this select should have an arm for each product.
+		"@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_constraint_value": "@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_product_vars",
+	}),
+)
+`)),
+		newFile(
+			"product_config_platforms",
+			"common.bazelrc",
+			productReplacer.Replace(`
+build --platforms @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
+
+build:android --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}
+build:linux_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
+build:linux_bionic_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_bionic_x86_64
+`)),
+		newFile(
+			"product_config_platforms",
+			"linux.bazelrc",
+			productReplacer.Replace(`
+build --host_platform @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
+`)),
+		newFile(
+			"product_config_platforms",
+			"darwin.bazelrc",
+			productReplacer.Replace(`
+build --host_platform @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_darwin_x86_64
+`)),
+		newFile(
+			"product_config_platforms",
+			"platform_mappings",
+			productReplacer.Replace(`
+flags:
+  --cpu=k8
+    @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}
+`)),
+	}
+
+	return result, nil
+}
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 5ab54e3..6c6631a 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -140,6 +140,7 @@
 	mode               CodegenMode
 	additionalDeps     []string
 	unconvertedDepMode unconvertedDepsMode
+	topDir             string
 }
 
 func (ctx *CodegenContext) Mode() CodegenMode {
@@ -208,7 +209,7 @@
 
 // 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, mode CodegenMode) *CodegenContext {
+func NewCodegenContext(config android.Config, context *android.Context, mode CodegenMode, topDir string) *CodegenContext {
 	var unconvertedDeps unconvertedDepsMode
 	if config.IsEnvTrue("BP2BUILD_ERROR_UNCONVERTED") {
 		unconvertedDeps = errorModulesUnconvertedDeps
@@ -218,6 +219,7 @@
 		config:             config,
 		mode:               mode,
 		unconvertedDepMode: unconvertedDeps,
+		topDir:             topDir,
 	}
 }
 
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 8433f52..d312169 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -209,7 +209,7 @@
 			_, errs = ctx.PrepareBuildActions(config)
 			android.FailIfErrored(t, errs)
 
-			codegenCtx := NewCodegenContext(config, ctx.Context, QueryView)
+			codegenCtx := NewCodegenContext(config, ctx.Context, QueryView, "")
 			bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
 			android.FailIfErrored(t, err)
 			if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
@@ -530,7 +530,7 @@
 				return
 			}
 
-			codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build)
+			codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "")
 			bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
 			android.FailIfErrored(t, err)
 
@@ -903,7 +903,7 @@
 		_, errs = ctx.ResolveDependencies(config)
 		android.FailIfErrored(t, errs)
 
-		codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build)
+		codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "")
 		bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
 		android.FailIfErrored(t, err)
 		if actualCount := len(bazelTargets); actualCount != testCase.expectedBazelTargetCount {
@@ -1156,7 +1156,7 @@
 			_, errs = ctx.ResolveDependencies(config)
 			android.FailIfErrored(t, errs)
 
-			codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build)
+			codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "")
 			bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
 			android.FailIfErrored(t, err)
 			if actualCount := len(bazelTargets); actualCount != testCase.expectedCount {
@@ -1263,7 +1263,7 @@
 		_, errs = ctx.ResolveDependencies(config)
 		android.FailIfErrored(t, errs)
 
-		codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build)
+		codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "")
 
 		// For each directory, test that the expected number of generated targets is correct.
 		for dir, expectedCount := range testCase.expectedCount {
@@ -1398,7 +1398,7 @@
 			if testCase.Dir != "" {
 				checkDir = testCase.Dir
 			}
-			codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build)
+			codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "")
 			bazelTargets, err := generateBazelTargetsForDir(codegenCtx, checkDir)
 			android.FailIfErrored(t, err)
 			bazelTargets.sort()
diff --git a/bp2build/performance_test.go b/bp2build/performance_test.go
index 272ebf5..5f80b83 100644
--- a/bp2build/performance_test.go
+++ b/bp2build/performance_test.go
@@ -106,7 +106,7 @@
 	ctx := android.NewTestContext(config)
 
 	registerCustomModuleForBp2buildConversion(ctx)
-	codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build)
+	codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "")
 	return testConfig{
 		config,
 		ctx,
diff --git a/bp2build/testing.go b/bp2build/testing.go
index c059add..1f9874c 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -200,7 +200,7 @@
 		return
 	}
 
-	codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build)
+	codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build, "")
 	res, errs := GenerateBazelTargets(codegenCtx, false)
 	if bazelResult.CollateErrs(errs) {
 		return