diff --git a/java/aar.go b/java/aar.go index bd67effdb..fdb62e859 100644 --- a/java/aar.go +++ b/java/aar.go @@ -30,7 +30,6 @@ import ( ) type AndroidLibraryDependency interface { - LibraryDependency ExportPackage() android.Path ResourcesNodeDepSet() *android.DepSet[*resourcesNode] RRODirsDepSet() *android.DepSet[rroDir] @@ -780,17 +779,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) { @@ -941,10 +932,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 } @@ -1048,10 +1035,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 1d1ea97f9..7cf86c02f 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 70aba8e2c..5f59fe401 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) {