release_config: cleanup how we emit results

 - Allow each of the formats (json, pb, textproto) to be individually
   controlled.
 - Include product and release in the name of the aritfact.

Bug: 328495189
Test: manual
Change-Id: Ia08e7224200a48994bea882a61b8199d53576d38
diff --git a/cmd/release_config/release_config/main.go b/cmd/release_config/release_config/main.go
index 4870f25..a43fdcc 100644
--- a/cmd/release_config/release_config/main.go
+++ b/cmd/release_config/release_config/main.go
@@ -16,7 +16,9 @@
 
 import (
 	"flag"
+	"fmt"
 	"os"
+	"path/filepath"
 
 	rc_lib "android/soong/cmd/release_config/release_config_lib"
 )
@@ -29,12 +31,23 @@
 	var outputDir string
 	var err error
 	var configs *rc_lib.ReleaseConfigs
+	var json, pb, textproto bool
+	var product string
+
+	defaultRelease := os.Getenv("TARGET_RELEASE")
+	if defaultRelease == "" {
+		defaultRelease = "trunk_staging"
+	}
 
 	flag.StringVar(&top, "top", ".", "path to top of workspace")
+	flag.StringVar(&product, "product", os.Getenv("TARGET_PRODUCT"), "TARGET_PRODUCT for the build")
 	flag.BoolVar(&quiet, "quiet", false, "disable warning messages")
 	flag.Var(&releaseConfigMapPaths, "map", "path to a release_config_map.textproto. may be repeated")
-	flag.StringVar(&targetRelease, "release", "trunk_staging", "TARGET_RELEASE for this build")
+	flag.StringVar(&targetRelease, "release", defaultRelease, "TARGET_RELEASE for this build")
 	flag.StringVar(&outputDir, "out_dir", rc_lib.GetDefaultOutDir(), "basepath for the output. Multiple formats are created")
