Break constant information out of product vars

We don't want rules to load the product variables directly, because
then they won't be able to transition on them.

Break constant information that is safe to load outside of the
product vars file, so that we can make the product vars file have
more restricted visibility later.

Bug: 269577299
Test: m nothing
Change-Id: I848bff33e4f5798f51296ea3a2600615cab36985
diff --git a/android/config.go b/android/config.go
index 6765f1f..2ccb732 100644
--- a/android/config.go
+++ b/android/config.go
@@ -420,6 +420,8 @@
 		fmt.Sprintf(`_arch_variant_product_var_constraints = %s`, archVariantProductVariablesJson),
 		"\n", `
 product_vars = _product_vars
+
+# TODO(b/269577299) Remove these when everything switches over to loading them from product_variable_constants.bzl
 product_var_constraints = _product_var_constraints
 arch_variant_product_var_constraints = _arch_variant_product_var_constraints
 `,
@@ -429,6 +431,13 @@
 	if err != nil {
 		return fmt.Errorf("Could not write .bzl config file %s", err)
 	}
+	err = pathtools.WriteFileIfChanged(filepath.Join(dir, "product_variable_constants.bzl"), []byte(fmt.Sprintf(`
+product_var_constraints = %s
+arch_variant_product_var_constraints = %s
+`, nonArchVariantProductVariablesJson, archVariantProductVariablesJson)), 0644)
+	if err != nil {
+		return fmt.Errorf("Could not write .bzl config file %s", err)
+	}
 	err = pathtools.WriteFileIfChanged(filepath.Join(dir, "BUILD"),
 		[]byte(bazel.GeneratedBazelFileWarning), 0644)
 	if err != nil {
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 6a39e25..21e9c09 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -1,7 +1,9 @@
 package bp2build
 
 import (
+	"android/soong/starlark_fmt"
 	"encoding/json"
+	"fmt"
 	"reflect"
 	"strings"
 
@@ -62,6 +64,7 @@
 	// TODO(b/269691302)  value of apiLevelsContent is product variable dependent and should be avoided for soong injection
 	files = append(files, newFile("api_levels", "api_levels.json", string(apiLevelsContent)))
 	files = append(files, newFile("api_levels", "api_levels.bzl", android.StarlarkApiLevelConfigs(cfg)))
+	files = append(files, newFile("api_levels", "platform_versions.bzl", platformVersionContents(cfg)))
 
 	files = append(files, newFile("allowlists", GeneratedBuildFileName, ""))
 	files = append(files, newFile("allowlists", "env.bzl", android.EnvironmentVarsFile(cfg)))
@@ -72,6 +75,26 @@
 	return files, nil
 }
 
+func platformVersionContents(cfg android.Config) string {
+	// Despite these coming from cfg.productVariables, they are actually hardcoded in global
+	// makefiles, not set in individual product config makesfiles, so they're safe to just export
+	// and load() directly.
+
+	platformVersionActiveCodenames := make([]string, 0, len(cfg.PlatformVersionActiveCodenames()))
+	for _, codename := range cfg.PlatformVersionActiveCodenames() {
+		platformVersionActiveCodenames = append(platformVersionActiveCodenames, fmt.Sprintf("%q", codename))
+	}
+
+	return fmt.Sprintf(`
+platform_versions = struct(
+    platform_sdk_final = %s,
+    platform_sdk_version = %d,
+    platform_sdk_codename = %q,
+    platform_version_active_codenames = [%s],
+)
+`, starlark_fmt.PrintBool(cfg.PlatformSdkFinal()), cfg.PlatformSdkVersion().FinalInt(), cfg.PlatformSdkCodename(), strings.Join(platformVersionActiveCodenames, ", "))
+}
+
 func CreateBazelFiles(
 	cfg android.Config,
 	ruleShims map[string]RuleShim,
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index 8c1d2ae..e73ad2e 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -154,6 +154,10 @@
 			basename: "api_levels.bzl",
 		},
 		{
+			dir:      "api_levels",
+			basename: "platform_versions.bzl",
+		},
+		{
 			dir:      "allowlists",
 			basename: GeneratedBuildFileName,
 		},
diff --git a/starlark_fmt/format.go b/starlark_fmt/format.go
index 064fc21..a97f71b 100644
--- a/starlark_fmt/format.go
+++ b/starlark_fmt/format.go
@@ -35,7 +35,11 @@
 
 // PrintBool returns a Starlark compatible bool string.
 func PrintBool(item bool) string {
-	return strings.Title(fmt.Sprintf("%t", item))
+	if item {
+		return "True"
+	} else {
+		return "False"
+	}
 }
 
 // PrintsStringList returns a Starlark-compatible string of a list of Strings/Labels.