Find subdir aconfig_values from srcs

This change supports adding dependencies from the aconfig_value_set
module to the aconfig_values modules from `srcs`, which are the
paths to the Android.bp files where the aconfig_value modules are
defined.

Test: m nothing --no-skip-soong-tests
Bug: 365827715
Change-Id: Idd5f1d9c2625a6362dbaf3adba0a74406e3a9928
diff --git a/aconfig/aconfig_value_set.go b/aconfig/aconfig_value_set.go
index 7ba76c0..d72ec48 100644
--- a/aconfig/aconfig_value_set.go
+++ b/aconfig/aconfig_value_set.go
@@ -16,6 +16,9 @@
 
 import (
 	"android/soong/android"
+	"fmt"
+	"strings"
+
 	"github.com/google/blueprint"
 )
 
@@ -27,6 +30,9 @@
 	properties struct {
 		// aconfig_values modules
 		Values []string
+
+		// Paths to the Android.bp files where the aconfig_values modules are defined.
+		Srcs []string
 	}
 }
 
@@ -56,7 +62,35 @@
 
 var valueSetProviderKey = blueprint.NewProvider[valueSetProviderData]()
 
+func (module *ValueSetModule) FindAconfigValuesFromSrc(ctx android.BottomUpMutatorContext) map[string]android.Path {
+	moduleDir := ctx.ModuleDir()
+	srcs := android.PathsForModuleSrcExcludes(ctx, module.properties.Srcs, []string{ctx.BlueprintsFile()})
+
+	aconfigValuesPrefix := strings.Replace(module.Name(), "aconfig_value_set", "aconfig-values", 1)
+	moduleNamesSrcMap := make(map[string]android.Path)
+	for _, src := range srcs {
+		subDir := strings.TrimPrefix(src.String(), moduleDir+"/")
+		packageName, _, found := strings.Cut(subDir, "/")
+		if found {
+			moduleName := fmt.Sprintf("%s-%s-all", aconfigValuesPrefix, packageName)
+			moduleNamesSrcMap[moduleName] = src
+		}
+	}
+	return moduleNamesSrcMap
+}
+
 func (module *ValueSetModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+
+	// TODO: b/366285733 - Replace the file path based solution with more robust solution.
+	aconfigValuesMap := module.FindAconfigValuesFromSrc(ctx)
+	for _, moduleName := range android.SortedKeys(aconfigValuesMap) {
+		if ctx.OtherModuleExists(moduleName) {
+			ctx.AddDependency(ctx.Module(), valueSetTag, moduleName)
+		} else {
+			ctx.ModuleErrorf("module %q not found. Rename the aconfig_values module defined in %q to %q", moduleName, aconfigValuesMap[moduleName], moduleName)
+		}
+	}
+
 	deps := ctx.AddDependency(ctx.Module(), valueSetTag, module.properties.Values...)
 	for _, dep := range deps {
 		_, ok := dep.(*ValuesModule)
diff --git a/aconfig/aconfig_value_set_test.go b/aconfig/aconfig_value_set_test.go
index 32c31cb..3b7281e 100644
--- a/aconfig/aconfig_value_set_test.go
+++ b/aconfig/aconfig_value_set_test.go
@@ -18,6 +18,8 @@
 	"testing"
 
 	"android/soong/android"
+
+	"github.com/google/blueprint"
 )
 
 func TestAconfigValueSet(t *testing.T) {
@@ -41,3 +43,112 @@
 	depData, _ := android.OtherModuleProvider(result, module, valueSetProviderKey)
 	android.AssertStringEquals(t, "AvailablePackages", "blah.aconfig_values", depData.AvailablePackages["foo.package"][0].String())
 }
+
+func TestAconfigValueSetBpGlob(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithAconfigBuildComponents,
+		android.FixtureMergeMockFs(
+			map[string][]byte{
+				// .../some_release/android.foo/
+				"some_release/android.foo/Android.bp": []byte(`
+				aconfig_values {
+					name: "aconfig-values-platform_build_release-some_release-android.foo-all",
+					package: "android.foo",
+					srcs: [
+						"*.textproto",
+					],
+				}
+				`),
+				"some_release/android.foo/flag.textproto": nil,
+
+				// .../some_release/android.bar/
+				"some_release/android.bar/Android.bp": []byte(`
+				aconfig_values {
+					name: "aconfig-values-platform_build_release-some_release-android.bar-all",
+					package: "android.bar",
+					srcs: [
+						"*.textproto",
+					],
+				}
+				`),
+				"some_release/android.bar/flag.textproto": nil,
+
+				// .../some_release/
+				"some_release/Android.bp": []byte(`
+				aconfig_value_set {
+					name: "aconfig_value_set-platform_build_release-some_release",
+					srcs: [
+						"*/Android.bp",
+					],
+				}
+				`),
+			},
+		),
+	).RunTest(t)
+
+	checkModuleHasDependency := func(name, variant, dep string) bool {
+		t.Helper()
+		module := result.ModuleForTests(name, variant).Module()
+		depFound := false
+		result.VisitDirectDeps(module, func(m blueprint.Module) {
+			if m.Name() == dep {
+				depFound = true
+			}
+		})
+		return depFound
+	}
+	android.AssertBoolEquals(t,
+		"aconfig_value_set expected to depend on aconfig_value via srcs",
+		true,
+		checkModuleHasDependency(
+			"aconfig_value_set-platform_build_release-some_release",
+			"",
+			"aconfig-values-platform_build_release-some_release-android.foo-all",
+		),
+	)
+	android.AssertBoolEquals(t,
+		"aconfig_value_set expected to depend on aconfig_value via srcs",
+		true,
+		checkModuleHasDependency(
+			"aconfig_value_set-platform_build_release-some_release",
+			"",
+			"aconfig-values-platform_build_release-some_release-android.bar-all",
+		),
+	)
+}
+
+func TestAconfigValueSetBpGlobError(t *testing.T) {
+	android.GroupFixturePreparers(
+		PrepareForTestWithAconfigBuildComponents,
+		android.FixtureMergeMockFs(
+			map[string][]byte{
+				// .../some_release/android.bar/
+				"some_release/android.bar/Android.bp": []byte(`
+				aconfig_values {
+					name: "aconfig-values-platform_build_release-some_release-android_bar-all",
+					package: "android.bar",
+					srcs: [
+						"*.textproto",
+					],
+				}
+				`),
+				"some_release/android.bar/flag.textproto": nil,
+
+				// .../some_release/
+				"some_release/Android.bp": []byte(`
+				aconfig_value_set {
+					name: "aconfig_value_set-platform_build_release-some_release",
+					srcs: [
+						"*/Android.bp",
+					],
+				}
+				`),
+			},
+		),
+	).ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+		`module "aconfig_value_set-platform_build_release-some_release": module ` +
+			`"aconfig-values-platform_build_release-some_release-android.bar-all" not found. ` +
+			`Rename the aconfig_values module defined in "some_release/android.bar/Android.bp" ` +
+			`to "aconfig-values-platform_build_release-some_release-android.bar-all"`),
+	).RunTest(t)
+}