Build proguard zips in soong-only builds

This builds the zips but doesn't dist them, I'll do that in a followup.

Bug: 395160816
Test: Diffed proguard-dict.zip proguard-usage.zip and proguard-dict-mapping.textproto between soong and make
Change-Id: Ic112427e4a2fdaf92a1d0a96d0f1c100b57be37a
diff --git a/cmd/symbols_map/symbols_map.go b/cmd/symbols_map/symbols_map.go
index c56cf93..3955c8a 100644
--- a/cmd/symbols_map/symbols_map.go
+++ b/cmd/symbols_map/symbols_map.go
@@ -72,6 +72,7 @@
 
 	elfFile := flags.String("elf", "", "extract identifier from an elf file")
 	r8File := flags.String("r8", "", "extract identifier from an r8 dictionary")
+	locationFlag := flags.String("location", "", "an override for the value of the location field in the proto. If not specified, the filename will be used")
 	merge := flags.String("merge", "", "merge multiple identifier protos")
 
 	writeIfChanged := flags.Bool("write_if_changed", false, "only write output file if it is modified")
@@ -134,6 +135,10 @@
 		panic("shouldn't get here")
 	}
 
+	if *locationFlag != "" {
+		location = *locationFlag
+	}
+
 	mapping := symbols_map_proto.Mapping{
 		Identifier: proto.String(identifier),
 		Location:   proto.String(location),
diff --git a/filesystem/android_device.go b/filesystem/android_device.go
index 959ef37..3b8bf93 100644
--- a/filesystem/android_device.go
+++ b/filesystem/android_device.go
@@ -17,16 +17,24 @@
 import (
 	"cmp"
 	"fmt"
+	"path/filepath"
 	"slices"
 	"strings"
 	"sync/atomic"
 
 	"android/soong/android"
+	"android/soong/java"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
+var proguardDictToProto = pctx.AndroidStaticRule("proguard_dict_to_proto", blueprint.RuleParams{
+	Command:     `${symbols_map} -r8 $in -location $location -write_if_changed $out`,
+	Restat:      true,
+	CommandDeps: []string{"${symbols_map}"},
+}, "location")
+
 type PartitionNameProperties struct {
 	// Name of the super partition filesystem module
 	Super_partition_name *string
@@ -161,7 +169,11 @@
 		}
 	}
 
+	allInstalledModules := a.allInstalledModules(ctx)
+
 	a.buildTargetFilesZip(ctx)
+	a.buildProguardZips(ctx, allInstalledModules)
+
 	var deps []android.Path
 	if proptools.String(a.partitionProps.Super_partition_name) != "" {
 		superImage := ctx.GetDirectDepProxyWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag)
@@ -305,6 +317,52 @@
 	}
 }
 
+func (a *androidDevice) buildProguardZips(ctx android.ModuleContext, allInstalledModules []android.Module) {
+	dictZip := android.PathForModuleOut(ctx, "proguard-dict.zip")
+	dictZipBuilder := android.NewRuleBuilder(pctx, ctx)
+	dictZipCmd := dictZipBuilder.Command().BuiltTool("soong_zip").Flag("-d").FlagWithOutput("-o ", dictZip)
+
+	dictMapping := android.PathForModuleOut(ctx, "proguard-dict-mapping.textproto")
+	dictMappingBuilder := android.NewRuleBuilder(pctx, ctx)
+	dictMappingCmd := dictMappingBuilder.Command().BuiltTool("symbols_map").Flag("-merge").Output(dictMapping)
+
+	protosDir := android.PathForModuleOut(ctx, "proguard_mapping_protos")
+
+	usageZip := android.PathForModuleOut(ctx, "proguard-usage.zip")
+	usageZipBuilder := android.NewRuleBuilder(pctx, ctx)
+	usageZipCmd := usageZipBuilder.Command().BuiltTool("merge_zips").Output(usageZip)
+
+	for _, mod := range allInstalledModules {
+		if proguardInfo, ok := android.OtherModuleProvider(ctx, mod, java.ProguardProvider); ok {
+			// Maintain these out/target/common paths for backwards compatibility. They may be able
+			// to be changed if tools look up file locations from the protobuf, but I'm not
+			// exactly sure how that works.
+			dictionaryFakePath := fmt.Sprintf("out/target/common/obj/%s/%s_intermediates/proguard_dictionary", proguardInfo.Class, proguardInfo.ModuleName)
+			dictZipCmd.FlagWithArg("-e ", dictionaryFakePath)
+			dictZipCmd.FlagWithInput("-f ", proguardInfo.ProguardDictionary)
+			dictZipCmd.Textf("-e out/target/common/obj/%s/%s_intermediates/classes.jar", proguardInfo.Class, proguardInfo.ModuleName)
+			dictZipCmd.FlagWithInput("-f ", proguardInfo.ClassesJar)
+
+			protoFile := protosDir.Join(ctx, filepath.Dir(dictionaryFakePath), "proguard_dictionary.textproto")
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   proguardDictToProto,
+				Input:  proguardInfo.ProguardDictionary,
+				Output: protoFile,
+				Args: map[string]string{
+					"location": dictionaryFakePath,
+				},
+			})
+			dictMappingCmd.Input(protoFile)
+
+			usageZipCmd.Input(proguardInfo.ProguardUsageZip)
+		}
+	}
+
+	dictZipBuilder.Build("proguard_dict_zip", "Building proguard dictionary zip")
+	dictMappingBuilder.Build("proguard_dict_mapping_proto", "Building proguard mapping proto")
+	usageZipBuilder.Build("proguard_usage_zip", "Building proguard usage zip")
+}
+
 // Helper structs for target_files.zip creation
 type targetFilesZipCopy struct {
 	srcModule  *string
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 1ce6131..28eb36d 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -33,11 +33,14 @@
 	"github.com/google/blueprint/proptools"
 )
 
