diff --git a/java/androidmk.go b/java/androidmk.go index e1b4efd7a..a52d43965 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -211,7 +211,7 @@ func (prebuilt *Import) AndroidMkEntries() []android.AndroidMkEntries { return []android.AndroidMkEntries{android.AndroidMkEntries{ Class: "JAVA_LIBRARIES", OverrideName: prebuilt.BaseModuleName(), - OutputFile: android.OptionalPathForPath(prebuilt.combinedClasspathFile), + OutputFile: android.OptionalPathForPath(prebuilt.combinedImplementationFile), Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk", ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { @@ -219,8 +219,8 @@ func (prebuilt *Import) AndroidMkEntries() []android.AndroidMkEntries { if prebuilt.dexJarFile.IsSet() { entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile.Path()) } - entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.combinedClasspathFile) - entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.combinedClasspathFile) + entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.combinedHeaderFile) + entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.combinedImplementationFile) entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion.String()) entries.SetString("LOCAL_MODULE_STEM", prebuilt.Stem()) // TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts diff --git a/java/java.go b/java/java.go index 97feb9bb2..72536cd6d 100644 --- a/java/java.go +++ b/java/java.go @@ -21,6 +21,7 @@ package java import ( "fmt" "path/filepath" + "slices" "sort" "strings" @@ -2337,6 +2338,9 @@ type ImportProperties struct { // List of shared java libs that this module has dependencies to Libs []string + // List of static java libs that this module has dependencies to + Static_libs []string + // List of files to remove from the jar file(s) Exclude_files []string @@ -2389,9 +2393,10 @@ type Import struct { dexJarFileErr error dexJarInstallFile android.Path - combinedClasspathFile android.Path - classLoaderContexts dexpreopt.ClassLoaderContextMap - exportAidlIncludeDirs android.Paths + combinedImplementationFile android.Path + combinedHeaderFile android.Path + classLoaderContexts dexpreopt.ClassLoaderContextMap + exportAidlIncludeDirs android.Paths hideApexVariantFromMake bool @@ -2475,6 +2480,7 @@ func (j *Import) setStrictUpdatabilityLinting(bool) { func (j *Import) DepsMutator(ctx android.BottomUpMutatorContext) { ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...) + ctx.AddVariationDependencies(nil, staticLibTag, j.properties.Static_libs...) if ctx.Device() && Bool(j.dexProperties.Compile_dex) { sdkDeps(ctx, android.SdkContext(j), j.dexer) @@ -2504,23 +2510,13 @@ func (j *Import) commonBuildActions(ctx android.ModuleContext) { func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.commonBuildActions(ctx) - jars := android.PathsForModuleSrc(ctx, j.properties.Jars) - - jarName := j.Stem() + ".jar" - outputFile := android.PathForModuleOut(ctx, "combined", jarName) - TransformJarsToJar(ctx, outputFile, "for prebuilts", jars, android.OptionalPath{}, - false, j.properties.Exclude_files, j.properties.Exclude_dirs) - if Bool(j.properties.Jetifier) { - inputFile := outputFile - outputFile = android.PathForModuleOut(ctx, "jetifier", jarName) - TransformJetifier(ctx, outputFile, inputFile) - } - j.combinedClasspathFile = outputFile j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap) var flags javaBuilderFlags j.collectTransitiveHeaderJars(ctx) + var staticJars android.Paths + var staticHeaderJars android.Paths ctx.VisitDirectDeps(func(module android.Module) { tag := ctx.OtherModuleDependencyTag(module) if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok { @@ -2530,6 +2526,8 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { flags.dexClasspath = append(flags.dexClasspath, dep.HeaderJars...) case staticLibTag: flags.classpath = append(flags.classpath, dep.HeaderJars...) + staticJars = append(staticJars, dep.ImplementationAndResourcesJars...) + staticHeaderJars = append(staticHeaderJars, dep.HeaderJars...) case bootClasspathTag: flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars...) } @@ -2543,6 +2541,46 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { addCLCFromDep(ctx, module, j.classLoaderContexts) }) + jars := android.PathsForModuleSrc(ctx, j.properties.Jars) + jarName := j.Stem() + ".jar" + + // Always pass the input jars to TransformJarsToJar, even if there is only a single jar, we need the output + // file of the module to be named jarName. + outputFile := android.PathForModuleOut(ctx, "combined", jarName) + implementationJars := append(slices.Clone(jars), staticJars...) + TransformJarsToJar(ctx, outputFile, "combine prebuilt implementation jars", implementationJars, android.OptionalPath{}, + false, j.properties.Exclude_files, j.properties.Exclude_dirs) + + // If no dependencies have separate header jars then there is no need to create a separate + // header jar for this module. + reuseImplementationJarAsHeaderJar := slices.Equal(staticJars, staticHeaderJars) + + var headerOutputFile android.WritablePath + if reuseImplementationJarAsHeaderJar { + headerOutputFile = outputFile + } else { + headerJars := append(slices.Clone(jars), staticHeaderJars...) + headerOutputFile = android.PathForModuleOut(ctx, "turbine-combined", jarName) + TransformJarsToJar(ctx, headerOutputFile, "combine prebuilt header jars", headerJars, android.OptionalPath{}, + false, j.properties.Exclude_files, j.properties.Exclude_dirs) + } + + if Bool(j.properties.Jetifier) { + inputFile := outputFile + outputFile = android.PathForModuleOut(ctx, "jetifier", jarName) + TransformJetifier(ctx, outputFile, inputFile) + + if !reuseImplementationJarAsHeaderJar { + headerInputFile := headerOutputFile + headerOutputFile = android.PathForModuleOut(ctx, "jetifier-headers", jarName) + TransformJetifier(ctx, headerOutputFile, headerInputFile) + } else { + headerOutputFile = outputFile + } + } + j.combinedHeaderFile = headerOutputFile + j.combinedImplementationFile = outputFile + j.maybeInstall(ctx, jarName, outputFile) j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs) @@ -2626,11 +2664,11 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { } android.SetProvider(ctx, JavaInfoProvider, JavaInfo{ - HeaderJars: android.PathsIfNonNil(j.combinedClasspathFile), + HeaderJars: android.PathsIfNonNil(j.combinedHeaderFile), TransitiveLibsHeaderJars: j.transitiveLibsHeaderJars, TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars, - ImplementationAndResourcesJars: android.PathsIfNonNil(j.combinedClasspathFile), - ImplementationJars: android.PathsIfNonNil(j.combinedClasspathFile), + ImplementationAndResourcesJars: android.PathsIfNonNil(j.combinedImplementationFile), + ImplementationJars: android.PathsIfNonNil(j.combinedImplementationFile), AidlIncludeDirs: j.exportAidlIncludeDirs, StubsLinkType: j.stubsLinkType, // TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts @@ -2658,7 +2696,7 @@ func (j *Import) maybeInstall(ctx android.ModuleContext, jarName string, outputF func (j *Import) OutputFiles(tag string) (android.Paths, error) { switch tag { case "", ".jar": - return android.Paths{j.combinedClasspathFile}, nil + return android.Paths{j.combinedImplementationFile}, nil default: return nil, fmt.Errorf("unsupported module reference tag %q", tag) } @@ -2667,17 +2705,11 @@ func (j *Import) OutputFiles(tag string) (android.Paths, error) { var _ android.OutputFileProducer = (*Import)(nil) func (j *Import) HeaderJars() android.Paths { - if j.combinedClasspathFile == nil { - return nil - } - return android.Paths{j.combinedClasspathFile} + return android.PathsIfNonNil(j.combinedHeaderFile) } func (j *Import) ImplementationAndResourcesJars() android.Paths { - if j.combinedClasspathFile == nil { - return nil - } - return android.Paths{j.combinedClasspathFile} + return android.PathsIfNonNil(j.combinedImplementationFile) } func (j *Import) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath { diff --git a/java/java_test.go b/java/java_test.go index 194f9d974..2676aa558 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -588,10 +588,11 @@ func TestPrebuilts(t *testing.T) { javac := fooModule.Rule("javac") combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac") barModule := ctx.ModuleForTests("bar", "android_common") - barJar := barModule.Rule("combineJar").Output + barJar := barModule.Output("combined/bar.jar").Output bazModule := ctx.ModuleForTests("baz", "android_common") bazJar := bazModule.Rule("combineJar").Output - sdklibStubsJar := ctx.ModuleForTests("sdklib.stubs", "android_common").Rule("combineJar").Output + sdklibStubsJar := ctx.ModuleForTests("sdklib.stubs", "android_common"). + Output("combined/sdklib.stubs.jar").Output fooLibrary := fooModule.Module().(*Library) assertDeepEquals(t, "foo unique sources incorrect", @@ -1035,7 +1036,7 @@ func TestExcludeFileGroupInSrcs(t *testing.T) { } } -func TestJavaLibrary(t *testing.T) { +func TestJavaLibraryOutputFiles(t *testing.T) { testJavaWithFS(t, "", map[string][]byte{ "libcore/Android.bp": []byte(` java_library { @@ -1052,7 +1053,7 @@ func TestJavaLibrary(t *testing.T) { }) } -func TestJavaImport(t *testing.T) { +func TestJavaImportOutputFiles(t *testing.T) { testJavaWithFS(t, "", map[string][]byte{ "libcore/Android.bp": []byte(` java_import { @@ -1068,6 +1069,85 @@ func TestJavaImport(t *testing.T) { }) } +func TestJavaImport(t *testing.T) { + bp := ` + java_library { + name: "source_library", + srcs: ["source.java"], + } + + java_import { + name: "import_with_no_deps", + jars: ["no_deps.jar"], + } + + java_import { + name: "import_with_source_deps", + jars: ["source_deps.jar"], + static_libs: ["source_library"], + } + + java_import { + name: "import_with_import_deps", + jars: ["import_deps.jar"], + static_libs: ["import_with_no_deps"], + } + ` + ctx := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + ).RunTestWithBp(t, bp) + + source := ctx.ModuleForTests("source_library", "android_common") + sourceJar := source.Output("javac/source_library.jar") + sourceHeaderJar := source.Output("turbine-combined/source_library.jar") + sourceJavaInfo, _ := android.SingletonModuleProvider(ctx, source.Module(), JavaInfoProvider) + + // The source library produces separate implementation and header jars + android.AssertPathsRelativeToTopEquals(t, "source library implementation jar", + []string{sourceJar.Output.String()}, sourceJavaInfo.ImplementationAndResourcesJars) + android.AssertPathsRelativeToTopEquals(t, "source library header jar", + []string{sourceHeaderJar.Output.String()}, sourceJavaInfo.HeaderJars) + + importWithNoDeps := ctx.ModuleForTests("import_with_no_deps", "android_common") + importWithNoDepsJar := importWithNoDeps.Output("combined/import_with_no_deps.jar") + importWithNoDepsJavaInfo, _ := android.SingletonModuleProvider(ctx, importWithNoDeps.Module(), JavaInfoProvider) + + // An import with no deps produces a single jar used as both the header and implementation jar. + android.AssertPathsRelativeToTopEquals(t, "import with no deps implementation jar", + []string{importWithNoDepsJar.Output.String()}, importWithNoDepsJavaInfo.ImplementationAndResourcesJars) + android.AssertPathsRelativeToTopEquals(t, "import with no deps header jar", + []string{importWithNoDepsJar.Output.String()}, importWithNoDepsJavaInfo.HeaderJars) + android.AssertPathsRelativeToTopEquals(t, "import with no deps combined inputs", + []string{"no_deps.jar"}, importWithNoDepsJar.Inputs) + + importWithSourceDeps := ctx.ModuleForTests("import_with_source_deps", "android_common") + importWithSourceDepsJar := importWithSourceDeps.Output("combined/import_with_source_deps.jar") + importWithSourceDepsHeaderJar := importWithSourceDeps.Output("turbine-combined/import_with_source_deps.jar") + importWithSourceDepsJavaInfo, _ := android.SingletonModuleProvider(ctx, importWithSourceDeps.Module(), JavaInfoProvider) + + // An import with source deps produces separate header and implementation jars. + android.AssertPathsRelativeToTopEquals(t, "import with source deps implementation jar", + []string{importWithSourceDepsJar.Output.String()}, importWithSourceDepsJavaInfo.ImplementationAndResourcesJars) + android.AssertPathsRelativeToTopEquals(t, "import with source deps header jar", + []string{importWithSourceDepsHeaderJar.Output.String()}, importWithSourceDepsJavaInfo.HeaderJars) + android.AssertPathsRelativeToTopEquals(t, "import with source deps combined implementation jar inputs", + []string{"source_deps.jar", sourceJar.Output.String()}, importWithSourceDepsJar.Inputs) + android.AssertPathsRelativeToTopEquals(t, "import with source deps combined header jar inputs", + []string{"source_deps.jar", sourceHeaderJar.Output.String()}, importWithSourceDepsHeaderJar.Inputs) + + importWithImportDeps := ctx.ModuleForTests("import_with_import_deps", "android_common") + importWithImportDepsJar := importWithImportDeps.Output("combined/import_with_import_deps.jar") + importWithImportDepsJavaInfo, _ := android.SingletonModuleProvider(ctx, importWithImportDeps.Module(), JavaInfoProvider) + + // An import with only import deps produces a single jar used as both the header and implementation jar. + android.AssertPathsRelativeToTopEquals(t, "import with import deps implementation jar", + []string{importWithImportDepsJar.Output.String()}, importWithImportDepsJavaInfo.ImplementationAndResourcesJars) + android.AssertPathsRelativeToTopEquals(t, "import with import deps header jar", + []string{importWithImportDepsJar.Output.String()}, importWithImportDepsJavaInfo.HeaderJars) + android.AssertPathsRelativeToTopEquals(t, "import with import deps combined implementation jar inputs", + []string{"import_deps.jar", importWithNoDepsJar.Output.String()}, importWithImportDepsJar.Inputs) +} + var compilerFlagsTestCases = []struct { in string out bool