+	flag.BoolVar(&textproto, "textproto", true, "write artifacts as text protobuf")
+	flag.BoolVar(&json, "json", true, "write artifacts as json")
+	flag.BoolVar(&pb, "pb", true, "write artifacts as binary protobuf")
 	flag.Parse()
 
 	if quiet {
@@ -48,16 +61,36 @@
 	if err != nil {
 		panic(err)
 	}
+	config, err := configs.GetReleaseConfig(targetRelease)
+	if err != nil {
+		panic(err)
+	}
+	releaseName := config.Name
 	err = os.MkdirAll(outputDir, 0775)
 	if err != nil {
 		panic(err)
 	}
-	err = configs.DumpMakefile(outputDir, targetRelease)
+	makefilePath := filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.mk", product, releaseName))
+	err = configs.WriteMakefile(makefilePath, targetRelease)
 	if err != nil {
 		panic(err)
 	}
-	err = configs.DumpArtifact(outputDir)
-	if err != nil {
-		panic(err)
+	if json {
+		err = configs.WriteArtifact(outputDir, product, "json")
+		if err != nil {
+			panic(err)
+		}
+	}
+	if pb {
+		err = configs.WriteArtifact(outputDir, product, "pb")
+		if err != nil {
+			panic(err)
+		}
+	}
+	if textproto {
+		err = configs.WriteArtifact(outputDir, product, "textproto")
+		if err != nil {
+			panic(err)
+		}
 	}
 }
diff --git a/cmd/release_config/release_config_lib/Android.bp b/cmd/release_config/release_config_lib/Android.bp
index 601194c..0c67e11 100644
--- a/cmd/release_config/release_config_lib/Android.bp
+++ b/cmd/release_config/release_config_lib/Android.bp
@@ -18,7 +18,7 @@
 
 bootstrap_go_package {
     name: "soong-cmd-release_config-lib",
-    pkgPath: "android/soong/release_config/release_config_lib",
+    pkgPath: "android/soong/cmd/release_config/release_config_lib",
     deps: [
         "golang-protobuf-encoding-prototext",
         "golang-protobuf-reflect-protoreflect",
diff --git a/cmd/release_config/release_config_lib/flag_artifact.go b/cmd/release_config/release_config_lib/flag_artifact.go
index 6d8085d..4446655 100644
--- a/cmd/release_config/release_config_lib/flag_artifact.go
+++ b/cmd/release_config/release_config_lib/flag_artifact.go
@@ -17,29 +17,37 @@
 import (
 	"fmt"
 
-	"android/soong/cmd/release_config/release_config_proto"
+	rc_proto "android/soong/cmd/release_config/release_config_proto"
 
 	"google.golang.org/protobuf/proto"
 )
 
+// A flag artifact, with its final value and declaration/override history.
 type FlagArtifact struct {
-	FlagDeclaration *release_config_proto.FlagDeclaration
+	// The flag_declaration message.
+	FlagDeclaration *rc_proto.FlagDeclaration
 
 	// The index of the config directory where this flag was declared.
 	// Flag values cannot be set in a location with a lower index.
 	DeclarationIndex int
 
-	Traces []*release_config_proto.Tracepoint
+	// A history of value assignments and overrides.
+	Traces []*rc_proto.Tracepoint
 
-	// Assigned value
-	Value *release_config_proto.Value
+	// The value of the flag.
+	Value *rc_proto.Value
 }
 
 // Key is flag name.
 type FlagArtifacts map[string]*FlagArtifact
 
+// Create a clone of the flag artifact.
+//
+// Returns:
+//
+//	*FlagArtifact: the copy of the artifact.
 func (src *FlagArtifact) Clone() *FlagArtifact {
-	value := &release_config_proto.Value{}
+	value := &rc_proto.Value{}
 	proto.Merge(value, src.Value)
 	return &FlagArtifact{
 		FlagDeclaration: src.FlagDeclaration,
@@ -48,6 +56,11 @@
 	}
 }
 
+// Clone FlagArtifacts.
+//
+// Returns:
+//
+//	FlagArtifacts: a copy of the source FlagArtifacts.
 func (src FlagArtifacts) Clone() (dst FlagArtifacts) {
 	if dst == nil {
 		dst = make(FlagArtifacts)
@@ -58,23 +71,34 @@
 	return
 }
 
+// Update the value of a flag.
+//
+// This appends to flagArtifact.Traces, and updates flagArtifact.Value.
+//
+// Args:
+//
+//	flagValue FlagValue: the value to assign
+//
+// Returns:
+//
+//	error: any error encountered
 func (fa *FlagArtifact) UpdateValue(flagValue FlagValue) error {
 	name := *flagValue.proto.Name
-	fa.Traces = append(fa.Traces, &release_config_proto.Tracepoint{Source: proto.String(flagValue.path), Value: flagValue.proto.Value})
+	fa.Traces = append(fa.Traces, &rc_proto.Tracepoint{Source: proto.String(flagValue.path), Value: flagValue.proto.Value})
 	if fa.Value.GetObsolete() {
 		return fmt.Errorf("Attempting to set obsolete flag %s. Trace=%v", name, fa.Traces)
 	}
-	var newValue *release_config_proto.Value
+	var newValue *rc_proto.Value
 	switch val := flagValue.proto.Value.Val.(type) {
-	case *release_config_proto.Value_StringValue:
-		newValue = &release_config_proto.Value{Val: &release_config_proto.Value_StringValue{val.StringValue}}
-	case *release_config_proto.Value_BoolValue:
-		newValue = &release_config_proto.Value{Val: &release_config_proto.Value_BoolValue{val.BoolValue}}
-	case *release_config_proto.Value_Obsolete:
+	case *rc_proto.Value_StringValue:
+		newValue = &rc_proto.Value{Val: &rc_proto.Value_StringValue{val.StringValue}}
+	case *rc_proto.Value_BoolValue:
+		newValue = &rc_proto.Value{Val: &rc_proto.Value_BoolValue{val.BoolValue}}
+	case *rc_proto.Value_Obsolete:
 		if !val.Obsolete {
 			return fmt.Errorf("%s: Cannot set obsolete=false.  Trace=%v", name, fa.Traces)
 		}
-		newValue = &release_config_proto.Value{Val: &release_config_proto.Value_Obsolete{true}}
+		newValue = &rc_proto.Value{Val: &rc_proto.Value_Obsolete{true}}
 	default:
 		return fmt.Errorf("Invalid type for flag_value: %T.  Trace=%v", val, fa.Traces)
 	}
@@ -85,8 +109,9 @@
 	return nil
 }
 
-func (fa *FlagArtifact) Marshal() (*release_config_proto.FlagArtifact, error) {
-	return &release_config_proto.FlagArtifact{
+// Marshal the FlagArtifact into a flag_artifact message.
+func (fa *FlagArtifact) Marshal() (*rc_proto.FlagArtifact, error) {
+	return &rc_proto.FlagArtifact{
 		FlagDeclaration: fa.FlagDeclaration,
 		Value:           fa.Value,
 		Traces:          fa.Traces,
diff --git a/cmd/release_config/release_config_lib/flag_declaration.go b/cmd/release_config/release_config_lib/flag_declaration.go
index d5cc418..97d4d4c 100644
--- a/cmd/release_config/release_config_lib/flag_declaration.go
+++ b/cmd/release_config/release_config_lib/flag_declaration.go
@@ -15,13 +15,13 @@
 package release_config_lib
 
 import (
-	"android/soong/cmd/release_config/release_config_proto"
+	rc_proto "android/soong/cmd/release_config/release_config_proto"
 )
 
-func FlagDeclarationFactory(protoPath string) (fd *release_config_proto.FlagDeclaration) {
-	fd = &release_config_proto.FlagDeclaration{}
+func FlagDeclarationFactory(protoPath string) (fd *rc_proto.FlagDeclaration) {
+	fd = &rc_proto.FlagDeclaration{}
 	if protoPath != "" {
-		LoadTextproto(protoPath, fd)
+		LoadMessage(protoPath, fd)
 	}
 	return fd
 }
diff --git a/cmd/release_config/release_config_lib/flag_value.go b/cmd/release_config/release_config_lib/flag_value.go
index 138e8f8..e155e77 100644
--- a/cmd/release_config/release_config_lib/flag_value.go
+++ b/cmd/release_config/release_config_lib/flag_value.go
@@ -15,7 +15,9 @@
 package release_config_lib
 
 import (
-	"android/soong/cmd/release_config/release_config_proto"
+	"strings"
+
+	rc_proto "android/soong/cmd/release_config/release_config_proto"
 )
 
 type FlagValue struct {
@@ -23,31 +25,46 @@
 	path string
 
 	// Protobuf
-	proto release_config_proto.FlagValue
+	proto rc_proto.FlagValue
 }
 
 func FlagValueFactory(protoPath string) (fv *FlagValue) {
 	fv = &FlagValue{path: protoPath}
 	if protoPath != "" {
-		LoadTextproto(protoPath, &fv.proto)
+		LoadMessage(protoPath, &fv.proto)
 	}
 	return fv
 }
 
-func MarshalValue(value *release_config_proto.Value) string {
+func UnmarshalValue(str string) *rc_proto.Value {
+	ret := &rc_proto.Value{}
+	switch v := strings.ToLower(str); v {
+	case "true":
+		ret = &rc_proto.Value{Val: &rc_proto.Value_BoolValue{true}}
+	case "false":
+		ret = &rc_proto.Value{Val: &rc_proto.Value_BoolValue{false}}
+	case "##obsolete":
+		ret = &rc_proto.Value{Val: &rc_proto.Value_Obsolete{true}}
+	default:
+		ret = &rc_proto.Value{Val: &rc_proto.Value_StringValue{str}}
+	}
+	return ret
+}
+
+func MarshalValue(value *rc_proto.Value) string {
 	switch val := value.Val.(type) {
-	case *release_config_proto.Value_UnspecifiedValue:
+	case *rc_proto.Value_UnspecifiedValue:
 		// Value was never set.
 		return ""
-	case *release_config_proto.Value_StringValue:
+	case *rc_proto.Value_StringValue:
 		return val.StringValue
-	case *release_config_proto.Value_BoolValue:
+	case *rc_proto.Value_BoolValue:
 		if val.BoolValue {
 			return "true"
 		}
 		// False ==> empty string
 		return ""
-	case *release_config_proto.Value_Obsolete:
+	case *rc_proto.Value_Obsolete:
 		return " #OBSOLETE"
 	default:
 		// Flagged as error elsewhere, so return empty string here.
diff --git a/cmd/release_config/release_config_lib/release_config.go b/cmd/release_config/release_config_lib/release_config.go
index 3110dae..c67cee5 100644
--- a/cmd/release_config/release_config_lib/release_config.go
+++ b/cmd/release_config/release_config_lib/release_config.go
@@ -16,8 +16,9 @@
 
 import (
 	"fmt"
+	"strings"
 
-	"android/soong/cmd/release_config/release_config_proto"
+	rc_proto "android/soong/cmd/release_config/release_config_proto"
 
 	"google.golang.org/protobuf/proto"
 )
@@ -33,7 +34,7 @@
 	DeclarationIndex int
 
 	// Protobufs relevant to the config.
-	proto release_config_proto.ReleaseConfig
+	proto rc_proto.ReleaseConfig
 
 	FlagValues []*FlagValue
 }
@@ -61,7 +62,7 @@
 	FlagArtifacts FlagArtifacts
 
 	// Generated release config
-	ReleaseConfigArtifact *release_config_proto.ReleaseConfigArtifact
+	ReleaseConfigArtifact *rc_proto.ReleaseConfigArtifact
 
 	// We have begun compiling this release config.
 	compileInProgress bool
@@ -79,12 +80,18 @@
 		return fmt.Errorf("Loop detected for release config %s", config.Name)
 	}
 	config.compileInProgress = true
+	isRoot := config.Name == "root"
 
 	// Generate any configs we need to inherit.  This will detect loops in
 	// the config.
 	contributionsToApply := []*ReleaseConfigContribution{}
 	myInherits := []string{}
 	myInheritsSet := make(map[string]bool)
+	// If there is a "root" release config, it is the start of every inheritance chain.
+	_, err := configs.GetReleaseConfig("root")
+	if err == nil && !isRoot {
+		config.InheritNames = append([]string{"root"}, config.InheritNames...)
+	}
 	for _, inherit := range config.InheritNames {
 		if _, ok := myInheritsSet[inherit]; ok {
 			continue
@@ -101,10 +108,46 @@
 	contributionsToApply = append(contributionsToApply, config.Contributions...)
 
 	myAconfigValueSets := []string{}
+	myAconfigValueSetsMap := map[string]bool{}
 	myFlags := configs.FlagArtifacts.Clone()
+	workflowManual := rc_proto.Workflow(rc_proto.Workflow_MANUAL)
+	container := rc_proto.Container(rc_proto.Container_ALL)
+	releaseAconfigValueSets := FlagArtifact{
+		FlagDeclaration: &rc_proto.FlagDeclaration{
+			Name:        proto.String("RELEASE_ACONFIG_VALUE_SETS"),
+			Namespace:   proto.String("android_UNKNOWN"),
+			Description: proto.String("Aconfig value sets assembled by release-config"),
+			Workflow:    &workflowManual,
+			Container:   &container,
+			Value:       &rc_proto.Value{Val: &rc_proto.Value_StringValue{""}},
+		},
+		DeclarationIndex: -1,
+		Traces: []*rc_proto.Tracepoint{
+			&rc_proto.Tracepoint{
+				Source: proto.String("$release-config"),
+				Value:  &rc_proto.Value{Val: &rc_proto.Value_StringValue{""}},
+			},
+		},
+	}
+	myFlags["RELEASE_ACONFIG_VALUE_SETS"] = &releaseAconfigValueSets
 	myDirsMap := make(map[int]bool)
 	for _, contrib := range contributionsToApply {
-		myAconfigValueSets = append(myAconfigValueSets, contrib.proto.AconfigValueSets...)
+		if len(contrib.proto.AconfigValueSets) > 0 {
+			contribAconfigValueSets := []string{}
+			for _, v := range contrib.proto.AconfigValueSets {
+				if _, ok := myAconfigValueSetsMap[v]; !ok {
+					contribAconfigValueSets = append(contribAconfigValueSets, v)
+					myAconfigValueSetsMap[v] = true
+				}
+			}
+			myAconfigValueSets = append(myAconfigValueSets, contribAconfigValueSets...)
+			releaseAconfigValueSets.Traces = append(
+				releaseAconfigValueSets.Traces,
+				&rc_proto.Tracepoint{
+					Source: proto.String(contrib.path),
+					Value:  &rc_proto.Value{Val: &rc_proto.Value_StringValue{strings.Join(contribAconfigValueSets, " ")}},
+				})
+		}
 		myDirsMap[contrib.DeclarationIndex] = true
 		for _, value := range contrib.FlagValues {
 			fa, ok := myFlags[*value.proto.Name]
@@ -116,27 +159,32 @@
 				// Setting location is to the left of declaration.
 				return fmt.Errorf("Setting value for flag %s not allowed in %s\n", *value.proto.Name, value.path)
 			}
+			if isRoot && *fa.FlagDeclaration.Workflow != workflowManual {
+				// The "root" release config can only contain workflow: MANUAL flags.
+				return fmt.Errorf("Setting value for non-MANUAL flag %s is not allowed in %s", *value.proto.Name, value.path)
+			}
 			if err := fa.UpdateValue(*value); err != nil {
 				return err
 			}
 		}
 	}
+	releaseAconfigValueSets.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{strings.Join(myAconfigValueSets, " ")}}
 
 	directories := []string{}
-	for idx, confDir := range configs.ConfigDirs {
+	for idx, confDir := range configs.configDirs {
 		if _, ok := myDirsMap[idx]; ok {
 			directories = append(directories, confDir)
 		}
 	}
 
 	config.FlagArtifacts = myFlags
-	config.ReleaseConfigArtifact = &release_config_proto.ReleaseConfigArtifact{
+	config.ReleaseConfigArtifact = &rc_proto.ReleaseConfigArtifact{
 		Name:       proto.String(config.Name),
 		OtherNames: config.OtherNames,
-		FlagArtifacts: func() []*release_config_proto.FlagArtifact {
-			ret := []*release_config_proto.FlagArtifact{}
+		FlagArtifacts: func() []*rc_proto.FlagArtifact {
+			ret := []*rc_proto.FlagArtifact{}
 			for _, flag := range myFlags {
-				ret = append(ret, &release_config_proto.FlagArtifact{
+				ret = append(ret, &rc_proto.FlagArtifact{
 					FlagDeclaration: flag.FlagDeclaration,
 					Traces:          flag.Traces,
 					Value:           flag.Value,
diff --git a/cmd/release_config/release_config_lib/release_configs.go b/cmd/release_config/release_config_lib/release_configs.go
index a609b4d..6efdb2f 100644
--- a/cmd/release_config/release_config_lib/release_configs.go
+++ b/cmd/release_config/release_config_lib/release_configs.go
@@ -16,7 +16,6 @@
 
 import (
 	"cmp"
-	"encoding/json"
 	"fmt"
 	"io/fs"
 	"os"
@@ -24,9 +23,8 @@
 	"slices"
 	"strings"
 
-	"android/soong/cmd/release_config/release_config_proto"
+	rc_proto "android/soong/cmd/release_config/release_config_proto"
 
-	"google.golang.org/protobuf/encoding/prototext"
 	"google.golang.org/protobuf/proto"
 )
 
@@ -37,10 +35,13 @@
 	path string
 
 	// Data received
-	proto release_config_proto.ReleaseConfigMap
+	proto rc_proto.ReleaseConfigMap
 
+	// Map of name:contribution for release config contributions.
 	ReleaseConfigContributions map[string]*ReleaseConfigContribution
-	FlagDeclarations           []release_config_proto.FlagDeclaration
+
+	// Flags declared this directory's flag_declarations/*.textproto
+	FlagDeclarations []rc_proto.FlagDeclaration
 }
 
 type ReleaseConfigDirMap map[string]int
@@ -50,50 +51,47 @@
 	// Ordered list of release config maps processed.
 	ReleaseConfigMaps []*ReleaseConfigMap
 
-	// Map of directory to *ReleaseConfigMap
-	ReleaseConfigMapsMap map[string]*ReleaseConfigMap
-
 	// Aliases
 	Aliases map[string]*string
 
 	// Dictionary of flag_name:FlagDeclaration, with no overrides applied.
 	FlagArtifacts FlagArtifacts
 
+	// Generated release configs artifact
+	Artifact rc_proto.ReleaseConfigsArtifact
+
 	// Dictionary of name:ReleaseConfig
+	// Use `GetReleaseConfigs(name)` to get a release config.
 	ReleaseConfigs map[string]*ReleaseConfig
 
-	// Generated release configs
-	Artifact release_config_proto.ReleaseConfigsArtifact
+	// Map of directory to *ReleaseConfigMap
+	releaseConfigMapsMap map[string]*ReleaseConfigMap
 
 	// The list of config directories used.
-	ConfigDirs []string
+	configDirs []string
 
 	// A map from the config directory to its order in the list of config
 	// directories.
-	ConfigDirIndexes ReleaseConfigDirMap
+	configDirIndexes ReleaseConfigDirMap
 }
 
-func (configs *ReleaseConfigs) DumpArtifact(outDir string) error {
-	message := &configs.Artifact
-	basePath := filepath.Join(outDir, "all_release_configs")
-	writer := func(suffix string, marshal func() ([]byte, error)) error {
-		data, err := marshal()
-		if err != nil {
-			return err
-		}
-		return os.WriteFile(fmt.Sprintf("%s.%s", basePath, suffix), data, 0644)
-	}
-	err := writer("textproto", func() ([]byte, error) { return prototext.MarshalOptions{Multiline: true}.Marshal(message) })
-	if err != nil {
-		return err
-	}
-
-	err = writer("pb", func() ([]byte, error) { return proto.Marshal(message) })
-	if err != nil {
-		return err
-	}
-
-	return writer("json", func() ([]byte, error) { return json.MarshalIndent(message, "", "  ") })
+// Write the "all_release_configs" artifact.
+//
+// The file will be in "{outDir}/all_release_configs-{product}.{format}"
+//
+// Args:
+//
+//	outDir string: directory path. Will be created if not present.
+//	product string: TARGET_PRODUCT for the release_configs.
+//	format string: one of "json", "pb", or "textproto"
+//
+// Returns:
+//
+//	error: Any error encountered.
+func (configs *ReleaseConfigs) WriteArtifact(outDir, product, format string) error {
+	return WriteMessage(
+		filepath.Join(outDir, fmt.Sprintf("all_release_configs-%s.%s", product, format)),
+		&configs.Artifact)
 }
 
 func ReleaseConfigsFactory() (c *ReleaseConfigs) {
@@ -101,9 +99,9 @@
 		Aliases:              make(map[string]*string),
 		FlagArtifacts:        make(map[string]*FlagArtifact),
 		ReleaseConfigs:       make(map[string]*ReleaseConfig),
-		ReleaseConfigMapsMap: make(map[string]*ReleaseConfigMap),
-		ConfigDirs:           []string{},
-		ConfigDirIndexes:     make(ReleaseConfigDirMap),
+		releaseConfigMapsMap: make(map[string]*ReleaseConfigMap),
+		configDirs:           []string{},
+		configDirIndexes:     make(ReleaseConfigDirMap),
 	}
 }
 
@@ -113,7 +111,7 @@
 		ReleaseConfigContributions: make(map[string]*ReleaseConfigContribution),
 	}
 	if protoPath != "" {
-		LoadTextproto(protoPath, &m.proto)
+		LoadMessage(protoPath, &m.proto)
 	}
 	return m
 }
@@ -149,7 +147,7 @@
 		}
 		// If the input didn't specify a value, create one (== UnspecifiedValue).
 		if flagDeclaration.Value == nil {
-			flagDeclaration.Value = &release_config_proto.Value{Val: &release_config_proto.Value_UnspecifiedValue{false}}
+			flagDeclaration.Value = &rc_proto.Value{Val: &rc_proto.Value_UnspecifiedValue{false}}
 		}
 		m.FlagDeclarations = append(m.FlagDeclarations, *flagDeclaration)
 		name := *flagDeclaration.Name
@@ -160,7 +158,7 @@
 		}
 		// Set the initial value in the flag artifact.
 		configs.FlagArtifacts[name].UpdateValue(
-			FlagValue{path: path, proto: release_config_proto.FlagValue{
+			FlagValue{path: path, proto: rc_proto.FlagValue{
 				Name: proto.String(name), Value: flagDeclaration.Value}})
 		return nil
 	})
@@ -170,7 +168,7 @@
 
 	err = WalkTextprotoFiles(dir, "release_configs", func(path string, d fs.DirEntry, err error) error {
 		releaseConfigContribution := &ReleaseConfigContribution{path: path, DeclarationIndex: ConfigDirIndex}
-		LoadTextproto(path, &releaseConfigContribution.proto)
+		LoadMessage(path, &releaseConfigContribution.proto)
 		name := *releaseConfigContribution.proto.Name
 		if fmt.Sprintf("%s.textproto", name) != filepath.Base(path) {
 			return fmt.Errorf("%s incorrectly declares release config %s", path, name)
@@ -201,7 +199,7 @@
 		return err
 	}
 	configs.ReleaseConfigMaps = append(configs.ReleaseConfigMaps, m)
-	configs.ReleaseConfigMapsMap[dir] = m
+	configs.releaseConfigMapsMap[dir] = m
 	return nil
 }
 
@@ -217,16 +215,23 @@
 	return nil, fmt.Errorf("Missing config %s.  Trace=%v", name, trace)
 }
 
-func (configs *ReleaseConfigs) DumpMakefile(outDir, targetRelease string) error {
-	outFile := filepath.Join(outDir, "release_config.mk")
+// Write the makefile for this targetRelease.
+func (configs *ReleaseConfigs) WriteMakefile(outFile, targetRelease string) error {
 	makeVars := make(map[string]string)
+	var allReleaseNames []string
+	for _, v := range configs.ReleaseConfigs {
+		allReleaseNames = append(allReleaseNames, v.Name)
+		allReleaseNames = append(allReleaseNames, v.OtherNames...)
+	}
 	config, err := configs.GetReleaseConfig(targetRelease)
 	if err != nil {
 		return err
 	}
+
+	myFlagArtifacts := config.FlagArtifacts.Clone()
 	// Sort the flags by name first.
 	names := []string{}
-	for k, _ := range config.FlagArtifacts {
+	for k, _ := range myFlagArtifacts {
 		names = append(names, k)
 	}
 	slices.SortFunc(names, func(a, b string) int {
@@ -242,12 +247,12 @@
 	}
 
 	for _, name := range names {
-		flag := config.FlagArtifacts[name]
+		flag := myFlagArtifacts[name]
 		decl := flag.FlagDeclaration
 
-		// cName := strings.ToLower(release_config_proto.Container_name[decl.GetContainer()])
+		// cName := strings.ToLower(rc_proto.Container_name[decl.GetContainer()])
 		cName := strings.ToLower(decl.Container.String())
-		if cName == strings.ToLower(release_config_proto.Container_ALL.String()) {
+		if cName == strings.ToLower(rc_proto.Container_ALL.String()) {
 			partitions["product"] = append(partitions["product"], name)
 			partitions["system"] = append(partitions["system"], name)
 			partitions["system_ext"] = append(partitions["system_ext"], name)
@@ -282,7 +287,13 @@
 	//   _ALL_RELEASE_FLAGS.PARTITIONS.*
 	//   all _ALL_RELEASE_FLAGS.*, sorted by name
 	//   Final flag values, sorted by name.
-	data := fmt.Sprintf("_ALL_RELEASE_FLAGS :=$= %s\n", strings.Join(names, " "))
+	data := fmt.Sprintf("# TARGET_RELEASE=%s\n", config.Name)
+	if targetRelease != config.Name {
+		data += fmt.Sprintf("# User specified TARGET_RELEASE=%s\n", targetRelease)
+	}
+	// The variable _all_release_configs will get deleted during processing, so do not mark it read-only.
+	data += fmt.Sprintf("_all_release_configs := %s\n", strings.Join(allReleaseNames, " "))
+	data += fmt.Sprintf("_ALL_RELEASE_FLAGS :=$= %s\n", strings.Join(names, " "))
 	for _, pName := range pNames {
 		data += fmt.Sprintf("_ALL_RELEASE_FLAGS.PARTITIONS.%s :=$= %s\n", pName, strings.Join(partitions[pName], " "))
 	}
@@ -290,8 +301,6 @@
 		data += fmt.Sprintf("%s :=$= %s\n", vName, makeVars[vName])
 	}
 	data += "\n\n# Values for all build flags\n"
-	data += fmt.Sprintf("RELEASE_ACONFIG_VALUE_SETS :=$= %s\n",
-		strings.Join(config.ReleaseConfigArtifact.AconfigValueSets, " "))
 	for _, name := range names {
 		data += fmt.Sprintf("%s :=$= %s\n", name, makeVars[name])
 	}
@@ -326,10 +335,10 @@
 	if err != nil {
 		return err
 	}
-	configs.Artifact = release_config_proto.ReleaseConfigsArtifact{
+	configs.Artifact = rc_proto.ReleaseConfigsArtifact{
 		ReleaseConfig: releaseConfig.ReleaseConfigArtifact,
-		OtherReleaseConfigs: func() []*release_config_proto.ReleaseConfigArtifact {
-			orc := []*release_config_proto.ReleaseConfigArtifact{}
+		OtherReleaseConfigs: func() []*rc_proto.ReleaseConfigArtifact {
+			orc := []*rc_proto.ReleaseConfigArtifact{}
 			for name, config := range configs.ReleaseConfigs {
 				if name != releaseConfig.Name {
 					orc = append(orc, config.ReleaseConfigArtifact)
@@ -337,9 +346,9 @@
 			}
 			return orc
 		}(),
-		ReleaseConfigMapsMap: func() map[string]*release_config_proto.ReleaseConfigMap {
-			ret := make(map[string]*release_config_proto.ReleaseConfigMap)
-			for k, v := range configs.ReleaseConfigMapsMap {
+		ReleaseConfigMapsMap: func() map[string]*rc_proto.ReleaseConfigMap {
+			ret := make(map[string]*rc_proto.ReleaseConfigMap)
+			for k, v := range configs.releaseConfigMapsMap {
 				ret[k] = &v.proto
 			}
 			return ret
@@ -363,8 +372,8 @@
 	for idx, releaseConfigMapPath := range releaseConfigMapPaths {
 		// Maintain an ordered list of release config directories.
 		configDir := filepath.Dir(releaseConfigMapPath)
-		configs.ConfigDirIndexes[configDir] = idx
-		configs.ConfigDirs = append(configs.ConfigDirs, configDir)
+		configs.configDirIndexes[configDir] = idx
+		configs.configDirs = append(configs.configDirs, configDir)
 		err = configs.LoadReleaseConfigMap(releaseConfigMapPath, idx)
 		if err != nil {
 			return nil, err
diff --git a/cmd/release_config/release_config_lib/util.go b/cmd/release_config/release_config_lib/util.go
index b04f344..86940da 100644
--- a/cmd/release_config/release_config_lib/util.go
+++ b/cmd/release_config/release_config_lib/util.go
@@ -15,6 +15,7 @@
 package release_config_lib
 
 import (
+	"encoding/json"
 	"fmt"
 	"io/fs"
 	"os"
@@ -38,15 +39,66 @@
 	return fmt.Sprintf("%v", *l)
 }
 
-func LoadTextproto(path string, message proto.Message) error {
+// Write a marshalled message to a file.
+//
+// Marshal the message based on the extension of the path we are writing it to.
+//
+// Args:
+//
+//	path string: the path of the file to write to.  Directories are not created.
+//	  Supported extensions are: ".json", ".pb", and ".textproto".
+//	message proto.Message: the message to write.
+//
+// Returns:
+//
+//	error: any error encountered.
+func WriteMessage(path string, message proto.Message) (err error) {
+	var data []byte
+	switch filepath.Ext(path) {
+	case ".json":
+		data, err = json.MarshalIndent(message, "", "  ")
+	case ".pb":
+		data, err = proto.Marshal(message)
+	case ".textproto":
+		data, err = prototext.MarshalOptions{Multiline: true}.Marshal(message)
+	default:
+		return fmt.Errorf("Unknown message format for %s", path)
+	}
+	if err != nil {
+		return err
+	}
+	return os.WriteFile(path, data, 0644)
+}
+
+// Read a message from a file.
+//
+// The message is unmarshalled based on the extension of the file read.
+//
+// Args:
+//
+//	path string: the path of the file to read.
+//	message proto.Message: the message to unmarshal the message into.
+//
+// Returns:
+//
+//	error: any error encountered.
+func LoadMessage(path string, message proto.Message) error {
 	data, err := os.ReadFile(path)
 	if err != nil {
 		return err
 	}
-	ret := prototext.Unmarshal(data, message)
-	return ret
+	switch filepath.Ext(path) {
+	case ".json":
+		return json.Unmarshal(data, message)
+	case ".pb":
+		return proto.Unmarshal(data, message)
+	case ".textproto":
+		return prototext.Unmarshal(data, message)
+	}
+	return fmt.Errorf("Unknown message format for %s", path)
 }
 
+// Call Func for any textproto files found in {root}/{subdir}.
 func WalkTextprotoFiles(root string, subdir string, Func fs.WalkDirFunc) error {
 	path := filepath.Join(root, subdir)
 	if _, err := os.Stat(path); err != nil {
@@ -76,6 +128,7 @@
 	return 0, nil
 }
 
+// Returns the default value for release config artifacts.
 func GetDefaultOutDir() string {
 	outEnv := os.Getenv("OUT_DIR")
 	if outEnv == "" {
@@ -84,6 +137,7 @@
 	return filepath.Join(outEnv, "soong", "release-config")
 }
 
+// Return the default list of map files to use.
 func GetDefaultMapPaths() StringList {
 	var defaultMapPaths StringList
 	defaultLocations := StringList{
diff --git a/cmd/release_config/release_config_proto/Android.bp b/cmd/release_config/release_config_proto/Android.bp
index 5a6aeab..8c47f2a 100644
--- a/cmd/release_config/release_config_proto/Android.bp
+++ b/cmd/release_config/release_config_proto/Android.bp
@@ -18,7 +18,7 @@
 
 bootstrap_go_package {
     name: "soong-cmd-release_config-proto",
-    pkgPath: "android/soong/release_config/release_config_proto",
+    pkgPath: "android/soong/cmd/release_config/release_config_proto",
     deps: [
         "golang-protobuf-reflect-protoreflect",
         "golang-protobuf-runtime-protoimpl",