+var pctx = android.NewPackageContext("android/soong/filesystem")
+
 func init() {
 	registerBuildComponents(android.InitRegistrationContext)
 	registerMutators(android.InitRegistrationContext)
 	pctx.HostBinToolVariable("fileslist", "fileslist")
 	pctx.HostBinToolVariable("fs_config", "fs_config")
+	pctx.HostBinToolVariable("symbols_map", "symbols_map")
 }
 
 func registerBuildComponents(ctx android.RegistrationContext) {
@@ -576,8 +579,6 @@
 	return txt, json
 }
 
-var pctx = android.NewPackageContext("android/soong/filesystem")
-
 func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	validatePartitionType(ctx, f)
 	if f.filesystemBuilder.ShouldUseVintfFragmentModuleOnly() {
diff --git a/java/app.go b/java/app.go
index 9b10bf3..7580db4 100644
--- a/java/app.go
+++ b/java/app.go
@@ -452,6 +452,16 @@
 	android.SetProvider(ctx, AppInfoProvider, appInfo)
 
 	a.requiredModuleNames = a.getRequiredModuleNames(ctx)
+
+	if a.dexer.proguardDictionary.Valid() {
+		android.SetProvider(ctx, ProguardProvider, ProguardInfo{
+			ModuleName:         ctx.ModuleName(),
+			Class:              "APPS",
+			ProguardDictionary: a.dexer.proguardDictionary.Path(),
+			ProguardUsageZip:   a.dexer.proguardUsageZip.Path(),
+			ClassesJar:         a.implementationAndResourcesJar,
+		})
+	}
 }
 
 func (a *AndroidApp) getRequiredModuleNames(ctx android.ModuleContext) []string {
diff --git a/java/dex.go b/java/dex.go
index 311657f..ed2df21 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -697,3 +697,13 @@
 
 	return javalibJar, artProfileOutputPath
 }
+
+type ProguardInfo struct {
+	ModuleName         string
+	Class              string
+	ProguardDictionary android.Path
+	ProguardUsageZip   android.Path
+	ClassesJar         android.Path
+}
+
+var ProguardProvider = blueprint.NewProvider[ProguardInfo]()
diff --git a/java/java.go b/java/java.go
index b9109ee..5056120 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1172,6 +1172,16 @@
 	buildComplianceMetadata(ctx)
 
 	j.createApiXmlFile(ctx)
+
+	if j.dexer.proguardDictionary.Valid() {
+		android.SetProvider(ctx, ProguardProvider, ProguardInfo{
+			ModuleName:         ctx.ModuleName(),
+			Class:              "JAVA_LIBRARIES",
+			ProguardDictionary: j.dexer.proguardDictionary.Path(),
+			ProguardUsageZip:   j.dexer.proguardUsageZip.Path(),
+			ClassesJar:         j.implementationAndResourcesJar,
+		})
+	}
 }
 
 func (j *Library) javaLibraryModuleInfoJSON(ctx android.ModuleContext) *android.ModuleInfoJSON {