diff --git a/android/sdk.go b/android/sdk.go index f2cdc880b..bab0ed8fd 100644 --- a/android/sdk.go +++ b/android/sdk.go @@ -177,6 +177,12 @@ type SnapshotBuilder interface { // to the zip CopyToSnapshot(src Path, dest string) + // Return the path to an empty file. + // + // This can be used by sdk member types that need to create an empty file in the snapshot, simply + // pass the value returned from this to the CopyToSnapshot() method. + EmptyFile() Path + // Unzip the supplied zip into the snapshot relative directory destDir. UnzipToSnapshot(zipPath Path, destDir string) diff --git a/android/testing.go b/android/testing.go index a66b1e19f..92ce014a9 100644 --- a/android/testing.go +++ b/android/testing.go @@ -470,6 +470,10 @@ func AndroidMkDataForTest(t *testing.T, config Config, bpPath string, mod bluepr // The build and source paths should be distinguishable based on their contents. func NormalizePathForTesting(path Path) string { p := path.String() + // Allow absolute paths to /dev/ + if strings.HasPrefix(p, "/dev/") { + return p + } if w, ok := path.(WritablePath); ok { rel, err := filepath.Rel(w.buildDir(), p) if err != nil { diff --git a/java/java.go b/java/java.go index f684a00e7..99bc55dd6 100644 --- a/java/java.go +++ b/java/java.go @@ -40,20 +40,55 @@ func init() { // Register sdk member types. android.RegisterSdkMemberType(javaHeaderLibsSdkMemberType) + // Register java implementation libraries for use only in module_exports (not sdk). android.RegisterSdkMemberType(&librarySdkMemberType{ android.SdkMemberTypeBase{ PropertyName: "java_libs", }, - func(j *Library) android.Path { + func(_ android.SdkMemberContext, j *Library) android.Path { implementationJars := j.ImplementationAndResourcesJars() if len(implementationJars) != 1 { panic(fmt.Errorf("there must be only one implementation jar from %q", j.Name())) } - return implementationJars[0] }, + sdkSnapshotFilePathForJar, + copyEverythingToSnapshot, }) + // Register java boot libraries for use in sdk. + // + // The build has some implicit dependencies (via the boot jars configuration) on a number of + // modules, e.g. core-oj, apache-xml, that are part of the java boot class path and which are + // provided by mainline modules (e.g. art, conscrypt, runtime-i18n) but which are not otherwise + // used outside those mainline modules. + // + // As they are not needed outside the mainline modules adding them to the sdk/module-exports as + // either java_libs, or java_header_libs would end up exporting more information than was strictly + // necessary. The java_boot_libs property to allow those modules to be exported as part of the + // sdk/module_exports without exposing any unnecessary information. + android.RegisterSdkMemberType(&librarySdkMemberType{ + android.SdkMemberTypeBase{ + PropertyName: "java_boot_libs", + SupportsSdk: true, + }, + func(ctx android.SdkMemberContext, j *Library) android.Path { + // Java boot libs are only provided in the SDK to provide access to their dex implementation + // jar for use by dexpreopting and boot jars package check. They do not need to provide an + // actual implementation jar but the java_import will need a file that exists so just copy an + // empty file. Any attempt to use that file as a jar will cause a build error. + return ctx.SnapshotBuilder().EmptyFile() + }, + func(osPrefix, name string) string { + // Create a special name for the implementation jar to try and provide some useful information + // to a developer that attempts to compile against this. + // TODO(b/175714559): Provide a proper error message in Soong not ninja. + return filepath.Join(osPrefix, "java_boot_libs", "snapshot", "jars", "are", "invalid", name+jarFileSuffix) + }, + onlyCopyJarToSnapshot, + }) + + // Register java test libraries for use only in module_exports (not sdk). android.RegisterSdkMemberType(&testSdkMemberType{ SdkMemberTypeBase: android.SdkMemberTypeBase{ PropertyName: "java_tests", @@ -2165,9 +2200,22 @@ type librarySdkMemberType struct { // Function to retrieve the appropriate output jar (implementation or header) from // the library. - jarToExportGetter func(j *Library) android.Path + jarToExportGetter func(ctx android.SdkMemberContext, j *Library) android.Path + + // Function to compute the snapshot relative path to which the named library's + // jar should be copied. + snapshotPathGetter func(osPrefix, name string) string + + // True if only the jar should be copied to the snapshot, false if the jar plus any additional + // files like aidl files should also be copied. + onlyCopyJarToSnapshot bool } +const ( + onlyCopyJarToSnapshot = true + copyEverythingToSnapshot = false +) + func (mt *librarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) { mctx.AddVariationDependencies(nil, dependencyTag, names...) } @@ -2195,21 +2243,32 @@ type librarySdkMemberProperties struct { func (p *librarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) { j := variant.(*Library) - p.JarToExport = ctx.MemberType().(*librarySdkMemberType).jarToExportGetter(j) + p.JarToExport = ctx.MemberType().(*librarySdkMemberType).jarToExportGetter(ctx, j) + p.AidlIncludeDirs = j.AidlIncludeDirs() } func (p *librarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) { builder := ctx.SnapshotBuilder() + memberType := ctx.MemberType().(*librarySdkMemberType) + exportedJar := p.JarToExport if exportedJar != nil { - snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(p.OsPrefix(), ctx.Name()) + // Delegate the creation of the snapshot relative path to the member type. + snapshotRelativeJavaLibPath := memberType.snapshotPathGetter(p.OsPrefix(), ctx.Name()) + + // Copy the exported jar to the snapshot. builder.CopyToSnapshot(exportedJar, snapshotRelativeJavaLibPath) propertySet.AddProperty("jars", []string{snapshotRelativeJavaLibPath}) } + // Do not copy anything else to the snapshot. + if memberType.onlyCopyJarToSnapshot { + return + } + aidlIncludeDirs := p.AidlIncludeDirs if len(aidlIncludeDirs) != 0 { sdkModuleContext := ctx.SdkModuleContext() @@ -2230,7 +2289,7 @@ var javaHeaderLibsSdkMemberType android.SdkMemberType = &librarySdkMemberType{ PropertyName: "java_header_libs", SupportsSdk: true, }, - func(j *Library) android.Path { + func(_ android.SdkMemberContext, j *Library) android.Path { headerJars := j.HeaderJars() if len(headerJars) != 1 { panic(fmt.Errorf("there must be only one header jar from %q", j.Name())) @@ -2238,6 +2297,8 @@ var javaHeaderLibsSdkMemberType android.SdkMemberType = &librarySdkMemberType{ return headerJars[0] }, + sdkSnapshotFilePathForJar, + copyEverythingToSnapshot, } // java_library builds and links sources into a `.jar` file for the device, and possibly for the host as well. diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go index d989c5be9..488afd84e 100644 --- a/sdk/java_sdk_test.go +++ b/sdk/java_sdk_test.go @@ -472,6 +472,61 @@ aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl ) } +func TestSnapshotWithJavaBootLibrary(t *testing.T) { + result := testSdkWithJava(t, ` + module_exports { + name: "myexports", + java_boot_libs: ["myjavalib"], + } + + java_library { + name: "myjavalib", + srcs: ["Test.java"], + java_resources: ["resource.txt"], + // The aidl files should not be copied to the snapshot because a java_boot_libs member is not + // intended to be used for compiling Java, only for accessing the dex implementation jar. + aidl: { + export_include_dirs: ["aidl"], + }, + system_modules: "none", + sdk_version: "none", + compile_dex: true, + } + `) + + result.CheckSnapshot("myexports", "", + checkAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. + +java_import { + name: "myexports_myjavalib@current", + sdk_member_name: "myjavalib", + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + jars: ["java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar"], +} + +java_import { + name: "myjavalib", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + jars: ["java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar"], +} + +module_exports_snapshot { + name: "myexports@current", + visibility: ["//visibility:public"], + java_boot_libs: ["myexports_myjavalib@current"], +} + +`), + checkAllCopyRules(` +.intermediates/myexports/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar +`), + ) +} + func TestHostSnapshotWithJavaImplLibrary(t *testing.T) { result := testSdkWithJava(t, ` module_exports { diff --git a/sdk/update.go b/sdk/update.go index 377aaae76..b5bc9f434 100644 --- a/sdk/update.go +++ b/sdk/update.go @@ -653,6 +653,9 @@ type snapshotBuilder struct { filesToZip android.Paths zipsToMerge android.Paths + // The path to an empty file. + emptyFile android.WritablePath + prebuiltModules map[string]*bpModule prebuiltOrder []*bpModule @@ -703,6 +706,19 @@ func (s *snapshotBuilder) UnzipToSnapshot(zipPath android.Path, destDir string) s.zipsToMerge = append(s.zipsToMerge, tmpZipPath) } +func (s *snapshotBuilder) EmptyFile() android.Path { + if s.emptyFile == nil { + ctx := s.ctx + s.emptyFile = android.PathForModuleOut(ctx, "empty") + s.ctx.Build(pctx, android.BuildParams{ + Rule: android.Touch, + Output: s.emptyFile, + }) + } + + return s.emptyFile +} + func (s *snapshotBuilder) AddPrebuiltModule(member android.SdkMember, moduleType string) android.BpModule { name := member.Name() if s.prebuiltModules[name] != nil {