release_config: better default map paths

- Automatically determine the top of the workspace.
- build-flag defaults to using `get_build_var` to get product specific
  release config maps.
- release-config defaults to using PRODUCT_RELEASE_CONFIG_MAPS but does
  not use `get_build_var` unless the argument is given.

Bug: 328495189
Test: manual
Change-Id: I4ba3c5dfab43c4ebc3eeda13318f42e886dada4e
diff --git a/cmd/release_config/build_flag/main.go b/cmd/release_config/build_flag/main.go
index 6f909af..a8e6326 100644
--- a/cmd/release_config/build_flag/main.go
+++ b/cmd/release_config/build_flag/main.go
@@ -184,6 +184,7 @@
 	var err error
 	var commonFlags Flags
 	var configs *rc_lib.ReleaseConfigs
+	var useBuildVar bool
 
 	outEnv := os.Getenv("OUT_DIR")
 	if outEnv == "" {
@@ -195,6 +196,7 @@
 	flag.Var(&commonFlags.maps, "map", "path to a release_config_map.textproto. may be repeated")
 	flag.StringVar(&commonFlags.outDir, "out_dir", rc_lib.GetDefaultOutDir(), "basepath for the output. Multiple formats are created")
 	flag.Var(&commonFlags.targetReleases, "release", "TARGET_RELEASE for this build")
+	flag.BoolVar(&useBuildVar, "use_get_build_var", true, "use get_build_var PRODUCT_RELEASE_CONFIG_MAPS")
 	flag.Parse()
 
 	if commonFlags.quiet {
@@ -215,7 +217,7 @@
 		// If the users said `--release --all`, grab trunk staging for simplicity.
 		relName = "trunk_staging"
 	}
-	configs, err = rc_lib.ReadReleaseConfigMaps(commonFlags.maps, relName)
+	configs, err = rc_lib.ReadReleaseConfigMaps(commonFlags.maps, relName, true)
 	if err != nil {
 		panic(err)
 	}
diff --git a/cmd/release_config/crunch_flags/main.go b/cmd/release_config/crunch_flags/main.go
index 5b64062..b67374a 100644
--- a/cmd/release_config/crunch_flags/main.go
+++ b/cmd/release_config/crunch_flags/main.go
@@ -311,8 +311,9 @@
 	var dirs rc_lib.StringList
 	var namespacesFile string
 	var descriptionsFile string
+	defaultTopDir, err := rc_lib.GetTopDir()
 
-	flag.StringVar(&top, "top", ".", "path to top of workspace")
+	flag.StringVar(&top, "top", defaultTopDir, "path to top of workspace")
 	flag.Var(&dirs, "dir", "directory to process, relative to the top of the workspace")
 	flag.StringVar(&namespacesFile, "namespaces", "", "location of file with 'flag_name namespace' information")
 	flag.StringVar(&descriptionsFile, "descriptions", "", "location of file with 'directory description' information")
diff --git a/cmd/release_config/release_config/main.go b/cmd/release_config/release_config/main.go
index 22e72a5..c443257 100644
--- a/cmd/release_config/release_config/main.go
+++ b/cmd/release_config/release_config/main.go
@@ -34,6 +34,7 @@
 	var json, pb, textproto bool
 	var product string
 	var allMake bool
+	var useBuildVar bool
 
 	defaultRelease := os.Getenv("TARGET_RELEASE")
 	if defaultRelease == "" {
@@ -50,6 +51,8 @@
 	flag.BoolVar(&json, "json", true, "write artifacts as json")
 	flag.BoolVar(&pb, "pb", true, "write artifacts as binary protobuf")
 	flag.BoolVar(&allMake, "all_make", true, "write makefiles for all release configs")
+	flag.BoolVar(&useBuildVar, "use_get_build_var", false, "use get_build_var PRODUCT_RELEASE_CONFIG_MAPS")
+
 	flag.Parse()
 
 	if quiet {
@@ -59,7 +62,7 @@
 	if err = os.Chdir(top); err != nil {
 		panic(err)
 	}
-	configs, err = rc_lib.ReadReleaseConfigMaps(releaseConfigMapPaths, targetRelease)
+	configs, err = rc_lib.ReadReleaseConfigMaps(releaseConfigMapPaths, targetRelease, useBuildVar)
 	if err != nil {
 		panic(err)
 	}
diff --git a/cmd/release_config/release_config_lib/release_configs.go b/cmd/release_config/release_config_lib/release_configs.go
index d7d4a8c..6b47091 100644
--- a/cmd/release_config/release_config_lib/release_configs.go
+++ b/cmd/release_config/release_config_lib/release_configs.go
@@ -365,11 +365,14 @@
 	return nil
 }
 
-func ReadReleaseConfigMaps(releaseConfigMapPaths StringList, targetRelease string) (*ReleaseConfigs, error) {
+func ReadReleaseConfigMaps(releaseConfigMapPaths StringList, targetRelease string, useBuildVar bool) (*ReleaseConfigs, error) {
 	var err error
 
 	if len(releaseConfigMapPaths) == 0 {
-		releaseConfigMapPaths = GetDefaultMapPaths()
+		releaseConfigMapPaths, err = GetDefaultMapPaths(useBuildVar)
+		if err != nil {
+			return nil, err
+		}
 		if len(releaseConfigMapPaths) == 0 {
 			return nil, fmt.Errorf("No maps found")
 		}
diff --git a/cmd/release_config/release_config_lib/util.go b/cmd/release_config/release_config_lib/util.go
index d55cf29..52a1f85 100644
--- a/cmd/release_config/release_config_lib/util.go
+++ b/cmd/release_config/release_config_lib/util.go
@@ -19,6 +19,7 @@
 	"fmt"
 	"io/fs"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"regexp"
 	"strings"
@@ -145,22 +146,64 @@
 	return filepath.Join(outEnv, "soong", "release-config")
 }
 
+// Find the top of the workspace.
+//
+// This mirrors the logic in build/envsetup.sh's gettop().
+func GetTopDir() (topDir string, err error) {
+	workingDir, err := os.Getwd()
+	if err != nil {
+		return
+	}
+	topFile := "build/make/core/envsetup.mk"
+	for topDir = workingDir; topDir != "/"; topDir = filepath.Dir(topDir) {
+		if _, err = os.Stat(filepath.Join(topDir, topFile)); err == nil {
+			return filepath.Rel(workingDir, topDir)
+		}
+	}
+	return "", fmt.Errorf("Unable to locate top of workspace")
+}
+
 // Return the default list of map files to use.
-func GetDefaultMapPaths() StringList {
+func GetDefaultMapPaths(queryMaps bool) (defaultLocations StringList, err error) {
 	var defaultMapPaths StringList
-	defaultLocations := StringList{
+	workingDir, err := os.Getwd()
+	if err != nil {
+		return
+	}
+	defer func() {
+		os.Chdir(workingDir)
+	}()
+	topDir, err := GetTopDir()
+	os.Chdir(topDir)
+
+	defaultLocations = StringList{
 		"build/release/release_config_map.textproto",
 		"vendor/google_shared/build/release/release_config_map.textproto",
 		"vendor/google/release/release_config_map.textproto",
 	}
 	for _, path := range defaultLocations {
-		if _, err := os.Stat(path); err == nil {
+		if _, err = os.Stat(path); err == nil {
 			defaultMapPaths = append(defaultMapPaths, path)
 		}
 	}
-	prodMaps := os.Getenv("PRODUCT_RELEASE_CONFIG_MAPS")
-	if prodMaps != "" {
+
+	var prodMaps string
+	if queryMaps {
+		getBuildVar := exec.Command("build/soong/soong_ui.bash", "--dumpvar-mode", "PRODUCT_RELEASE_CONFIG_MAPS")
+		var stdout strings.Builder
+		getBuildVar.Stdin = strings.NewReader("")
+		getBuildVar.Stdout = &stdout
+		err = getBuildVar.Run()
+		if err != nil {
+			return
+		}
+		prodMaps = stdout.String()
+	} else {
+		prodMaps = os.Getenv("PRODUCT_RELEASE_CONFIG_MAPS")
+	}
+	prodMaps = strings.TrimSpace(prodMaps)
+	if len(prodMaps) > 0 {
 		defaultMapPaths = append(defaultMapPaths, strings.Split(prodMaps, " ")...)
 	}
-	return defaultMapPaths
+	return
 }