From 95d709402a74e7831460df3b6d06fd84bb5dcf1b Mon Sep 17 00:00:00 2001 From: Sam Delmerico Date: Wed, 2 Aug 2023 18:00:35 -0400 Subject: [PATCH] export_proguard_spec for libs deps Add a property to export proguard flags files for libs dependencies. Currently only proguard flags files from static deps are propagated up to reverse dependencies, but it is necessary sometimes to have flags from libs dependencies also be propagated. Bug: 289087274 Test: go test ./java Change-Id: Ic0aa22b086792bf322041aa5780db6c4f4eb2770 --- java/aar.go | 28 +++--- java/app.go | 10 +- java/base.go | 43 +++++++++ java/dex.go | 4 + java/dex_test.go | 246 ++++++++++++++++++++++++++++++++++++++++++++++- java/java.go | 40 ++++---- 6 files changed, 327 insertions(+), 44 deletions(-) diff --git a/java/aar.go b/java/aar.go index 1e38efc59..511add6bf 100644 --- a/java/aar.go +++ b/java/aar.go @@ -29,7 +29,6 @@ import ( ) type AndroidLibraryDependency interface { - LibraryDependency ExportPackage() android.Path ResourcesNodeDepSet() *android.DepSet[*resourcesNode] RRODirsDepSet() *android.DepSet[rroDir] @@ -777,17 +776,9 @@ func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) ctx.CheckbuildFile(a.aarFile) } - a.exportedProguardFlagFiles = append(a.exportedProguardFlagFiles, - android.PathsForModuleSrc(ctx, a.dexProperties.Optimize.Proguard_flags_files)...) - - ctx.VisitDirectDeps(func(m android.Module) { - if ctx.OtherModuleDependencyTag(m) == staticLibTag { - if lib, ok := m.(LibraryDependency); ok { - a.exportedProguardFlagFiles = append(a.exportedProguardFlagFiles, lib.ExportedProguardFlagFiles()...) - } - } - }) - a.exportedProguardFlagFiles = android.FirstUniquePaths(a.exportedProguardFlagFiles) + proguardSpecInfo := a.collectProguardSpecInfo(ctx) + ctx.SetProvider(ProguardSpecInfoProvider, proguardSpecInfo) + a.exportedProguardFlagFiles = proguardSpecInfo.ProguardFlagsFiles.ToList() prebuiltJniPackages := android.Paths{} ctx.VisitDirectDeps(func(module android.Module) { @@ -938,10 +929,6 @@ var _ AndroidLibraryDependency = (*AARImport)(nil) func (a *AARImport) ExportPackage() android.Path { return a.exportPackage } -func (a *AARImport) ExportedProguardFlagFiles() android.Paths { - return android.Paths{a.proguardFlags} -} - func (a *AARImport) ResourcesNodeDepSet() *android.DepSet[*resourcesNode] { return a.resourcesNodesDepSet } @@ -1045,10 +1032,17 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { extractedAARDir := android.PathForModuleOut(ctx, "aar") a.classpathFile = extractedAARDir.Join(ctx, "classes-combined.jar") - a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt") a.manifest = extractedAARDir.Join(ctx, "AndroidManifest.xml") aarRTxt := extractedAARDir.Join(ctx, "R.txt") a.assetsPackage = android.PathForModuleOut(ctx, "assets.zip") + a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt") + ctx.SetProvider(ProguardSpecInfoProvider, ProguardSpecInfo{ + ProguardFlagsFiles: android.NewDepSet[android.Path]( + android.POSTORDER, + android.Paths{a.proguardFlags}, + nil, + ), + }) ctx.Build(pctx, android.BuildParams{ Rule: unzipAAR, diff --git a/java/app.go b/java/app.go index e277aed55..ab26ced50 100755 --- a/java/app.go +++ b/java/app.go @@ -200,10 +200,6 @@ func (a *AndroidApp) IsInstallable() bool { return Bool(a.properties.Installable) } -func (a *AndroidApp) ExportedProguardFlagFiles() android.Paths { - return nil -} - func (a *AndroidApp) ResourcesNodeDepSet() *android.DepSet[*resourcesNode] { return a.aapt.resourcesNodesDepSet } @@ -482,8 +478,10 @@ func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) { func (a *AndroidApp) proguardBuildActions(ctx android.ModuleContext) { var staticLibProguardFlagFiles android.Paths ctx.VisitDirectDeps(func(m android.Module) { - if lib, ok := m.(LibraryDependency); ok && ctx.OtherModuleDependencyTag(m) == staticLibTag { - staticLibProguardFlagFiles = append(staticLibProguardFlagFiles, lib.ExportedProguardFlagFiles()...) + depProguardInfo := ctx.OtherModuleProvider(m, ProguardSpecInfoProvider).(ProguardSpecInfo) + staticLibProguardFlagFiles = append(staticLibProguardFlagFiles, depProguardInfo.UnconditionallyExportedProguardFlags.ToList()...) + if ctx.OtherModuleDependencyTag(m) == staticLibTag { + staticLibProguardFlagFiles = append(staticLibProguardFlagFiles, depProguardInfo.ProguardFlagsFiles.ToList()...) } }) diff --git a/java/base.go b/java/base.go index f5eb01c4b..a9b986b56 100644 --- a/java/base.go +++ b/java/base.go @@ -1670,6 +1670,49 @@ func (j *Module) useCompose() bool { return android.InList("androidx.compose.runtime_runtime", j.properties.Static_libs) } +func (j *Module) collectProguardSpecInfo(ctx android.ModuleContext) ProguardSpecInfo { + transitiveUnconditionalExportedFlags := []*android.DepSet[android.Path]{} + transitiveProguardFlags := []*android.DepSet[android.Path]{} + + ctx.VisitDirectDeps(func(m android.Module) { + depProguardInfo := ctx.OtherModuleProvider(m, ProguardSpecInfoProvider).(ProguardSpecInfo) + depTag := ctx.OtherModuleDependencyTag(m) + + if depProguardInfo.UnconditionallyExportedProguardFlags != nil { + transitiveUnconditionalExportedFlags = append(transitiveUnconditionalExportedFlags, depProguardInfo.UnconditionallyExportedProguardFlags) + transitiveProguardFlags = append(transitiveProguardFlags, depProguardInfo.UnconditionallyExportedProguardFlags) + } + + if depTag == staticLibTag && depProguardInfo.ProguardFlagsFiles != nil { + transitiveProguardFlags = append(transitiveProguardFlags, depProguardInfo.ProguardFlagsFiles) + } + }) + + directUnconditionalExportedFlags := android.Paths{} + proguardFlagsForThisModule := android.PathsForModuleSrc(ctx, j.dexProperties.Optimize.Proguard_flags_files) + exportUnconditionally := proptools.Bool(j.dexProperties.Optimize.Export_proguard_flags_files) + if exportUnconditionally { + // if we explicitly export, then our unconditional exports are the same as our transitive flags + transitiveUnconditionalExportedFlags = transitiveProguardFlags + directUnconditionalExportedFlags = proguardFlagsForThisModule + } + + return ProguardSpecInfo{ + Export_proguard_flags_files: exportUnconditionally, + ProguardFlagsFiles: android.NewDepSet[android.Path]( + android.POSTORDER, + proguardFlagsForThisModule, + transitiveProguardFlags, + ), + UnconditionallyExportedProguardFlags: android.NewDepSet[android.Path]( + android.POSTORDER, + directUnconditionalExportedFlags, + transitiveUnconditionalExportedFlags, + ), + } + +} + // Returns a copy of the supplied flags, but with all the errorprone-related // fields copied to the regular build's fields. func enableErrorproneFlags(flags javaBuilderFlags) javaBuilderFlags { diff --git a/java/dex.go b/java/dex.go index 7e7da00fd..df501bffa 100644 --- a/java/dex.go +++ b/java/dex.go @@ -71,6 +71,10 @@ type DexProperties struct { // Specifies the locations of files containing proguard flags. Proguard_flags_files []string `android:"path"` + + // If true, transitive reverse dependencies of this module will have this + // module's proguard spec appended to their optimization action + Export_proguard_flags_files *bool } // Keep the data uncompressed. We always need uncompressed dex for execution, diff --git a/java/dex_test.go b/java/dex_test.go index 2ba3831f4..ec1ef1516 100644 --- a/java/dex_test.go +++ b/java/dex_test.go @@ -15,6 +15,7 @@ package java import ( + "fmt" "testing" "android/soong/android" @@ -327,7 +328,7 @@ func TestD8(t *testing.T) { fooD8.Args["d8Flags"], staticLibHeader.String()) } -func TestProguardFlagsInheritance(t *testing.T) { +func TestProguardFlagsInheritanceStatic(t *testing.T) { result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, ` android_app { name: "app", @@ -380,3 +381,246 @@ func TestProguardFlagsInheritance(t *testing.T) { android.AssertStringDoesContain(t, "expected tertiary_lib's proguard flags from inherited dep", appR8.Args["r8Flags"], "tertiary.flags") } + +func TestProguardFlagsInheritance(t *testing.T) { + directDepFlagsFileName := "direct_dep.flags" + transitiveDepFlagsFileName := "transitive_dep.flags" + bp := ` + android_app { + name: "app", + static_libs: ["androidlib"], // this must be static_libs to initate dexing + platform_apis: true, + } + + android_library { + name: "androidlib", + static_libs: ["app_dep"], + } + + java_library { + name: "app_dep", + %s: ["dep"], + } + + java_library { + name: "dep", + %s: ["transitive_dep"], + optimize: { + proguard_flags_files: ["direct_dep.flags"], + export_proguard_flags_files: %v, + }, + } + + java_library { + name: "transitive_dep", + optimize: { + proguard_flags_files: ["transitive_dep.flags"], + export_proguard_flags_files: %v, + }, + } + ` + + testcases := []struct { + name string + depType string + depExportsFlagsFiles bool + transitiveDepType string + transitiveDepExportsFlagsFiles bool + expectedFlagsFiles []string + }{ + { + name: "libs_export_libs_export", + depType: "libs", + depExportsFlagsFiles: true, + transitiveDepType: "libs", + transitiveDepExportsFlagsFiles: true, + expectedFlagsFiles: []string{directDepFlagsFileName, transitiveDepFlagsFileName}, + }, + { + name: "static_export_libs_export", + depType: "static_libs", + depExportsFlagsFiles: true, + transitiveDepType: "libs", + transitiveDepExportsFlagsFiles: true, + expectedFlagsFiles: []string{directDepFlagsFileName, transitiveDepFlagsFileName}, + }, + { + name: "libs_no-export_static_export", + depType: "libs", + depExportsFlagsFiles: false, + transitiveDepType: "static_libs", + transitiveDepExportsFlagsFiles: true, + expectedFlagsFiles: []string{transitiveDepFlagsFileName}, + }, + { + name: "static_no-export_static_export", + depType: "static_libs", + depExportsFlagsFiles: false, + transitiveDepType: "static_libs", + transitiveDepExportsFlagsFiles: true, + expectedFlagsFiles: []string{directDepFlagsFileName, transitiveDepFlagsFileName}, + }, + { + name: "libs_export_libs_no-export", + depType: "libs", + depExportsFlagsFiles: true, + transitiveDepType: "libs", + transitiveDepExportsFlagsFiles: false, + expectedFlagsFiles: []string{directDepFlagsFileName}, + }, + { + name: "static_export_libs_no-export", + depType: "static_libs", + depExportsFlagsFiles: true, + transitiveDepType: "libs", + transitiveDepExportsFlagsFiles: false, + expectedFlagsFiles: []string{directDepFlagsFileName}, + }, + { + name: "libs_no-export_static_no-export", + depType: "libs", + depExportsFlagsFiles: false, + transitiveDepType: "static_libs", + transitiveDepExportsFlagsFiles: false, + expectedFlagsFiles: []string{}, + }, + { + name: "static_no-export_static_no-export", + depType: "static_libs", + depExportsFlagsFiles: false, + transitiveDepType: "static_libs", + transitiveDepExportsFlagsFiles: false, + expectedFlagsFiles: []string{directDepFlagsFileName, transitiveDepFlagsFileName}, + }, + { + name: "libs_no-export_libs_export", + depType: "libs", + depExportsFlagsFiles: false, + transitiveDepType: "libs", + transitiveDepExportsFlagsFiles: true, + expectedFlagsFiles: []string{transitiveDepFlagsFileName}, + }, + { + name: "static_no-export_libs_export", + depType: "static_libs", + depExportsFlagsFiles: false, + transitiveDepType: "libs", + transitiveDepExportsFlagsFiles: true, + expectedFlagsFiles: []string{directDepFlagsFileName, transitiveDepFlagsFileName}, + }, + { + name: "libs_export_static_export", + depType: "libs", + depExportsFlagsFiles: true, + transitiveDepType: "static_libs", + transitiveDepExportsFlagsFiles: true, + expectedFlagsFiles: []string{directDepFlagsFileName, transitiveDepFlagsFileName}, + }, + { + name: "static_export_static_export", + depType: "static_libs", + depExportsFlagsFiles: true, + transitiveDepType: "static_libs", + transitiveDepExportsFlagsFiles: true, + expectedFlagsFiles: []string{directDepFlagsFileName, transitiveDepFlagsFileName}, + }, + { + name: "libs_no-export_libs_no-export", + depType: "libs", + depExportsFlagsFiles: false, + transitiveDepType: "libs", + transitiveDepExportsFlagsFiles: false, + expectedFlagsFiles: []string{}, + }, + { + name: "static_no-export_libs_no-export", + depType: "static_libs", + depExportsFlagsFiles: false, + transitiveDepType: "libs", + transitiveDepExportsFlagsFiles: false, + expectedFlagsFiles: []string{directDepFlagsFileName}, + }, + { + name: "libs_export_static_no-export", + depType: "libs", + depExportsFlagsFiles: true, + transitiveDepType: "static_libs", + transitiveDepExportsFlagsFiles: false, + expectedFlagsFiles: []string{directDepFlagsFileName, transitiveDepFlagsFileName}, + }, + { + name: "static_export_static_no-export", + depType: "static_libs", + depExportsFlagsFiles: true, + transitiveDepType: "static_libs", + transitiveDepExportsFlagsFiles: false, + expectedFlagsFiles: []string{directDepFlagsFileName, transitiveDepFlagsFileName}, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + android.FixtureMergeMockFs(android.MockFS{ + directDepFlagsFileName: nil, + transitiveDepFlagsFileName: nil, + }), + ).RunTestWithBp(t, + fmt.Sprintf( + bp, + tc.depType, + tc.transitiveDepType, + tc.depExportsFlagsFiles, + tc.transitiveDepExportsFlagsFiles, + ), + ) + appR8 := result.ModuleForTests("app", "android_common").Rule("r8") + + shouldHaveDepFlags := android.InList(directDepFlagsFileName, tc.expectedFlagsFiles) + if shouldHaveDepFlags { + android.AssertStringDoesContain(t, "expected deps's proguard flags", + appR8.Args["r8Flags"], directDepFlagsFileName) + } else { + android.AssertStringDoesNotContain(t, "app did not expect deps's proguard flags", + appR8.Args["r8Flags"], directDepFlagsFileName) + } + + shouldHaveTransitiveDepFlags := android.InList(transitiveDepFlagsFileName, tc.expectedFlagsFiles) + if shouldHaveTransitiveDepFlags { + android.AssertStringDoesContain(t, "expected transitive deps's proguard flags", + appR8.Args["r8Flags"], transitiveDepFlagsFileName) + } else { + android.AssertStringDoesNotContain(t, "app did not expect transitive deps's proguard flags", + appR8.Args["r8Flags"], transitiveDepFlagsFileName) + } + }) + } +} + +func TestProguardFlagsInheritanceAppImport(t *testing.T) { + bp := ` + android_app { + name: "app", + static_libs: ["aarimport"], // this must be static_libs to initate dexing + platform_apis: true, + } + + android_library { + name: "androidlib", + static_libs: ["aarimport"], + } + + android_library_import { + name: "aarimport", + aars: ["import.aar"], + } + ` + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + ).RunTestWithBp(t, bp) + + appR8 := result.ModuleForTests("app", "android_common").Rule("r8") + android.AssertStringDoesContain(t, "expected aarimports's proguard flags", + appR8.Args["r8Flags"], "proguard.txt") +} diff --git a/java/java.go b/java/java.go index 0d39a6a78..cbd4859ce 100644 --- a/java/java.go +++ b/java/java.go @@ -225,6 +225,23 @@ var ( }, "jar_name", "partition", "main_class") ) +type ProguardSpecInfo struct { + // If true, proguard flags files will be exported to reverse dependencies across libs edges + // If false, proguard flags files will only be exported to reverse dependencies across + // static_libs edges. + Export_proguard_flags_files bool + + // TransitiveDepsProguardSpecFiles is a depset of paths to proguard flags files that are exported from + // all transitive deps. This list includes all proguard flags files from transitive static dependencies, + // and all proguard flags files from transitive libs dependencies which set `export_proguard_spec: true`. + ProguardFlagsFiles *android.DepSet[android.Path] + + // implementation detail to store transitive proguard flags files from exporting shared deps + UnconditionallyExportedProguardFlags *android.DepSet[android.Path] +} + +var ProguardSpecInfoProvider = blueprint.NewProvider(ProguardSpecInfo{}) + // JavaInfo contains information about a java module for use by modules that depend on it. type JavaInfo struct { // HeaderJars is a list of jars that can be passed as the javac classpath in order to link @@ -310,11 +327,6 @@ type UsesLibraryDependency interface { ClassLoaderContexts() dexpreopt.ClassLoaderContextMap } -// Provides transitive Proguard flag files to downstream DEX jars. -type LibraryDependency interface { - ExportedProguardFlagFiles() android.Paths -} - // TODO(jungjw): Move this to kythe.go once it's created. type xref interface { XrefJavaFiles() android.Paths @@ -626,12 +638,6 @@ type Library struct { InstallMixin func(ctx android.ModuleContext, installPath android.Path) (extraInstallDeps android.Paths) } -var _ LibraryDependency = (*Library)(nil) - -func (j *Library) ExportedProguardFlagFiles() android.Paths { - return j.exportedProguardFlagFiles -} - var _ android.ApexModule = (*Library)(nil) // Provides access to the list of permitted packages from apex boot jars. @@ -730,15 +736,9 @@ func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.installFile = ctx.InstallFile(installDir, j.Stem()+".jar", j.outputFile, extraInstallDeps...) } - j.exportedProguardFlagFiles = append(j.exportedProguardFlagFiles, - android.PathsForModuleSrc(ctx, j.dexProperties.Optimize.Proguard_flags_files)...) - ctx.VisitDirectDeps(func(m android.Module) { - if lib, ok := m.(LibraryDependency); ok && ctx.OtherModuleDependencyTag(m) == staticLibTag { - j.exportedProguardFlagFiles = append(j.exportedProguardFlagFiles, lib.ExportedProguardFlagFiles()...) - } - }) - j.exportedProguardFlagFiles = android.FirstUniquePaths(j.exportedProguardFlagFiles) - + proguardSpecInfo := j.collectProguardSpecInfo(ctx) + ctx.SetProvider(ProguardSpecInfoProvider, proguardSpecInfo) + j.exportedProguardFlagFiles = proguardSpecInfo.ProguardFlagsFiles.ToList() } func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) {