diff options
Diffstat (limited to 'java')
49 files changed, 3139 insertions, 687 deletions
diff --git a/java/Android.bp b/java/Android.bp index 9ffa12384..8835b4456 100644 --- a/java/Android.bp +++ b/java/Android.bp @@ -40,6 +40,7 @@ bootstrap_go_package { "dex.go", "dexpreopt.go", "dexpreopt_bootjars.go", + "dexpreopt_check.go", "dexpreopt_config.go", "droiddoc.go", "droidstubs.go", @@ -92,6 +93,7 @@ bootstrap_go_package { "platform_bootclasspath_test.go", "platform_compat_config_test.go", "plugin_test.go", + "prebuilt_apis_test.go", "rro_test.go", "sdk_test.go", "sdk_library_test.go", diff --git a/java/aar.go b/java/aar.go index afbaea23b..13390db66 100644 --- a/java/aar.go +++ b/java/aar.go @@ -486,6 +486,18 @@ type AndroidLibrary struct { exportedStaticPackages android.Paths } +var _ android.OutputFileProducer = (*AndroidLibrary)(nil) + +// For OutputFileProducer interface +func (a *AndroidLibrary) OutputFiles(tag string) (android.Paths, error) { + switch tag { + case ".aar": + return []android.Path{a.aarFile}, nil + default: + return a.Library.OutputFiles(tag) + } +} + func (a *AndroidLibrary) ExportedProguardFlagFiles() android.Paths { return a.exportedProguardFlagFiles } diff --git a/java/android_manifest.go b/java/android_manifest.go index 38065f153..f29d8ad1a 100644 --- a/java/android_manifest.go +++ b/java/android_manifest.go @@ -16,6 +16,7 @@ package java import ( "fmt" + "strconv" "strings" "github.com/google/blueprint" @@ -42,6 +43,21 @@ var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger", }, "args", "libs") +// targetSdkVersion for manifest_fixer +// When TARGET_BUILD_APPS is not empty, this method returns 10000 for modules targeting an unreleased SDK +// This enables release builds (that run with TARGET_BUILD_APPS=[val...]) to target APIs that have not yet been finalized as part of an SDK +func targetSdkVersionForManifestFixer(ctx android.ModuleContext, sdkContext android.SdkContext) string { + targetSdkVersionSpec := sdkContext.TargetSdkVersion(ctx) + if ctx.Config().UnbundledBuildApps() && targetSdkVersionSpec.ApiLevel.IsPreview() { + return strconv.Itoa(android.FutureApiLevel.FinalOrFutureInt()) + } + targetSdkVersion, err := targetSdkVersionSpec.EffectiveVersionString(ctx) + if err != nil { + ctx.ModuleErrorf("invalid targetSdkVersion: %s", err) + } + return targetSdkVersion +} + // Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext android.SdkContext, classLoaderContexts dexpreopt.ClassLoaderContextMap, isLibrary, useEmbeddedNativeLibs, usesNonSdkApis, @@ -89,10 +105,8 @@ func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext args = append(args, "--logging-parent", loggingParent) } var deps android.Paths - targetSdkVersion, err := sdkContext.TargetSdkVersion(ctx).EffectiveVersionString(ctx) - if err != nil { - ctx.ModuleErrorf("invalid targetSdkVersion: %s", err) - } + targetSdkVersion := targetSdkVersionForManifestFixer(ctx, sdkContext) + if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" { targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String()) deps = append(deps, ApiFingerprintPath(ctx)) diff --git a/java/android_resources.go b/java/android_resources.go index 6864ebb90..8c5908f69 100644 --- a/java/android_resources.go +++ b/java/android_resources.go @@ -43,7 +43,7 @@ var androidResourceIgnoreFilenames = []string{ // androidResourceGlob returns the list of files in the given directory, using the standard // exclusion patterns for Android resources. -func androidResourceGlob(ctx android.ModuleContext, dir android.Path) android.Paths { +func androidResourceGlob(ctx android.EarlyModuleContext, dir android.Path) android.Paths { return ctx.GlobFiles(filepath.Join(dir.String(), "**/*"), androidResourceIgnoreFilenames) } diff --git a/java/androidmk.go b/java/androidmk.go index 68ccd82e9..19fe7e2fb 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -29,8 +29,8 @@ func (library *Library) AndroidMkEntriesHostDex() android.AndroidMkEntries { if hostDexNeeded { var output android.Path - if library.dexJarFile != nil { - output = library.dexJarFile + if library.dexJarFile.IsSet() { + output = library.dexJarFile.Path() } else { output = library.implementationAndResourcesJar } @@ -44,9 +44,10 @@ func (library *Library) AndroidMkEntriesHostDex() android.AndroidMkEntries { func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetBool("LOCAL_IS_HOST_MODULE", true) entries.SetPath("LOCAL_PREBUILT_MODULE_FILE", output) - if library.dexJarFile != nil { - entries.SetPath("LOCAL_SOONG_DEX_JAR", library.dexJarFile) + if library.dexJarFile.IsSet() { + entries.SetPath("LOCAL_SOONG_DEX_JAR", library.dexJarFile.Path()) } + entries.SetPath("LOCAL_SOONG_INSTALLED_MODULE", library.hostdexInstallFile) entries.SetPath("LOCAL_SOONG_HEADER_JAR", library.headerJarFile) entries.SetPath("LOCAL_SOONG_CLASSES_JAR", library.implementationAndResourcesJar) entries.SetString("LOCAL_MODULE_STEM", library.Stem()+"-hostdex") @@ -60,28 +61,23 @@ func (library *Library) AndroidMkEntriesHostDex() android.AndroidMkEntries { func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { var entriesList []android.AndroidMkEntries + if library.Os() == android.Windows { + // Make does not support Windows Java modules + return nil + } + if library.hideApexVariantFromMake { - // For a java library built for an APEX we don't need Make module + // For a java library built for an APEX, we don't need a Make module for itself. Otherwise, it + // will conflict with the platform variant because they have the same module name in the + // makefile. However, we need to add its dexpreopt outputs as sub-modules, if it is preopted. + dexpreoptEntries := library.dexpreopter.AndroidMkEntriesForApex() + if len(dexpreoptEntries) > 0 { + entriesList = append(entriesList, dexpreoptEntries...) + } entriesList = append(entriesList, android.AndroidMkEntries{Disabled: true}) } else if !library.ApexModuleBase.AvailableFor(android.AvailableToPlatform) { // Platform variant. If not available for the platform, we don't need Make module. - // May still need to add some additional dependencies. - checkedModulePaths := library.additionalCheckedModules - if len(checkedModulePaths) != 0 { - entriesList = append(entriesList, android.AndroidMkEntries{ - Class: "FAKE", - // Need at least one output file in order for this to take effect. - OutputFile: android.OptionalPathForPath(checkedModulePaths[0]), - Include: "$(BUILD_PHONY_PACKAGE)", - ExtraEntries: []android.AndroidMkExtraEntriesFunc{ - func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", checkedModulePaths.Strings()...) - }, - }, - }) - } else { - entriesList = append(entriesList, android.AndroidMkEntries{Disabled: true}) - } + entriesList = append(entriesList, android.AndroidMkEntries{Disabled: true}) } else { entriesList = append(entriesList, android.AndroidMkEntries{ Class: "JAVA_LIBRARIES", @@ -100,8 +96,8 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { if library.installFile == nil { entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", true) } - if library.dexJarFile != nil { - entries.SetPath("LOCAL_SOONG_DEX_JAR", library.dexJarFile) + if library.dexJarFile.IsSet() { + entries.SetPath("LOCAL_SOONG_DEX_JAR", library.dexJarFile.Path()) } if len(library.dexpreopter.builtInstalled) > 0 { entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", library.dexpreopter.builtInstalled) @@ -117,10 +113,6 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { requiredUsesLibs, optionalUsesLibs := library.classLoaderContexts.UsesLibs() entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", append(requiredUsesLibs, optionalUsesLibs...)...) - if len(library.additionalCheckedModules) != 0 { - entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", library.additionalCheckedModules.Strings()...) - } - entries.SetOptionalPath("LOCAL_SOONG_PROGUARD_DICT", library.dexer.proguardDictionary) entries.SetOptionalPath("LOCAL_SOONG_PROGUARD_USAGE_ZIP", library.dexer.proguardUsageZip) entries.SetString("LOCAL_MODULE_STEM", library.Stem()) @@ -141,20 +133,21 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { } // Called for modules that are a component of a test suite. -func testSuiteComponent(entries *android.AndroidMkEntries, test_suites []string) { +func testSuiteComponent(entries *android.AndroidMkEntries, test_suites []string, perTestcaseDirectory bool) { entries.SetString("LOCAL_MODULE_TAGS", "tests") if len(test_suites) > 0 { entries.AddCompatibilityTestSuites(test_suites...) } else { entries.AddCompatibilityTestSuites("null-suite") } + entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", perTestcaseDirectory) } func (j *Test) AndroidMkEntries() []android.AndroidMkEntries { entriesList := j.Library.AndroidMkEntries() entries := &entriesList[0] entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - testSuiteComponent(entries, j.testProperties.Test_suites) + testSuiteComponent(entries, j.testProperties.Test_suites, Bool(j.testProperties.Per_testcase_directory)) if j.testConfig != nil { entries.SetPath("LOCAL_FULL_TEST_CONFIG", j.testConfig) } @@ -182,14 +175,21 @@ func (j *TestHelperLibrary) AndroidMkEntries() []android.AndroidMkEntries { entriesList := j.Library.AndroidMkEntries() entries := &entriesList[0] entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - testSuiteComponent(entries, j.testHelperLibraryProperties.Test_suites) + testSuiteComponent(entries, j.testHelperLibraryProperties.Test_suites, Bool(j.testHelperLibraryProperties.Per_testcase_directory)) }) return entriesList } func (prebuilt *Import) AndroidMkEntries() []android.AndroidMkEntries { - if prebuilt.hideApexVariantFromMake || !prebuilt.ContainingSdk().Unversioned() { + if prebuilt.hideApexVariantFromMake { + // For a library imported from a prebuilt APEX, we don't need a Make module for itself, as we + // don't need to install it. However, we need to add its dexpreopt outputs as sub-modules, if it + // is preopted. + dexpreoptEntries := prebuilt.dexpreopter.AndroidMkEntriesForApex() + return append(dexpreoptEntries, android.AndroidMkEntries{Disabled: true}) + } + if !prebuilt.ContainingSdk().Unversioned() { return []android.AndroidMkEntries{android.AndroidMkEntries{ Disabled: true, }} @@ -201,8 +201,8 @@ func (prebuilt *Import) AndroidMkEntries() []android.AndroidMkEntries { ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !Bool(prebuilt.properties.Installable)) - if prebuilt.dexJarFile != nil { - entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile) + 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) @@ -221,12 +221,12 @@ func (prebuilt *DexImport) AndroidMkEntries() []android.AndroidMkEntries { } return []android.AndroidMkEntries{android.AndroidMkEntries{ Class: "JAVA_LIBRARIES", - OutputFile: android.OptionalPathForPath(prebuilt.dexJarFile), + OutputFile: android.OptionalPathForPath(prebuilt.dexJarFile.Path()), Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk", ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - if prebuilt.dexJarFile != nil { - entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile) + if prebuilt.dexJarFile.IsSet() { + entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile.Path()) } if len(prebuilt.dexpreopter.builtInstalled) > 0 { entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", prebuilt.dexpreopter.builtInstalled) @@ -263,6 +263,10 @@ func (prebuilt *AARImport) AndroidMkEntries() []android.AndroidMkEntries { } func (binary *Binary) AndroidMkEntries() []android.AndroidMkEntries { + if binary.Os() == android.Windows { + // Make does not support Windows Java modules + return nil + } if !binary.isWrapperVariant { return []android.AndroidMkEntries{android.AndroidMkEntries{ @@ -273,8 +277,8 @@ func (binary *Binary) AndroidMkEntries() []android.AndroidMkEntries { func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetPath("LOCAL_SOONG_HEADER_JAR", binary.headerJarFile) entries.SetPath("LOCAL_SOONG_CLASSES_JAR", binary.implementationAndResourcesJar) - if binary.dexJarFile != nil { - entries.SetPath("LOCAL_SOONG_DEX_JAR", binary.dexJarFile) + if binary.dexJarFile.IsSet() { + entries.SetPath("LOCAL_SOONG_DEX_JAR", binary.dexJarFile.Path()) } if len(binary.dexpreopter.builtInstalled) > 0 { entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", binary.dexpreopter.builtInstalled) @@ -289,11 +293,6 @@ func (binary *Binary) AndroidMkEntries() []android.AndroidMkEntries { }} } else { outputFile := binary.wrapperFile - // Have Make installation trigger Soong installation by using Soong's install path as - // the output file. - if binary.Host() { - outputFile = binary.binaryFile - } return []android.AndroidMkEntries{android.AndroidMkEntries{ Class: "EXECUTABLES", @@ -330,8 +329,8 @@ func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries { entries.SetString("LOCAL_MODULE", app.installApkName) entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", app.appProperties.PreventInstall) entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", app.exportPackage) - if app.dexJarFile != nil { - entries.SetPath("LOCAL_SOONG_DEX_JAR", app.dexJarFile) + if app.dexJarFile.IsSet() { + entries.SetPath("LOCAL_SOONG_DEX_JAR", app.dexJarFile.Path()) } if app.implementationAndResourcesJar != nil { entries.SetPath("LOCAL_SOONG_CLASSES_JAR", app.implementationAndResourcesJar) @@ -444,7 +443,7 @@ func (a *AndroidTest) AndroidMkEntries() []android.AndroidMkEntries { entriesList := a.AndroidApp.AndroidMkEntries() entries := &entriesList[0] entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - testSuiteComponent(entries, a.testProperties.Test_suites) + testSuiteComponent(entries, a.testProperties.Test_suites, Bool(a.testProperties.Per_testcase_directory)) if a.testConfig != nil { entries.SetPath("LOCAL_FULL_TEST_CONFIG", a.testConfig) } @@ -460,7 +459,7 @@ func (a *AndroidTestHelperApp) AndroidMkEntries() []android.AndroidMkEntries { entriesList := a.AndroidApp.AndroidMkEntries() entries := &entriesList[0] entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - testSuiteComponent(entries, a.appTestHelperAppProperties.Test_suites) + testSuiteComponent(entries, a.appTestHelperAppProperties.Test_suites, Bool(a.appTestHelperAppProperties.Per_testcase_directory)) // introduce a flag variable to control the generation of the .config file entries.SetString("LOCAL_DISABLE_TEST_CONFIG", "true") }) @@ -671,7 +670,7 @@ func (a *AndroidTestImport) AndroidMkEntries() []android.AndroidMkEntries { entriesList := a.AndroidAppImport.AndroidMkEntries() entries := &entriesList[0] entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - testSuiteComponent(entries, a.testProperties.Test_suites) + testSuiteComponent(entries, a.testProperties.Test_suites, Bool(a.testProperties.Per_testcase_directory)) androidMkWriteTestData(a.data, entries) }) return entriesList @@ -693,7 +692,7 @@ func (r *RuntimeResourceOverlay) AndroidMkEntries() []android.AndroidMkEntries { ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetString("LOCAL_CERTIFICATE", r.certificate.AndroidMkString()) - entries.SetPath("LOCAL_MODULE_PATH", r.installDir.ToMakePath()) + entries.SetPath("LOCAL_MODULE_PATH", r.installDir) entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", r.properties.Overrides...) }, }, @@ -704,12 +703,12 @@ func (apkSet *AndroidAppSet) AndroidMkEntries() []android.AndroidMkEntries { return []android.AndroidMkEntries{ android.AndroidMkEntries{ Class: "APPS", - OutputFile: android.OptionalPathForPath(apkSet.packedOutput), + OutputFile: android.OptionalPathForPath(apkSet.primaryOutput), Include: "$(BUILD_SYSTEM)/soong_android_app_set.mk", ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", apkSet.Privileged()) - entries.SetString("LOCAL_APK_SET_INSTALL_FILE", apkSet.InstallFile()) + entries.SetPath("LOCAL_APK_SET_INSTALL_FILE", apkSet.PackedAdditionalOutputs()) entries.SetPath("LOCAL_APKCERTS_FILE", apkSet.apkcertsFile) entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", apkSet.properties.Overrides...) }, diff --git a/java/app.go b/java/app.go index 5104f07d6..1c69aeb7a 100755 --- a/java/app.go +++ b/java/app.go @@ -43,8 +43,6 @@ func RegisterAppBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory) ctx.RegisterModuleType("override_android_app", OverrideAndroidAppModuleFactory) ctx.RegisterModuleType("override_android_test", OverrideAndroidTestModuleFactory) - - android.RegisterBp2BuildMutator("android_app_certificate", AndroidAppCertificateBp2Build) } // AndroidManifest.xml merging @@ -139,6 +137,7 @@ type overridableAppProperties struct { } type AndroidApp struct { + android.BazelModuleBase Library aapt android.OverridableModuleBase @@ -291,7 +290,7 @@ func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) { if minSdkVersion, err := a.MinSdkVersion(ctx).EffectiveVersion(ctx); err == nil { a.checkJniLibsSdkVersion(ctx, minSdkVersion) - android.CheckMinSdkVersion(a, ctx, minSdkVersion) + android.CheckMinSdkVersion(ctx, minSdkVersion, a.WalkPayloadDeps) } else { ctx.PropertyErrorf("min_sdk_version", "%s", err.Error()) } @@ -471,12 +470,13 @@ func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path { a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries() a.dexpreopter.classLoaderContexts = a.classLoaderContexts a.dexpreopter.manifestFile = a.mergedManifestFile + a.dexpreopter.preventInstall = a.appProperties.PreventInstall if ctx.ModuleName() != "framework-res" { a.Module.compile(ctx, a.aaptSrcJar) } - return a.dexJarFile + return a.dexJarFile.PathOrNil() } func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext) android.WritablePath { @@ -485,7 +485,7 @@ func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext a.jniLibs = jniLibs if a.shouldEmbedJnis(ctx) { jniJarFile = android.PathForModuleOut(ctx, "jnilibs.zip") - a.installPathForJNISymbols = a.installPath(ctx).ToMakePath() + a.installPathForJNISymbols = a.installPath(ctx) TransformJniLibsToJar(ctx, jniJarFile, jniLibs, a.useEmbeddedNativeLibs(ctx)) for _, jni := range jniLibs { if jni.coverageFile.Valid() { @@ -720,11 +720,15 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) // Install the app package. - if (Bool(a.Module.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() { - ctx.InstallFile(a.installDir, a.outputFile.Base(), a.outputFile) + if (Bool(a.Module.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() && + !a.appProperties.PreventInstall { + + var extraInstalledPaths android.Paths for _, extra := range a.extraOutputFiles { - ctx.InstallFile(a.installDir, extra.Base(), extra) + installed := ctx.InstallFile(a.installDir, extra.Base(), extra) + extraInstalledPaths = append(extraInstalledPaths, installed) } + ctx.InstallFile(a.installDir, a.outputFile.Base(), a.outputFile, extraInstalledPaths...) } a.buildAppDependencyInfo(ctx) @@ -760,18 +764,18 @@ func collectAppDeps(ctx android.ModuleContext, app appDepsInterface, } lib := dep.OutputFile() - path := lib.Path() - if seenModulePaths[path.String()] { - return false - } - seenModulePaths[path.String()] = true + if lib.Valid() { + path := lib.Path() + if seenModulePaths[path.String()] { + return false + } + seenModulePaths[path.String()] = true - if checkNativeSdkVersion && dep.SdkVersion() == "" { - ctx.PropertyErrorf("jni_libs", "JNI dependency %q uses platform APIs, but this module does not", - otherName) - } + if checkNativeSdkVersion && dep.SdkVersion() == "" { + ctx.PropertyErrorf("jni_libs", "JNI dependency %q uses platform APIs, but this module does not", + otherName) + } - if lib.Valid() { jniLibs = append(jniLibs, jniLib{ name: ctx.OtherModuleName(module), path: path, @@ -938,6 +942,7 @@ func AndroidAppFactory() android.Module { android.InitDefaultableModule(module) android.InitOverridableModule(module, &module.appProperties.Overrides) android.InitApexModule(module) + android.InitBazelModule(module) return module } @@ -1070,6 +1075,9 @@ type appTestHelperAppProperties struct { // doesn't exist next to the Android.bp, this attribute doesn't need to be set to true // explicitly. Auto_gen_config *bool + + // Install the test into a folder named for the module in all test suites. + Per_testcase_directory *bool } type AndroidTestHelperApp struct { @@ -1305,7 +1313,8 @@ func (u *usesLibrary) classLoaderContextForUsesLibDeps(ctx android.ModuleContext replaceInList(u.usesLibraryProperties.Optional_uses_libs, dep, libName) } clcMap.AddContext(ctx, tag.sdkVersion, libName, tag.optional, tag.implicit, - lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ClassLoaderContexts()) + lib.DexJarBuildPath().PathOrNil(), lib.DexJarInstallPath(), + lib.ClassLoaderContexts()) } else if ctx.Config().AllowMissingDependencies() { ctx.AddMissingDependencies([]string{dep}) } else { @@ -1396,23 +1405,11 @@ type bazelAndroidAppCertificateAttributes struct { Certificate string } -func AndroidAppCertificateBp2Build(ctx android.TopDownMutatorContext) { - module, ok := ctx.Module().(*AndroidAppCertificate) - if !ok { - // Not an Android app certificate - return - } - if !module.ConvertWithBp2build(ctx) { - return - } - if ctx.ModuleType() != "android_app_certificate" { - return - } - - androidAppCertificateBp2BuildInternal(ctx, module) +func (m *AndroidAppCertificate) ConvertWithBp2build(ctx android.TopDownMutatorContext) { + androidAppCertificateBp2Build(ctx, m) } -func androidAppCertificateBp2BuildInternal(ctx android.TopDownMutatorContext, module *AndroidAppCertificate) { +func androidAppCertificateBp2Build(ctx android.TopDownMutatorContext, module *AndroidAppCertificate) { var certificate string if module.properties.Certificate != nil { certificate = *module.properties.Certificate @@ -1427,5 +1424,41 @@ func androidAppCertificateBp2BuildInternal(ctx android.TopDownMutatorContext, mo Bzl_load_location: "//build/bazel/rules:android_app_certificate.bzl", } - ctx.CreateBazelTargetModule(module.Name(), props, attrs) + ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs) +} + +type bazelAndroidAppAttributes struct { + Srcs bazel.LabelListAttribute + Manifest bazel.Label + Custom_package *string + Resource_files bazel.LabelListAttribute +} + +// ConvertWithBp2build is used to convert android_app to Bazel. +func (a *AndroidApp) ConvertWithBp2build(ctx android.TopDownMutatorContext) { + //TODO(b/209577426): Support multiple arch variants + srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, a.properties.Srcs, a.properties.Exclude_srcs)) + + manifest := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml") + + resourceFiles := bazel.LabelList{ + Includes: []bazel.Label{}, + } + for _, dir := range android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res") { + files := android.RootToModuleRelativePaths(ctx, androidResourceGlob(ctx, dir)) + resourceFiles.Includes = append(resourceFiles.Includes, files...) + } + + attrs := &bazelAndroidAppAttributes{ + Srcs: srcs, + Manifest: android.BazelLabelForModuleSrcSingle(ctx, manifest), + // TODO(b/209576404): handle package name override by product variable PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES + Custom_package: a.overridableAppProperties.Package_name, + Resource_files: bazel.MakeLabelListAttribute(resourceFiles), + } + props := bazel.BazelTargetModuleProperties{Rule_class: "android_binary", + Bzl_load_location: "@rules_android//rules:rules.bzl"} + + ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: a.Name()}, attrs) + } diff --git a/java/app_set.go b/java/app_set.go index 6b25638da..694b1670e 100644 --- a/java/app_set.go +++ b/java/app_set.go @@ -55,10 +55,10 @@ type AndroidAppSet struct { android.DefaultableModuleBase prebuilt android.Prebuilt - properties AndroidAppSetProperties - packedOutput android.WritablePath - installFile string - apkcertsFile android.ModuleOutPath + properties AndroidAppSetProperties + packedOutput android.WritablePath + primaryOutput android.WritablePath + apkcertsFile android.ModuleOutPath } func (as *AndroidAppSet) Name() string { @@ -78,11 +78,11 @@ func (as *AndroidAppSet) Privileged() bool { } func (as *AndroidAppSet) OutputFile() android.Path { - return as.packedOutput + return as.primaryOutput } -func (as *AndroidAppSet) InstallFile() string { - return as.installFile +func (as *AndroidAppSet) PackedAdditionalOutputs() android.Path { + return as.packedOutput } func (as *AndroidAppSet) APKCertsFile() android.Path { @@ -114,11 +114,11 @@ func SupportedAbis(ctx android.ModuleContext) []string { func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) { as.packedOutput = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip") + as.primaryOutput = android.PathForModuleOut(ctx, as.BaseModuleName()+".apk") as.apkcertsFile = android.PathForModuleOut(ctx, "apkcerts.txt") // We are assuming here that the install file in the APK // set has `.apk` suffix. If it doesn't the build will fail. // APK sets containing APEX files are handled elsewhere. - as.installFile = as.BaseModuleName() + ".apk" screenDensities := "all" if dpis := ctx.Config().ProductAAPTPrebuiltDPI(); len(dpis) > 0 { screenDensities = strings.ToUpper(strings.Join(dpis, ",")) @@ -127,11 +127,11 @@ func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) // TODO(asmundak): do we support device features ctx.Build(pctx, android.BuildParams{ - Rule: extractMatchingApks, - Description: "Extract APKs from APK set", - Output: as.packedOutput, - ImplicitOutput: as.apkcertsFile, - Inputs: android.Paths{as.prebuilt.SingleSourcePath(ctx)}, + Rule: extractMatchingApks, + Description: "Extract APKs from APK set", + Output: as.primaryOutput, + ImplicitOutputs: android.WritablePaths{as.packedOutput, as.apkcertsFile}, + Inputs: android.Paths{as.prebuilt.SingleSourcePath(ctx)}, Args: map[string]string{ "abis": strings.Join(SupportedAbis(ctx), ","), "allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)), @@ -140,10 +140,21 @@ func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) "stem": as.BaseModuleName(), "apkcerts": as.apkcertsFile.String(), "partition": as.PartitionTag(ctx.DeviceConfig()), + "zip": as.packedOutput.String(), }, }) + + var installDir android.InstallPath + if as.Privileged() { + installDir = android.PathForModuleInstall(ctx, "priv-app", as.BaseModuleName()) + } else { + installDir = android.PathForModuleInstall(ctx, "app", as.BaseModuleName()) + } + ctx.InstallFileWithExtraFilesZip(installDir, as.BaseModuleName()+".apk", as.primaryOutput, as.packedOutput) } +func (as *AndroidAppSet) InstallBypassMake() bool { return true } + // android_app_set extracts a set of APKs based on the target device // configuration and installs this set as "split APKs". // The extracted set always contains an APK whose name is diff --git a/java/app_set_test.go b/java/app_set_test.go index adaf71bab..03eb66786 100644 --- a/java/app_set_test.go +++ b/java/app_set_test.go @@ -17,19 +17,20 @@ package java import ( "fmt" "reflect" + "strings" "testing" "android/soong/android" ) func TestAndroidAppSet(t *testing.T) { - ctx, _ := testJava(t, ` + result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, ` android_app_set { name: "foo", set: "prebuilts/apks/app.apks", prerelease: true, }`) - module := ctx.ModuleForTests("foo", "android_common") + module := result.ModuleForTests("foo", "android_common") const packedSplitApks = "foo.zip" params := module.Output(packedSplitApks) if params.Rule == nil { @@ -41,9 +42,22 @@ func TestAndroidAppSet(t *testing.T) { if s := params.Args["partition"]; s != "system" { t.Errorf("wrong partition value: '%s', expected 'system'", s) } - mkEntries := android.AndroidMkEntriesForTest(t, ctx, module.Module())[0] + + android.AssertPathRelativeToTopEquals(t, "incorrect output path", + "out/soong/.intermediates/foo/android_common/foo.apk", params.Output) + + android.AssertPathsRelativeToTopEquals(t, "incorrect implicit output paths", + []string{ + "out/soong/.intermediates/foo/android_common/foo.zip", + "out/soong/.intermediates/foo/android_common/apkcerts.txt", + }, + params.ImplicitOutputs.Paths()) + + mkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, module.Module())[0] actualInstallFile := mkEntries.EntryMap["LOCAL_APK_SET_INSTALL_FILE"] - expectedInstallFile := []string{"foo.apk"} + expectedInstallFile := []string{ + strings.Replace(params.ImplicitOutputs[0].String(), android.OutSoongDir, result.Config.SoongOutDir(), 1), + } if !reflect.DeepEqual(actualInstallFile, expectedInstallFile) { t.Errorf("Unexpected LOCAL_APK_SET_INSTALL_FILE value: '%s', expected: '%s',", actualInstallFile, expectedInstallFile) diff --git a/java/app_test.go b/java/app_test.go index 07439fcd0..4da7c3dba 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -144,14 +144,14 @@ func TestPlatformAPIs(t *testing.T) { } `) - testJavaError(t, "platform_apis must be true when sdk_version is empty.", ` + testJavaError(t, "This module has conflicting settings. sdk_version is empty, which means that this module is build against platform APIs. However platform_apis is not set to true", ` android_app { name: "bar", srcs: ["b.java"], } `) - testJavaError(t, "platform_apis must be false when sdk_version is not empty.", ` + testJavaError(t, "This module has conflicting settings. sdk_version is not empty, which means this module cannot use platform APIs. However platform_apis is set to true.", ` android_app { name: "bar", srcs: ["b.java"], @@ -2873,3 +2873,76 @@ func TestExportedProguardFlagFiles(t *testing.T) { t.Errorf("App does not use library proguard config") } } + +func TestTargetSdkVersionManifestFixer(t *testing.T) { + platform_sdk_codename := "Tiramisu" + testCases := []struct { + name string + targetSdkVersionInBp string + targetSdkVersionExpected string + unbundledBuild bool + }{ + { + name: "Non-Unbundled build: Android.bp has targetSdkVersion", + targetSdkVersionInBp: "30", + targetSdkVersionExpected: "30", + unbundledBuild: false, + }, + { + name: "Unbundled build: Android.bp has targetSdkVersion", + targetSdkVersionInBp: "30", + targetSdkVersionExpected: "30", + unbundledBuild: true, + }, + { + name: "Non-Unbundled build: Android.bp has targetSdkVersion equal to platform_sdk_codename", + targetSdkVersionInBp: platform_sdk_codename, + targetSdkVersionExpected: platform_sdk_codename, + unbundledBuild: false, + }, + { + name: "Unbundled build: Android.bp has targetSdkVersion equal to platform_sdk_codename", + targetSdkVersionInBp: platform_sdk_codename, + targetSdkVersionExpected: "10000", + unbundledBuild: true, + }, + + { + name: "Non-Unbundled build: Android.bp has no targetSdkVersion", + targetSdkVersionExpected: platform_sdk_codename, + unbundledBuild: false, + }, + { + name: "Unbundled build: Android.bp has no targetSdkVersion", + targetSdkVersionExpected: "10000", + unbundledBuild: true, + }, + } + for _, testCase := range testCases { + bp := fmt.Sprintf(` + android_app { + name: "foo", + sdk_version: "current", + target_sdk_version: "%v", + } + `, testCase.targetSdkVersionInBp) + fixture := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + // explicitly set platform_sdk_codename to make the test deterministic + variables.Platform_sdk_codename = &platform_sdk_codename + variables.Platform_version_active_codenames = []string{platform_sdk_codename} + // create a non-empty list if unbundledBuild==true + if testCase.unbundledBuild { + variables.Unbundled_build_apps = []string{"apex_a", "apex_b"} + } + }), + ) + + result := fixture.RunTestWithBp(t, bp) + foo := result.ModuleForTests("foo", "android_common") + + manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args + android.AssertStringEquals(t, testCase.name, testCase.targetSdkVersionExpected, manifestFixerArgs["targetSdkVersion"]) + } +} diff --git a/java/base.go b/java/base.go index 86022c3b2..7cd71a270 100644 --- a/java/base.go +++ b/java/base.go @@ -122,6 +122,14 @@ type CommonProperties struct { Javacflags []string } + Openjdk11 struct { + // List of source files that should only be used when passing -source 1.9 or higher + Srcs []string `android:"path"` + + // List of javac flags that should only be used when passing -source 1.9 or higher + Javacflags []string + } + // When compiling language level 9+ .java code in packages that are part of // a system module, patch_module names the module that your sources and // dependencies should be patched into. The Android runtime currently @@ -184,16 +192,25 @@ type CommonProperties struct { // Properties that are specific to device modules. Host module factories should not add these when // constructing a new module. type DeviceProperties struct { - // if not blank, set to the version of the sdk to compile against. + // If not blank, set to the version of the sdk to compile against. // Defaults to compiling against the current platform. + // Values are of one of the following forms: + // 1) numerical API level or "current" + // 2) An SDK kind with an API level: "<sdk kind>_<API level>". See + // build/soong/android/sdk_version.go for the complete and up to date list of + // SDK kinds. If the SDK kind value is empty, it will be set to public. Sdk_version *string // if not blank, set the minimum version of the sdk that the compiled artifacts will run against. - // Defaults to sdk_version if not set. + // Defaults to sdk_version if not set. See sdk_version for possible values. Min_sdk_version *string + // if not blank, set the maximum version of the sdk that the compiled artifacts will run against. + // Defaults to empty string "". See sdk_version for possible values. + Max_sdk_version *string + // if not blank, set the targetSdkVersion in the AndroidManifest.xml. - // Defaults to sdk_version if not set. + // Defaults to sdk_version if not set. See sdk_version for possible values. Target_sdk_version *string // Whether to compile against the platform APIs instead of an SDK. @@ -276,12 +293,94 @@ func (e *embeddableInModuleAndImport) depIsInSameApex(ctx android.BaseModuleCont return false } +// OptionalDexJarPath can be either unset, hold a valid path to a dex jar file, +// or an invalid path describing the reason it is invalid. +// +// It is unset if a dex jar isn't applicable, i.e. no build rule has been +// requested to create one. +// +// If a dex jar has been requested to be built then it is set, and it may be +// either a valid android.Path, or invalid with a reason message. The latter +// happens if the source that should produce the dex file isn't able to. +// +// E.g. it is invalid with a reason message if there is a prebuilt APEX that +// could produce the dex jar through a deapexer module, but the APEX isn't +// installable so doing so wouldn't be safe. +type OptionalDexJarPath struct { + isSet bool + path android.OptionalPath +} + +// IsSet returns true if a path has been set, either invalid or valid. +func (o OptionalDexJarPath) IsSet() bool { + return o.isSet +} + +// Valid returns true if there is a path that is valid. +func (o OptionalDexJarPath) Valid() bool { + return o.isSet && o.path.Valid() +} + +// Path returns the valid path, or panics if it's either not set or is invalid. +func (o OptionalDexJarPath) Path() android.Path { + if !o.isSet { + panic("path isn't set") + } + return o.path.Path() +} + +// PathOrNil returns the path if it's set and valid, or else nil. +func (o OptionalDexJarPath) PathOrNil() android.Path { + if o.Valid() { + return o.Path() + } + return nil +} + +// InvalidReason returns the reason for an invalid path, which is never "". It +// returns "" for an unset or valid path. +func (o OptionalDexJarPath) InvalidReason() string { + if !o.isSet { + return "" + } + return o.path.InvalidReason() +} + +func (o OptionalDexJarPath) String() string { + if !o.isSet { + return "<unset>" + } + return o.path.String() +} + +// makeUnsetDexJarPath returns an unset OptionalDexJarPath. +func makeUnsetDexJarPath() OptionalDexJarPath { + return OptionalDexJarPath{isSet: false} +} + +// makeDexJarPathFromOptionalPath returns an OptionalDexJarPath that is set with +// the given OptionalPath, which may be valid or invalid. +func makeDexJarPathFromOptionalPath(path android.OptionalPath) OptionalDexJarPath { + return OptionalDexJarPath{isSet: true, path: path} +} + +// makeDexJarPathFromPath returns an OptionalDexJarPath that is set with the +// valid given path. It returns an unset OptionalDexJarPath if the given path is +// nil. +func makeDexJarPathFromPath(path android.Path) OptionalDexJarPath { + if path == nil { + return makeUnsetDexJarPath() + } + return makeDexJarPathFromOptionalPath(android.OptionalPathForPath(path)) +} + // Module contains the properties and members used by all java module types type Module struct { android.ModuleBase android.DefaultableModuleBase android.ApexModuleBase android.SdkBase + android.BazelModuleBase // Functionality common to Module and Import. embeddableInModuleAndImport @@ -310,7 +409,7 @@ type Module struct { implementationAndResourcesJar android.Path // output file containing classes.dex and resources - dexJarFile android.Path + dexJarFile OptionalDexJarPath // output file containing uninstrumented classes that will be instrumented by jacoco jacocoReportClassesFile android.Path @@ -326,6 +425,9 @@ type Module struct { // installed file for binary dependency installFile android.Path + // installed file for hostdex copy + hostdexInstallFile android.InstallPath + // list of .java files and srcjars that was passed to javac compiledJavaSrcs android.Paths compiledSrcJars android.Paths @@ -352,9 +454,6 @@ type Module struct { // expanded Jarjar_rules expandJarjarRules android.Path - // list of additional targets for checkbuild - additionalCheckedModules android.Paths - // Extra files generated by the module type to be added as java resources. extraResources android.Paths @@ -374,6 +473,7 @@ type Module struct { sdkVersion android.SdkSpec minSdkVersion android.SdkSpec + maxSdkVersion android.SdkSpec } func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error { @@ -382,7 +482,7 @@ func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error { return nil } if sdkVersion.Kind == android.SdkCorePlatform { - if useLegacyCorePlatformApiByName(j.BaseModuleName()) { + if useLegacyCorePlatformApi(ctx, j.BaseModuleName()) { return fmt.Errorf("non stable SDK %v - uses legacy core platform", sdkVersion) } else { // Treat stable core platform as stable. @@ -424,9 +524,9 @@ func (j *Module) checkPlatformAPI(ctx android.ModuleContext) { usePlatformAPI := proptools.Bool(j.deviceProperties.Platform_apis) sdkVersionSpecified := sc.SdkVersion(ctx).Specified() if usePlatformAPI && sdkVersionSpecified { - ctx.PropertyErrorf("platform_apis", "platform_apis must be false when sdk_version is not empty.") + ctx.PropertyErrorf("platform_apis", "This module has conflicting settings. sdk_version is not empty, which means this module cannot use platform APIs. However platform_apis is set to true.") } else if !usePlatformAPI && !sdkVersionSpecified { - ctx.PropertyErrorf("platform_apis", "platform_apis must be true when sdk_version is empty.") + ctx.PropertyErrorf("platform_apis", "This module has conflicting settings. sdk_version is empty, which means that this module is build against platform APIs. However platform_apis is not set to true") } } @@ -531,6 +631,13 @@ func (j *Module) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec { return j.SdkVersion(ctx) } +func (j *Module) MaxSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec { + maxSdkVersion := proptools.StringDefault(j.deviceProperties.Max_sdk_version, "") + // SdkSpecFrom returns SdkSpecPrivate for this, which may be confusing. + // TODO(b/208456999): ideally MaxSdkVersion should be an ApiLevel and not SdkSpec. + return android.SdkSpecFrom(ctx, maxSdkVersion) +} + func (j *Module) MinSdkVersionString() string { return j.minSdkVersion.Raw } @@ -643,6 +750,11 @@ func (j *Module) deps(ctx android.BottomUpMutatorContext) { } else if j.shouldInstrumentStatic(ctx) { ctx.AddVariationDependencies(nil, staticLibTag, "jacocoagent") } + + if j.useCompose() { + ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), kotlinPluginTag, + "androidx.compose.compiler_compiler-hosted") + } } func hasSrcExt(srcs []string, ext string) bool { @@ -701,6 +813,9 @@ func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.Opt flags = append(flags, "--transaction_names") } + aidlMinSdkVersion := j.MinSdkVersion(ctx).ApiLevel.String() + flags = append(flags, "--min_sdk_version="+aidlMinSdkVersion) + return strings.Join(flags, " "), deps } @@ -852,6 +967,10 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { if flags.javaVersion.usesJavaModules() { j.properties.Srcs = append(j.properties.Srcs, j.properties.Openjdk9.Srcs...) } + if ctx.Config().TargetsJava11() { + j.properties.Srcs = append(j.properties.Srcs, j.properties.Openjdk11.Srcs...) + } + srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs) if hasSrcExt(srcFiles.Strings(), ".proto") { flags = protoFlags(ctx, &j.properties, &j.protoProperties, flags) @@ -911,6 +1030,12 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { if ctx.Device() { kotlincFlags = append(kotlincFlags, "-no-jdk") } + + for _, plugin := range deps.kotlinPlugins { + kotlincFlags = append(kotlincFlags, "-Xplugin="+plugin.String()) + } + flags.kotlincDeps = append(flags.kotlincDeps, deps.kotlinPlugins...) + if len(kotlincFlags) > 0 { // optimization. ctx.Variable(pctx, "kotlincFlags", strings.Join(kotlincFlags, " ")) @@ -966,7 +1091,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { j.compiledSrcJars = srcJars enableSharding := false - var headerJarFileWithoutJarjar android.Path + var headerJarFileWithoutDepsOrJarjar android.Path if ctx.Device() && !ctx.Config().IsEnvFalse("TURBINE_ENABLED") && !deps.disableTurbine { if j.properties.Javac_shard_size != nil && *(j.properties.Javac_shard_size) > 0 { enableSharding = true @@ -976,7 +1101,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { // allow for the use of annotation processors that do function correctly // with sharding enabled. See: b/77284273. } - headerJarFileWithoutJarjar, j.headerJarFile = + headerJarFileWithoutDepsOrJarjar, j.headerJarFile = j.compileJavaHeader(ctx, uniqueSrcFiles, srcJars, deps, flags, jarName, kotlinJars) if ctx.Failed() { return @@ -1005,7 +1130,9 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { } if enableSharding { - flags.classpath = append(flags.classpath, headerJarFileWithoutJarjar) + if headerJarFileWithoutDepsOrJarjar != nil { + flags.classpath = append(classpath{headerJarFileWithoutDepsOrJarjar}, flags.classpath...) + } shardSize := int(*(j.properties.Javac_shard_size)) var shardSrcs []android.Paths if len(uniqueSrcFiles) > 0 { @@ -1173,10 +1300,25 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { // Check package restrictions if necessary. if len(j.properties.Permitted_packages) > 0 { - // Check packages and copy to package-checked file. + // Time stamp file created by the package check rule. pkgckFile := android.PathForModuleOut(ctx, "package-check.stamp") + + // Create a rule to copy the output jar to another path and add a validate dependency that + // will check that the jar only contains the permitted packages. The new location will become + // the output file of this module. + inputFile := outputFile + outputFile = android.PathForModuleOut(ctx, "package-check", jarName).OutputPath + ctx.Build(pctx, android.BuildParams{ + Rule: android.Cp, + Input: inputFile, + Output: outputFile, + // Make sure that any dependency on the output file will cause ninja to run the package check + // rule. + Validation: pkgckFile, + }) + + // Check packages and create a timestamp file when complete. CheckJarPackages(ctx, pkgckFile, outputFile, j.properties.Permitted_packages) - j.additionalCheckedModules = append(j.additionalCheckedModules, pkgckFile) if ctx.Failed() { return @@ -1254,12 +1396,13 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { } // Initialize the hiddenapi structure. - j.initHiddenAPI(ctx, dexOutputFile, j.implementationJarFile, j.dexProperties.Uncompress_dex) + + j.initHiddenAPI(ctx, makeDexJarPathFromPath(dexOutputFile), j.implementationJarFile, j.dexProperties.Uncompress_dex) // Encode hidden API flags in dex file, if needed. dexOutputFile = j.hiddenAPIEncodeDex(ctx, dexOutputFile) - j.dexJarFile = dexOutputFile + j.dexJarFile = makeDexJarPathFromPath(dexOutputFile) // Dexpreopting j.dexpreopt(ctx, dexOutputFile) @@ -1269,7 +1412,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { // There is no code to compile into a dex jar, make sure the resources are propagated // to the APK if this is an app. outputFile = implementationAndResourcesJar - j.dexJarFile = j.resourceJar + j.dexJarFile = makeDexJarPathFromPath(j.resourceJar) } if ctx.Failed() { @@ -1325,6 +1468,10 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { j.outputFile = outputFile.WithoutRel() } +func (j *Module) useCompose() bool { + return android.InList("androidx.compose.runtime_runtime", j.properties.Static_libs) +} + // 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 { @@ -1388,7 +1535,7 @@ func CheckKotlincFlags(ctx android.ModuleContext, flags []string) { func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars android.Paths, deps deps, flags javaBuilderFlags, jarName string, - extraJars android.Paths) (headerJar, jarjarHeaderJar android.Path) { + extraJars android.Paths) (headerJar, jarjarAndDepsHeaderJar android.Path) { var jars android.Paths if len(srcFiles) > 0 || len(srcJars) > 0 { @@ -1399,6 +1546,7 @@ func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars return nil, nil } jars = append(jars, turbineJar) + headerJar = turbineJar } jars = append(jars, extraJars...) @@ -1412,20 +1560,19 @@ func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars combinedJar := android.PathForModuleOut(ctx, "turbine-combined", jarName) TransformJarsToJar(ctx, combinedJar, "for turbine", jars, android.OptionalPath{}, false, nil, []string{"META-INF/TRANSITIVE"}) - headerJar = combinedJar - jarjarHeaderJar = combinedJar + jarjarAndDepsHeaderJar = combinedJar if j.expandJarjarRules != nil { // Transform classes.jar into classes-jarjar.jar jarjarFile := android.PathForModuleOut(ctx, "turbine-jarjar", jarName) - TransformJarJar(ctx, jarjarFile, headerJar, j.expandJarjarRules) - jarjarHeaderJar = jarjarFile + TransformJarJar(ctx, jarjarFile, jarjarAndDepsHeaderJar, j.expandJarjarRules) + jarjarAndDepsHeaderJar = jarjarFile if ctx.Failed() { return nil, nil } } - return headerJar, jarjarHeaderJar + return headerJar, jarjarAndDepsHeaderJar } func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags, @@ -1455,7 +1602,7 @@ func (j *Module) ImplementationJars() android.Paths { return android.Paths{j.implementationJarFile} } -func (j *Module) DexJarBuildPath() android.Path { +func (j *Module) DexJarBuildPath() OptionalDexJarPath { return j.dexJarFile } @@ -1509,8 +1656,7 @@ func (j *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Modu } // Implements android.ApexModule -func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, - sdkVersion android.ApiLevel) error { +func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error { sdkSpec := j.MinSdkVersion(ctx) if !sdkSpec.Specified() { return fmt.Errorf("min_sdk_version is not specified") @@ -1755,6 +1901,8 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { deps.kotlinStdlib = append(deps.kotlinStdlib, dep.HeaderJars...) case kotlinAnnotationsTag: deps.kotlinAnnotations = dep.HeaderJars + case kotlinPluginTag: + deps.kotlinPlugins = append(deps.kotlinPlugins, dep.ImplementationAndResourcesJars...) case syspropPublicStubDepTag: // This is a sysprop implementation library, forward the JavaInfoProvider from // the corresponding sysprop public stub library as SyspropPublicStubInfoProvider. @@ -1817,3 +1965,17 @@ type ModuleWithStem interface { } var _ ModuleWithStem = (*Module)(nil) + +func (j *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) { + switch ctx.ModuleType() { + case "java_library", "java_library_host": + if lib, ok := ctx.Module().(*Library); ok { + javaLibraryBp2Build(ctx, lib) + } + case "java_binary_host": + if binary, ok := ctx.Module().(*Binary); ok { + javaBinaryHostBp2Build(ctx, binary) + } + } + +} diff --git a/java/bootclasspath.go b/java/bootclasspath.go index 4108770cd..52ce77d46 100644 --- a/java/bootclasspath.go +++ b/java/bootclasspath.go @@ -95,15 +95,6 @@ func addDependencyOntoApexModulePair(ctx android.BottomUpMutatorContext, apex st if ctx.OtherModuleDependencyVariantExists(variations, prebuiltName) { ctx.AddVariationDependencies(variations, tag, prebuiltName) addedDep = true - } else if ctx.Config().AlwaysUsePrebuiltSdks() && len(variations) > 0 { - // TODO(b/179354495): Remove this code path once the Android build has been fully migrated to - // use bootclasspath_fragment properly. - // Some prebuilt java_sdk_library modules do not yet have an APEX variations so try and add a - // dependency on the non-APEX variant. - if ctx.OtherModuleDependencyVariantExists(nil, prebuiltName) { - ctx.AddVariationDependencies(nil, tag, prebuiltName) - addedDep = true - } } // If no appropriate variant existing for this, so no dependency could be added, then it is an diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index f7561b439..bfe895c17 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -89,7 +89,7 @@ var bootclasspathFragmentContentDepTag = bootclasspathFragmentContentDependencyT var _ android.ExcludeFromVisibilityEnforcementTag = bootclasspathFragmentContentDepTag var _ android.ReplaceSourceWithPrebuilt = bootclasspathFragmentContentDepTag -var _ android.SdkMemberTypeDependencyTag = bootclasspathFragmentContentDepTag +var _ android.SdkMemberDependencyTag = bootclasspathFragmentContentDepTag var _ android.CopyDirectlyInAnyApexTag = bootclasspathFragmentContentDepTag var _ android.RequiresFilesFromPrebuiltApexTag = bootclasspathFragmentContentDepTag @@ -139,6 +139,74 @@ type bootclasspathFragmentProperties struct { BootclasspathFragmentsDepsProperties } +type SourceOnlyBootclasspathProperties struct { + Hidden_api struct { + // Contains prefixes of a package hierarchy that is provided solely by this + // bootclasspath_fragment. + // + // This affects the signature patterns file that is used to select the subset of monolithic + // hidden API flags. See split_packages property for more details. + Package_prefixes []string + + // The list of split packages provided by this bootclasspath_fragment. + // + // A split package is one that contains classes which are provided by multiple + // bootclasspath_fragment modules. + // + // This defaults to "*" - which treats all packages as being split. A module that has no split + // packages must specify an empty list. + // + // This affects the signature patterns file that is generated by a bootclasspath_fragment and + // used to select the subset of monolithic hidden API flags against which the flags generated + // by the bootclasspath_fragment are compared. + // + // The signature patterns file selects the subset of monolithic hidden API flags using a number + // of patterns, i.e.: + // * The qualified name (including package) of an outermost class, e.g. java/lang/Character. + // This selects all the flags for all the members of this class and any nested classes. + // * A package wildcard, e.g. java/lang/*. This selects all the flags for all the members of all + // the classes in this package (but not in sub-packages). + // * A recursive package wildcard, e.g. java/**. This selects all the flags for all the members + // of all the classes in this package and sub-packages. + // + // The signature patterns file is constructed as follows: + // * All the signatures are retrieved from the all-flags.csv file. + // * The member and inner class names are removed. + // * If a class is in a split package then that is kept, otherwise the class part is removed + // and replaced with a wildcard, i.e. *. + // * If a package matches a package prefix then the package is removed. + // * All the package prefixes are added with a recursive wildcard appended to each, i.e. **. + // * The resulting patterns are sorted. + // + // So, by default (i.e. without specifying any package_prefixes or split_packages) the signature + // patterns is a list of class names, because there are no package packages and all packages are + // assumed to be split. + // + // If any split packages are specified then only those packages are treated as split and all + // other packages are treated as belonging solely to the bootclasspath_fragment and so they use + // wildcard package patterns. + // + // So, if an empty list of split packages is specified then the signature patterns file just + // includes a wildcard package pattern for every package provided by the bootclasspath_fragment. + // + // If split_packages are specified and a package that is split is not listed then it could lead + // to build failures as it will select monolithic flags that are generated by another + // bootclasspath_fragment to compare against the flags provided by this fragment. The latter + // will obviously not contain those flags and that can cause the comparison and build to fail. + // + // If any package prefixes are specified then any matching packages are removed from the + // signature patterns and replaced with a single recursive package pattern. + // + // It is not strictly necessary to specify either package_prefixes or split_packages as the + // defaults will produce a valid set of signature patterns. However, those patterns may include + // implementation details, e.g. names of implementation classes or packages, which will be + // exported to the sdk snapshot in the signature patterns file. That is something that should be + // avoided where possible. Specifying package_prefixes and split_packages allows those + // implementation details to be excluded from the snapshot. + Split_packages []string + } +} + type BootclasspathFragmentModule struct { android.ModuleBase android.ApexModuleBase @@ -147,6 +215,8 @@ type BootclasspathFragmentModule struct { properties bootclasspathFragmentProperties + sourceOnlyProperties SourceOnlyBootclasspathProperties + // Collect the module directory for IDE info in java/jdeps.go. modulePaths []string } @@ -180,7 +250,7 @@ type bootImageFilesByArch map[android.ArchType]android.Paths func bootclasspathFragmentFactory() android.Module { m := &BootclasspathFragmentModule{} - m.AddProperties(&m.properties) + m.AddProperties(&m.properties, &m.sourceOnlyProperties) android.InitApexModule(m) android.InitSdkAwareModule(m) initClasspathFragment(m, BOOTCLASSPATH) @@ -320,6 +390,13 @@ type BootclasspathFragmentApexContentInfo struct { // Map from the base module name (without prebuilt_ prefix) of a fragment's contents module to the // hidden API encoded dex jar path. contentModuleDexJarPaths bootDexJarByModule + + // Path to the image profile file on host (or empty, if profile is not generated). + profilePathOnHost android.Path + + // Install path of the boot image profile if it needs to be installed in the APEX, or empty if not + // needed. + profileInstallPathInApex string } func (i BootclasspathFragmentApexContentInfo) Modules() android.ConfiguredJarList { @@ -348,6 +425,14 @@ func (i BootclasspathFragmentApexContentInfo) DexBootJarPathForContentModule(mod } } +func (i BootclasspathFragmentApexContentInfo) ProfilePathOnHost() android.Path { + return i.profilePathOnHost +} + +func (i BootclasspathFragmentApexContentInfo) ProfileInstallPathInApex() string { + return i.profileInstallPathInApex +} + func (b *BootclasspathFragmentModule) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool { tag := ctx.OtherModuleDependencyTag(dep) if IsBootclasspathFragmentContentDepTag(tag) { @@ -509,6 +594,11 @@ func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleC if imageConfig != nil { info.modules = imageConfig.modules + global := dexpreopt.GetGlobalConfig(ctx) + if !global.DisableGenerateProfile { + info.profilePathOnHost = imageConfig.profilePathOnHost + info.profileInstallPathInApex = imageConfig.profileInstallPathInApex + } } info.bootImageFilesByArch = bootImageFilesByArch @@ -546,6 +636,8 @@ func (b *BootclasspathFragmentModule) configuredJars(ctx android.ModuleContext) // This is an exception to support end-to-end test for SdkExtensions, until such support exists. if android.InList("test_framework-sdkextensions", possibleUpdatableModules) { jars = jars.Append("com.android.sdkext", "test_framework-sdkextensions") + } else if android.InList("test_framework-apexd", possibleUpdatableModules) { + jars = jars.Append("com.android.apex.test_package", "test_framework-apexd") } else if global.ApexBootJars.Len() != 0 && !android.IsModuleInVersionedSdk(ctx.Module()) { unknown = android.RemoveListFromList(unknown, b.properties.Coverage.Contents) _, unknown = android.RemoveFromList("core-icu4j", unknown) @@ -590,7 +682,7 @@ func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android. // TODO(b/192868581): Remove once the source and prebuilts provide a signature patterns file of // their own. if output.SignaturePatternsPath == nil { - output.SignaturePatternsPath = buildRuleSignaturePatternsFile(ctx, output.AllFlagsPath) + output.SignaturePatternsPath = buildRuleSignaturePatternsFile(ctx, output.AllFlagsPath, []string{"*"}, nil) } // Initialize a HiddenAPIInfo structure. @@ -659,7 +751,20 @@ func (b *BootclasspathFragmentModule) createHiddenAPIFlagInput(ctx android.Modul func (b *BootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput { // Generate the rules to create the hidden API flags and update the supplied hiddenAPIInfo with the // paths to the created files. - return hiddenAPIRulesForBootclasspathFragment(ctx, contents, input) + output := hiddenAPIRulesForBootclasspathFragment(ctx, contents, input) + + // If the module specifies split_packages or package_prefixes then use those to generate the + // signature patterns. + splitPackages := b.sourceOnlyProperties.Hidden_api.Split_packages + packagePrefixes := b.sourceOnlyProperties.Hidden_api.Package_prefixes + if splitPackages != nil || packagePrefixes != nil { + if splitPackages == nil { + splitPackages = []string{"*"} + } + output.SignaturePatternsPath = buildRuleSignaturePatternsFile(ctx, output.AllFlagsPath, splitPackages, packagePrefixes) + } + + return output } // produceBootImageFiles builds the boot image files from the source if it is required. @@ -767,14 +872,20 @@ type bootclasspathFragmentSdkMemberProperties struct { // The path to the generated index.csv file. Index_path android.OptionalPath - // The path to the generated signature-patterns.csv file. - Signature_patterns_path android.OptionalPath - // The path to the generated stub-flags.csv file. - Stub_flags_path android.OptionalPath + Stub_flags_path android.OptionalPath `supported_build_releases:"S"` // The path to the generated all-flags.csv file. - All_flags_path android.OptionalPath + All_flags_path android.OptionalPath `supported_build_releases:"S"` + + // The path to the generated signature-patterns.csv file. + Signature_patterns_path android.OptionalPath `supported_build_releases:"T+"` + + // The path to the generated filtered-stub-flags.csv file. + Filtered_stub_flags_path android.OptionalPath `supported_build_releases:"T+"` + + // The path to the generated filtered-flags.csv file. + Filtered_flags_path android.OptionalPath `supported_build_releases:"T+"` } func (b *bootclasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) { @@ -793,10 +904,13 @@ func (b *bootclasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx andro b.Metadata_path = android.OptionalPathForPath(hiddenAPIInfo.MetadataPath) b.Index_path = android.OptionalPathForPath(hiddenAPIInfo.IndexPath) - b.Signature_patterns_path = android.OptionalPathForPath(hiddenAPIInfo.SignaturePatternsPath) b.Stub_flags_path = android.OptionalPathForPath(hiddenAPIInfo.StubFlagsPath) b.All_flags_path = android.OptionalPathForPath(hiddenAPIInfo.AllFlagsPath) + b.Signature_patterns_path = android.OptionalPathForPath(hiddenAPIInfo.SignaturePatternsPath) + b.Filtered_stub_flags_path = android.OptionalPathForPath(hiddenAPIInfo.FilteredStubFlagsPath) + b.Filtered_flags_path = android.OptionalPathForPath(hiddenAPIInfo.FilteredFlagsPath) + // Copy stub_libs properties. b.Stub_libs = module.properties.Api.Stub_libs b.Core_platform_stub_libs = module.properties.Core_platform_api.Stub_libs @@ -861,9 +975,13 @@ func (b *bootclasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android. copyOptionalPath(b.Annotation_flags_path, "annotation_flags") copyOptionalPath(b.Metadata_path, "metadata") copyOptionalPath(b.Index_path, "index") - copyOptionalPath(b.Signature_patterns_path, "signature_patterns") + copyOptionalPath(b.Stub_flags_path, "stub_flags") copyOptionalPath(b.All_flags_path, "all_flags") + + copyOptionalPath(b.Signature_patterns_path, "signature_patterns") + copyOptionalPath(b.Filtered_stub_flags_path, "filtered_stub_flags") + copyOptionalPath(b.Filtered_flags_path, "filtered_flags") } var _ android.SdkMemberType = (*bootclasspathFragmentMemberType)(nil) @@ -889,6 +1007,12 @@ type prebuiltBootclasspathFragmentProperties struct { // The path to the all-flags.csv file created by the bootclasspath_fragment. All_flags *string `android:"path"` + + // The path to the filtered-stub-flags.csv file created by the bootclasspath_fragment. + Filtered_stub_flags *string `android:"path"` + + // The path to the filtered-flags.csv file created by the bootclasspath_fragment. + Filtered_flags *string `android:"path"` } } @@ -915,9 +1039,9 @@ func (module *prebuiltBootclasspathFragmentModule) Name() string { // produceHiddenAPIOutput returns a path to the prebuilt all-flags.csv or nil if none is specified. func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput { - pathForOptionalSrc := func(src *string) android.Path { + pathForOptionalSrc := func(src *string, defaultPath android.Path) android.Path { if src == nil { - return nil + return defaultPath } return android.PathForModuleSrc(ctx, *src) } @@ -938,13 +1062,19 @@ func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx an AnnotationFlagsPath: pathForSrc("hidden_api.annotation_flags", module.prebuiltProperties.Hidden_api.Annotation_flags), MetadataPath: pathForSrc("hidden_api.metadata", module.prebuiltProperties.Hidden_api.Metadata), IndexPath: pathForSrc("hidden_api.index", module.prebuiltProperties.Hidden_api.Index), - SignaturePatternsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Signature_patterns), - StubFlagsPath: pathForSrc("hidden_api.stub_flags", module.prebuiltProperties.Hidden_api.Stub_flags), - AllFlagsPath: pathForSrc("hidden_api.all_flags", module.prebuiltProperties.Hidden_api.All_flags), + SignaturePatternsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Signature_patterns, nil), + // TODO: Temporarily handle stub_flags/all_flags properties until prebuilts have been updated. + StubFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Stub_flags, nil), + AllFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.All_flags, nil), }, + EncodedBootDexFilesByModule: encodedBootDexJarsByModule, } + // TODO: Temporarily fallback to stub_flags/all_flags properties until prebuilts have been updated. + output.FilteredStubFlagsPath = pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Filtered_stub_flags, output.StubFlagsPath) + output.FilteredFlagsPath = pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Filtered_flags, output.AllFlagsPath) + return &output } @@ -954,23 +1084,11 @@ func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx and return nil } - var deapexerModule android.Module - ctx.VisitDirectDeps(func(module android.Module) { - tag := ctx.OtherModuleDependencyTag(module) - // Save away the `deapexer` module on which this depends, if any. - if tag == android.DeapexerTag { - deapexerModule = module - } - }) - - if deapexerModule == nil { - // This should never happen as a variant for a prebuilt_apex is only created if the - // deapexer module has been configured to export the dex implementation jar for this module. - ctx.ModuleErrorf("internal error: module does not depend on a `deapexer` module") - return nil + di := android.FindDeapexerProviderForModule(ctx) + if di == nil { + return nil // An error has been reported by FindDeapexerProviderForModule. } - di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo) files := bootImageFilesByArch{} for _, variant := range imageConfig.apexVariants() { arch := variant.target.Arch.ArchType diff --git a/java/builder.go b/java/builder.go index ea011b8e1..e64a61f5c 100644 --- a/java/builder.go +++ b/java/builder.go @@ -120,14 +120,14 @@ var ( "extractMatchingApks", blueprint.RuleParams{ Command: `rm -rf "$out" && ` + - `${config.ExtractApksCmd} -o "${out}" -allow-prereleased=${allow-prereleased} ` + + `${config.ExtractApksCmd} -o "${out}" -zip "${zip}" -allow-prereleased=${allow-prereleased} ` + `-sdk-version=${sdk-version} -abis=${abis} ` + `--screen-densities=${screen-densities} --stem=${stem} ` + `-apkcerts=${apkcerts} -partition=${partition} ` + `${in}`, CommandDeps: []string{"${config.ExtractApksCmd}"}, }, - "abis", "allow-prereleased", "screen-densities", "sdk-version", "stem", "apkcerts", "partition") + "abis", "allow-prereleased", "screen-densities", "sdk-version", "stem", "apkcerts", "partition", "zip") turbine, turbineRE = pctx.RemoteStaticRules("turbine", blueprint.RuleParams{ @@ -263,6 +263,7 @@ type javaBuilderFlags struct { kotlincFlags string kotlincClasspath classpath + kotlincDeps android.Paths proto android.ProtoFlags } diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go index f63d81d6e..ca2752877 100644 --- a/java/classpath_fragment.go +++ b/java/classpath_fragment.go @@ -25,7 +25,7 @@ import ( "android/soong/android" ) -// Build rules and utilities to generate individual packages/modules/SdkExtensions/proto/classpaths.proto +// Build rules and utilities to generate individual packages/modules/common/proto/classpaths.proto // config files based on build configuration to embed into /system and /apex on a device. // // See `derive_classpath` service that reads the configs at runtime and defines *CLASSPATH variables @@ -34,14 +34,15 @@ import ( type classpathType int const ( - // Matches definition in packages/modules/SdkExtensions/proto/classpaths.proto + // Matches definition in packages/modules/common/proto/classpaths.proto BOOTCLASSPATH classpathType = iota DEX2OATBOOTCLASSPATH SYSTEMSERVERCLASSPATH + STANDALONE_SYSTEMSERVER_JARS ) func (c classpathType) String() string { - return [...]string{"BOOTCLASSPATH", "DEX2OATBOOTCLASSPATH", "SYSTEMSERVERCLASSPATH"}[c] + return [...]string{"BOOTCLASSPATH", "DEX2OATBOOTCLASSPATH", "SYSTEMSERVERCLASSPATH", "STANDALONE_SYSTEMSERVER_JARS"}[c] } type classpathFragmentProperties struct { @@ -84,11 +85,10 @@ func initClasspathFragment(c classpathFragment, classpathType classpathType) { // Matches definition of Jar in packages/modules/SdkExtensions/proto/classpaths.proto type classpathJar struct { - path string - classpath classpathType - // TODO(satayev): propagate min/max sdk versions for the jars - minSdkVersion int32 - maxSdkVersion int32 + path string + classpath classpathType + minSdkVersion string + maxSdkVersion string } // gatherPossibleApexModuleNamesAndStems returns a set of module and stem names from the @@ -120,10 +120,32 @@ func configuredJarListToClasspathJars(ctx android.ModuleContext, configuredJars jars := make([]classpathJar, 0, len(paths)*len(classpaths)) for i := 0; i < len(paths); i++ { for _, classpathType := range classpaths { - jars = append(jars, classpathJar{ + jar := classpathJar{ classpath: classpathType, path: paths[i], + } + ctx.VisitDirectDepsIf(func(m android.Module) bool { + return m.Name() == configuredJars.Jar(i) + }, func(m android.Module) { + if s, ok := m.(*SdkLibrary); ok { + // TODO(208456999): instead of mapping "current" to latest, min_sdk_version should never be set to "current" + if s.minSdkVersion.Specified() { + if s.minSdkVersion.ApiLevel.IsCurrent() { + jar.minSdkVersion = ctx.Config().LatestPreviewApiLevel().String() + } else { + jar.minSdkVersion = s.minSdkVersion.ApiLevel.String() + } + } + if s.maxSdkVersion.Specified() { + if s.maxSdkVersion.ApiLevel.IsCurrent() { + jar.maxSdkVersion = ctx.Config().LatestPreviewApiLevel().String() + } else { + jar.maxSdkVersion = s.maxSdkVersion.ApiLevel.String() + } + } + } }) + jars = append(jars, jar) } } return jars @@ -136,15 +158,15 @@ func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.M c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths") - generatedJson := android.PathForModuleOut(ctx, outputFilename+".json") - writeClasspathsJson(ctx, generatedJson, jars) + generatedTextproto := android.PathForModuleOut(ctx, outputFilename+".textproto") + writeClasspathsTextproto(ctx, generatedTextproto, jars) rule := android.NewRuleBuilder(pctx, ctx) rule.Command(). BuiltTool("conv_classpaths_proto"). Flag("encode"). - Flag("--format=json"). - FlagWithInput("--input=", generatedJson). + Flag("--format=textproto"). + FlagWithInput("--input=", generatedTextproto). FlagWithOutput("--output=", c.outputFilepath) rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String()) @@ -159,24 +181,18 @@ func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.M ctx.SetProvider(ClasspathFragmentProtoContentInfoProvider, classpathProtoInfo) } -func writeClasspathsJson(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) { +func writeClasspathsTextproto(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) { var content strings.Builder - fmt.Fprintf(&content, "{\n") - fmt.Fprintf(&content, "\"jars\": [\n") - for idx, jar := range jars { - fmt.Fprintf(&content, "{\n") - fmt.Fprintf(&content, "\"path\": \"%s\",\n", jar.path) - fmt.Fprintf(&content, "\"classpath\": \"%s\"\n", jar.classpath) - - if idx < len(jars)-1 { - fmt.Fprintf(&content, "},\n") - } else { - fmt.Fprintf(&content, "}\n") - } + for _, jar := range jars { + fmt.Fprintf(&content, "jars {\n") + fmt.Fprintf(&content, "path: \"%s\"\n", jar.path) + fmt.Fprintf(&content, "classpath: %s\n", jar.classpath) + fmt.Fprintf(&content, "min_sdk_version: \"%s\"\n", jar.minSdkVersion) + fmt.Fprintf(&content, "max_sdk_version: \"%s\"\n", jar.maxSdkVersion) + fmt.Fprintf(&content, "}\n") } - fmt.Fprintf(&content, "]\n") - fmt.Fprintf(&content, "}\n") + android.WriteFileRule(ctx, output, content.String()) } @@ -188,7 +204,7 @@ func (c *ClasspathFragmentBase) androidMkEntries() []android.AndroidMkEntries { OutputFile: android.OptionalPathForPath(c.outputFilepath), ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - entries.SetString("LOCAL_MODULE_PATH", c.installDirPath.ToMakePath().String()) + entries.SetString("LOCAL_MODULE_PATH", c.installDirPath.String()) entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.outputFilepath.Base()) }, }, diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp index b198c2456..cf3974601 100644 --- a/java/core-libraries/Android.bp +++ b/java/core-libraries/Android.bp @@ -28,6 +28,11 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], } +dist_targets = [ + "sdk", + "win_sdk", +] + java_library { name: "core.current.stubs", visibility: ["//visibility:public"], @@ -40,15 +45,16 @@ java_library { system_modules: "none", dist: { - targets: [ - "sdk", - "win_sdk", - ], + targets: dist_targets, }, } // Distributed with the SDK for turning into system modules to compile apps // against. +// +// Also, produces dist files that are used by the +// prebuilts/sdk/update_prebuilts.py script to update the prebuilts/sdk +// directory. java_library { name: "core-current-stubs-for-system-modules", visibility: ["//development/sdk"], @@ -65,18 +71,22 @@ java_library { ], sdk_version: "none", system_modules: "none", - dist: { - dest: "core-for-system-modules.jar", - targets: [ - "sdk", - "win_sdk", - ], - }, + dists: [ + { + // Legacy dist location for the public file. + dest: "core-for-system-modules.jar", + targets: dist_targets, + }, + { + dest: "system-modules/public/core-for-system-modules.jar", + targets: dist_targets, + }, + ], } // Used when compiling higher-level code against core.current.stubs. java_system_modules { - name: "core-current-stubs-system-modules", + name: "core-public-stubs-system-modules", visibility: ["//visibility:public"], libs: [ "core-current-stubs-for-system-modules", @@ -103,10 +113,13 @@ java_library { visibility: ["//visibility:private"], } -// Used when compiling higher-level code with sdk_version "module_current" -java_system_modules { - name: "core-module-lib-stubs-system-modules", - libs: [ +// Produces a dist file that is used by the +// prebuilts/sdk/update_prebuilts.py script to update the prebuilts/sdk +// directory. +java_library { + name: "core-module-lib-stubs-for-system-modules", + visibility: ["//visibility:private"], + static_libs: [ "core.module_lib.stubs", // This one is not on device but it's needed when javac compiles code // containing lambdas. @@ -117,6 +130,20 @@ java_system_modules { // See http://b/123891440. "core-generated-annotation-stubs", ], + sdk_version: "none", + system_modules: "none", + dist: { + dest: "system-modules/module-lib/core-for-system-modules.jar", + targets: dist_targets, + }, +} + +// Used when compiling higher-level code with sdk_version "module_current" +java_system_modules { + name: "core-module-lib-stubs-system-modules", + libs: [ + "core-module-lib-stubs-for-system-modules", + ], visibility: ["//visibility:public"], } diff --git a/java/device_host_converter.go b/java/device_host_converter.go index 39fb04a8e..4abdcc6e9 100644 --- a/java/device_host_converter.go +++ b/java/device_host_converter.go @@ -118,7 +118,7 @@ func (d *DeviceHostConverter) GenerateAndroidBuildActions(ctx android.ModuleCont TransformJarsToJar(ctx, outputFile, "combine", d.implementationAndResourceJars, android.OptionalPath{}, false, nil, nil) d.combinedImplementationJar = outputFile - } else { + } else if len(d.implementationAndResourceJars) == 1 { d.combinedImplementationJar = d.implementationAndResourceJars[0] } @@ -127,7 +127,7 @@ func (d *DeviceHostConverter) GenerateAndroidBuildActions(ctx android.ModuleCont TransformJarsToJar(ctx, outputFile, "turbine combine", d.headerJars, android.OptionalPath{}, false, nil, []string{"META-INF/TRANSITIVE"}) d.combinedHeaderJar = outputFile - } else { + } else if len(d.headerJars) == 1 { d.combinedHeaderJar = d.headerJars[0] } @@ -174,7 +174,9 @@ func (d *DeviceHostConverter) AndroidMk() android.AndroidMkData { return android.AndroidMkData{ Class: "JAVA_LIBRARIES", OutputFile: android.OptionalPathForPath(d.combinedImplementationJar), - Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk", + // Make does not support Windows Java modules + Disabled: d.Os() == android.Windows, + Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk", Extra: []android.AndroidMkExtraFunc{ func(w io.Writer, outputFile android.Path) { fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true") diff --git a/java/dex.go b/java/dex.go index 667800f8a..8045b5cc6 100644 --- a/java/dex.go +++ b/java/dex.go @@ -32,6 +32,9 @@ type DexProperties struct { // list of module-specific flags that will be used for dex compiles Dxflags []string `android:"arch_variant"` + // A list of files containing rules that specify the classes to keep in the main dex file. + Main_dex_rules []string `android:"path"` + Optimize struct { // If false, disable all optimization. Defaults to true for android_app and android_test // modules, false for java_library and java_test modules. @@ -164,13 +167,20 @@ var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8", }, []string{"outDir", "outDict", "outUsage", "outUsageZip", "outUsageDir", "r8Flags", "zipFlags", "tmpJar"}, []string{"implicits"}) -func (d *dexer) dexCommonFlags(ctx android.ModuleContext, minSdkVersion android.SdkSpec) []string { - flags := d.dexProperties.Dxflags +func (d *dexer) dexCommonFlags(ctx android.ModuleContext, + minSdkVersion android.SdkSpec) (flags []string, deps android.Paths) { + + flags = d.dexProperties.Dxflags // Translate all the DX flags to D8 ones until all the build files have been migrated // to D8 flags. See: b/69377755 flags = android.RemoveListFromList(flags, []string{"--core-library", "--dex", "--multi-dex"}) + for _, f := range android.PathsForModuleSrc(ctx, d.dexProperties.Main_dex_rules) { + flags = append(flags, "--main-dex-rules", f.String()) + deps = append(deps, f) + } + if ctx.Config().Getenv("NO_OPTIMIZE_DX") != "" { flags = append(flags, "--debug") } @@ -187,7 +197,7 @@ func (d *dexer) dexCommonFlags(ctx android.ModuleContext, minSdkVersion android. } flags = append(flags, "--min-api "+strconv.Itoa(effectiveVersion.FinalOrFutureInt())) - return flags + return flags, deps } func d8Flags(flags javaBuilderFlags) (d8Flags []string, d8Deps android.Paths) { @@ -286,7 +296,7 @@ func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, mi zipFlags += " -L 0" } - commonFlags := d.dexCommonFlags(ctx, minSdkVersion) + commonFlags, commonDeps := d.dexCommonFlags(ctx, minSdkVersion) useR8 := d.effectiveOptimizeEnabled() if useR8 { @@ -298,6 +308,7 @@ func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, mi proguardUsageZip := android.PathForModuleOut(ctx, "proguard_usage.zip") d.proguardUsageZip = android.OptionalPathForPath(proguardUsageZip) r8Flags, r8Deps := d.r8Flags(ctx, flags) + r8Deps = append(r8Deps, commonDeps...) rule := r8 args := map[string]string{ "r8Flags": strings.Join(append(commonFlags, r8Flags...), " "), @@ -324,6 +335,7 @@ func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, mi }) } else { d8Flags, d8Deps := d8Flags(flags) + d8Deps = append(d8Deps, commonDeps...) rule := d8 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8") { rule = d8RE diff --git a/java/dexpreopt.go b/java/dexpreopt.go index 0faae36ba..e9bc51878 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -15,13 +15,46 @@ package java import ( + "path/filepath" + "strings" + "android/soong/android" "android/soong/dexpreopt" ) -type dexpreopterInterface interface { +type DexpreopterInterface interface { IsInstallable() bool // Structs that embed dexpreopter must implement this. dexpreoptDisabled(ctx android.BaseModuleContext) bool + DexpreoptBuiltInstalledForApex() []dexpreopterInstall + AndroidMkEntriesForApex() []android.AndroidMkEntries +} + +type dexpreopterInstall struct { + // A unique name to distinguish an output from others for the same java library module. Usually in + // the form of `<arch>-<encoded-path>.odex/vdex/art`. + name string + + // The name of the input java module. + moduleName string + + // The path to the dexpreopt output on host. + outputPathOnHost android.Path + + // The directory on the device for the output to install to. + installDirOnDevice android.InstallPath + + // The basename (the last segment of the path) for the output to install as. + installFileOnDevice string +} + +// The full module name of the output in the makefile. +func (install *dexpreopterInstall) FullModuleName() string { + return install.moduleName + install.SubModuleName() +} + +// The sub-module name of the output in the makefile (the name excluding the java module name). +func (install *dexpreopterInstall) SubModuleName() string { + return "-dexpreopt-" + install.name } type dexpreopter struct { @@ -33,13 +66,16 @@ type dexpreopter struct { isApp bool isTest bool isPresignedPrebuilt bool + preventInstall bool manifestFile android.Path statusFile android.WritablePath enforceUsesLibs bool classLoaderContexts dexpreopt.ClassLoaderContextMap - builtInstalled string + // See the `dexpreopt` function for details. + builtInstalled string + builtInstalledForApex []dexpreopterInstall // The config is used for two purposes: // - Passing dexpreopt information about libraries from Soong to Make. This is needed when @@ -74,14 +110,24 @@ func init() { dexpreopt.DexpreoptRunningInSoong = true } -func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool { - global := dexpreopt.GetGlobalConfig(ctx) +func isApexVariant(ctx android.BaseModuleContext) bool { + apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + return !apexInfo.IsForPlatform() +} - if global.DisablePreopt { - return true - } +func forPrebuiltApex(ctx android.BaseModuleContext) bool { + apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + return apexInfo.ForPrebuiltApex +} + +func moduleName(ctx android.BaseModuleContext) string { + // Remove the "prebuilt_" prefix if the module is from a prebuilt because the prefix is not + // expected by dexpreopter. + return android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()) +} - if inList(ctx.ModuleName(), global.DisablePreoptModules) { +func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool { + if !ctx.Device() { return true } @@ -93,36 +139,80 @@ func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool { return true } - if !ctx.Module().(dexpreopterInterface).IsInstallable() { + // If the module is from a prebuilt APEX, it shouldn't be installable, but it can still be + // dexpreopted. + if !ctx.Module().(DexpreopterInterface).IsInstallable() && !forPrebuiltApex(ctx) { return true } - if ctx.Host() { + if !android.IsModulePreferred(ctx.Module()) { return true } - // Don't preopt APEX variant module - if apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo); !apexInfo.IsForPlatform() { + global := dexpreopt.GetGlobalConfig(ctx) + + if global.DisablePreopt { return true } + if inList(moduleName(ctx), global.DisablePreoptModules) { + return true + } + + isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) + if isApexVariant(ctx) { + // Don't preopt APEX variant module unless the module is an APEX system server jar and we are + // building the entire system image. + if !isApexSystemServerJar || ctx.Config().UnbundledBuild() { + return true + } + } else { + // Don't preopt the platform variant of an APEX system server jar to avoid conflicts. + if isApexSystemServerJar { + return true + } + } + // TODO: contains no java code return false } func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) { - if d, ok := ctx.Module().(dexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) { + if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) { return } dexpreopt.RegisterToolDeps(ctx) } -func odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool { - return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx)) +func (d *dexpreopter) odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool { + return dexpreopt.OdexOnSystemOtherByName(moduleName(ctx), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx)) +} + +// Returns the install path of the dex jar of a module. +// +// Do not rely on `ApexInfo.ApexVariationName` because it can be something like "apex1000", rather +// than the `name` in the path `/apex/<name>` as suggested in its comment. +// +// This function is on a best-effort basis. It cannot handle the case where an APEX jar is not a +// system server jar, which is fine because we currently only preopt system server jars for APEXes. +func (d *dexpreopter) getInstallPath( + ctx android.ModuleContext, defaultInstallPath android.InstallPath) android.InstallPath { + global := dexpreopt.GetGlobalConfig(ctx) + if global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) { + dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, moduleName(ctx)) + return android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexLocation, "/")) + } + if !d.dexpreoptDisabled(ctx) && isApexVariant(ctx) && + filepath.Base(defaultInstallPath.PartitionDir()) != "apex" { + ctx.ModuleErrorf("unable to get the install path of the dex jar for dexpreopt") + } + return defaultInstallPath } func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.WritablePath) { + global := dexpreopt.GetGlobalConfig(ctx) + // TODO(b/148690468): The check on d.installPath is to bail out in cases where // the dexpreopter struct hasn't been fully initialized before we're called, // e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively @@ -133,7 +223,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath) - providesUsesLib := ctx.ModuleName() + providesUsesLib := moduleName(ctx) if ulib, ok := ctx.Module().(ProvidesUsesLib); ok { name := ulib.ProvidesUsesLib() if name != nil { @@ -147,17 +237,14 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr return } - global := dexpreopt.GetGlobalConfig(ctx) - - isSystemServerJar := global.SystemServerJars.ContainsJar(ctx.ModuleName()) + isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(moduleName(ctx)) bootImage := defaultBootImageConfig(ctx) if global.UseArtImage { bootImage = artBootImageConfig(ctx) } - // System server jars are an exception: they are dexpreopted without updatable bootclasspath. - dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp && !isSystemServerJar) + dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp) targets := ctx.MultiTargets() if len(targets) == 0 { @@ -199,15 +286,15 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr profileIsTextListing = true } else if global.ProfileDir != "" { profileClassListing = android.ExistentPathForSource(ctx, - global.ProfileDir, ctx.ModuleName()+".prof") + global.ProfileDir, moduleName(ctx)+".prof") } } // Full dexpreopt config, used to create dexpreopt build rules. dexpreoptConfig := &dexpreopt.ModuleConfig{ - Name: ctx.ModuleName(), + Name: moduleName(ctx), DexLocation: dexLocation, - BuildPath: android.PathForModuleOut(ctx, "dexpreopt", ctx.ModuleName()+".jar").OutputPath, + BuildPath: android.PathForModuleOut(ctx, "dexpreopt", moduleName(ctx)+".jar").OutputPath, DexPath: dexJarFile, ManifestPath: android.OptionalPathForPath(d.manifestFile), UncompressedDex: d.uncompressedDex, @@ -256,5 +343,59 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr dexpreoptRule.Build("dexpreopt", "dexpreopt") - d.builtInstalled = dexpreoptRule.Installs().String() + isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) + + for _, install := range dexpreoptRule.Installs() { + // Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT. + installDir := strings.TrimPrefix(filepath.Dir(install.To), "/") + installBase := filepath.Base(install.To) + arch := filepath.Base(installDir) + installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir) + + if isApexSystemServerJar { + // APEX variants of java libraries are hidden from Make, so their dexpreopt + // outputs need special handling. Currently, for APEX variants of java + // libraries, only those in the system server classpath are handled here. + // Preopting of boot classpath jars in the ART APEX are handled in + // java/dexpreopt_bootjars.go, and other APEX jars are not preopted. + // The installs will be handled by Make as sub-modules of the java library. + d.builtInstalledForApex = append(d.builtInstalledForApex, dexpreopterInstall{ + name: arch + "-" + installBase, + moduleName: moduleName(ctx), + outputPathOnHost: install.From, + installDirOnDevice: installPath, + installFileOnDevice: installBase, + }) + } else if !d.preventInstall { + ctx.InstallFile(installPath, installBase, install.From) + } + } + + if !isApexSystemServerJar { + d.builtInstalled = dexpreoptRule.Installs().String() + } +} + +func (d *dexpreopter) DexpreoptBuiltInstalledForApex() []dexpreopterInstall { + return d.builtInstalledForApex +} + +func (d *dexpreopter) AndroidMkEntriesForApex() []android.AndroidMkEntries { + var entries []android.AndroidMkEntries + for _, install := range d.builtInstalledForApex { + install := install + entries = append(entries, android.AndroidMkEntries{ + Class: "ETC", + SubName: install.SubModuleName(), + OutputFile: android.OptionalPathForPath(install.outputPathOnHost), + ExtraEntries: []android.AndroidMkExtraEntriesFunc{ + func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { + entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String()) + entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice) + entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false") + }, + }, + }) + } + return entries } diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index 946092cdb..c599c4da0 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -16,7 +16,6 @@ package java import ( "path/filepath" - "sort" "strings" "android/soong/android" @@ -257,6 +256,10 @@ type bootImageConfig struct { // Subdirectory where the image files on device are installed. installDirOnDevice string + // Install path of the boot image profile if it needs to be installed in the APEX, or empty if not + // needed. + profileInstallPathInApex string + // A list of (location, jar) pairs for the Java modules in this image. modules android.ConfiguredJarList @@ -273,6 +276,9 @@ type bootImageConfig struct { // Rules which should be used in make to install the outputs. profileInstalls android.RuleBuilderInstalls + // Path to the image profile file on host (or empty, if profile is not generated). + profilePathOnHost android.Path + // Target-dependent fields. variants []*bootImageVariant } @@ -500,8 +506,18 @@ func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, srcBootDexJars dst := dstBootJarsByModule[name] if src == nil { + // A dex boot jar should be provided by the source java module. It needs to be installable or + // have compile_dex=true - cf. assignments to java.Module.dexJarFile. + // + // However, the source java module may be either replaced or overridden (using prefer:true) by + // a prebuilt java module with the same name. In that case the dex boot jar needs to be + // provided by the corresponding prebuilt APEX module. That APEX is the one that refers + // through a exported_(boot|systemserver)classpath_fragments property to a + // prebuilt_(boot|systemserver)classpath_fragment module, which in turn lists the prebuilt + // java module in the contents property. If that chain is broken then this dependency will + // fail. if !ctx.Config().AllowMissingDependencies() { - ctx.ModuleErrorf("module %s does not provide a dex boot jar", name) + ctx.ModuleErrorf("module %s does not provide a dex boot jar (see comment next to this message in Soong for details)", name) } else { ctx.AddMissingDependencies([]string{name}) } @@ -626,7 +642,6 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p Flag("--runtime-arg").FlagWithArg("-Xmx", global.Dex2oatImageXmx) if profile != nil { - cmd.FlagWithArg("--compiler-filter=", "speed-profile") cmd.FlagWithInput("--profile-file=", profile) } @@ -771,11 +786,14 @@ func bootImageProfileRule(ctx android.ModuleContext, image *bootImageConfig) and FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps). FlagWithOutput("--reference-profile-file=", profile) - rule.Install(profile, "/system/etc/boot-image.prof") + if image == defaultBootImageConfig(ctx) { + rule.Install(profile, "/system/etc/boot-image.prof") + image.profileInstalls = append(image.profileInstalls, rule.Installs()...) + } rule.Build("bootJarsProfile", "profile boot jars") - image.profileInstalls = append(image.profileInstalls, rule.Installs()...) + image.profilePathOnHost = profile return profile } @@ -812,40 +830,6 @@ func bootFrameworkProfileRule(ctx android.ModuleContext, image *bootImageConfig) return profile } -// generateUpdatableBcpPackagesRule generates the rule to create the updatable-bcp-packages.txt file -// and returns a path to the generated file. -func generateUpdatableBcpPackagesRule(ctx android.ModuleContext, image *bootImageConfig, apexModules []android.Module) android.WritablePath { - // Collect `permitted_packages` for updatable boot jars. - var updatablePackages []string - for _, module := range apexModules { - if j, ok := module.(PermittedPackagesForUpdatableBootJars); ok { - pp := j.PermittedPackagesForUpdatableBootJars() - if len(pp) > 0 { - updatablePackages = append(updatablePackages, pp...) - } else { - ctx.OtherModuleErrorf(module, "Missing permitted_packages") - } - } - } - - // Sort updatable packages to ensure deterministic ordering. - sort.Strings(updatablePackages) - - updatableBcpPackagesName := "updatable-bcp-packages.txt" - updatableBcpPackages := image.dir.Join(ctx, updatableBcpPackagesName) - - // WriteFileRule automatically adds the last end-of-line. - android.WriteFileRule(ctx, updatableBcpPackages, strings.Join(updatablePackages, "\n")) - - rule := android.NewRuleBuilder(pctx, ctx) - rule.Install(updatableBcpPackages, "/system/etc/"+updatableBcpPackagesName) - // TODO: Rename `profileInstalls` to `extraInstalls`? - // Maybe even move the field out of the bootImageConfig into some higher level type? - image.profileInstalls = append(image.profileInstalls, rule.Installs()...) - - return updatableBcpPackages -} - func dumpOatRules(ctx android.ModuleContext, image *bootImageConfig) { var allPhonies android.Paths for _, image := range image.variants { diff --git a/java/dexpreopt_check.go b/java/dexpreopt_check.go new file mode 100644 index 000000000..149cbb758 --- /dev/null +++ b/java/dexpreopt_check.go @@ -0,0 +1,97 @@ +// Copyright 2021 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package java + +import ( + "strings" + + "android/soong/android" + "android/soong/dexpreopt" + + "github.com/google/blueprint/pathtools" +) + +func init() { + RegisterDexpreoptCheckBuildComponents(android.InitRegistrationContext) +} + +func RegisterDexpreoptCheckBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterSingletonModuleType("dexpreopt_systemserver_check", dexpreoptSystemserverCheckFactory) +} + +// A build-time check to verify if all compilation artifacts of system server jars are installed +// into the system image. When the check fails, it means that dexpreopting is not working for some +// system server jars and needs to be fixed. +// This singleton module generates a list of the paths to the artifacts based on +// PRODUCT_SYSTEM_SERVER_JARS and PRODUCT_APEX_SYSTEM_SERVER_JARS, and passes it to Make via a +// variable. Make will then do the actual check. +// Currently, it only checks artifacts of modules defined in Soong. Artifacts of modules defined in +// Makefile are generated by a script generated by dexpreopt_gen, and their existence is unknown to +// Make and Ninja. +type dexpreoptSystemserverCheck struct { + android.SingletonModuleBase + + // Mapping from the module name to the install paths to the compilation artifacts. + artifactsByModuleName map[string][]string + + // The install paths to the compilation artifacts. + artifacts []string +} + +func dexpreoptSystemserverCheckFactory() android.SingletonModule { + m := &dexpreoptSystemserverCheck{} + m.artifactsByModuleName = make(map[string][]string) + android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) + return m +} + +func getInstallPath(ctx android.ModuleContext, location string) android.InstallPath { + return android.PathForModuleInPartitionInstall( + ctx, "", strings.TrimPrefix(location, "/")) +} + +func (m *dexpreoptSystemserverCheck) GenerateAndroidBuildActions(ctx android.ModuleContext) { + global := dexpreopt.GetGlobalConfig(ctx) + targets := ctx.Config().Targets[android.Android] + + // The check should be skipped on unbundled builds because system server jars are not preopted on + // unbundled builds since the artifacts are installed into the system image, not the APEXes. + if global.DisablePreopt || len(targets) == 0 || ctx.Config().UnbundledBuild() { + return + } + + // TODO(b/203198541): Check all system server jars. + systemServerJars := global.AllSystemServerClasspathJars(ctx) + for _, jar := range systemServerJars.CopyOfJars() { + dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, jar) + odexLocation := dexpreopt.ToOdexPath(dexLocation, targets[0].Arch.ArchType) + odexPath := getInstallPath(ctx, odexLocation) + vdexPath := getInstallPath(ctx, pathtools.ReplaceExtension(odexLocation, "vdex")) + m.artifactsByModuleName[jar] = []string{odexPath.String(), vdexPath.String()} + } +} + +func (m *dexpreoptSystemserverCheck) GenerateSingletonBuildActions(ctx android.SingletonContext) { + // Only keep modules defined in Soong. + ctx.VisitAllModules(func(module android.Module) { + if artifacts, ok := m.artifactsByModuleName[module.Name()]; ok { + m.artifacts = append(m.artifacts, artifacts...) + } + }) +} + +func (m *dexpreoptSystemserverCheck) MakeVars(ctx android.MakeVarsContext) { + ctx.Strict("DEXPREOPT_SYSTEMSERVER_ARTIFACTS", strings.Join(m.artifacts, " ")) +} diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go index 415a1d4e7..26c110544 100644 --- a/java/dexpreopt_config.go +++ b/java/dexpreopt_config.go @@ -56,22 +56,20 @@ func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig { artModules := global.ArtApexJars frameworkModules := global.BootJars.RemoveList(artModules) - artDirOnHost := "apex/art_boot_images/javalib" - artDirOnDevice := "apex/com.android.art/javalib" - frameworkSubdir := "system/framework" - // ART config for the primary boot image in the ART apex. // It includes the Core Libraries. artCfg := bootImageConfig{ - name: artBootImageName, - stem: "boot", - installDirOnHost: artDirOnHost, - installDirOnDevice: artDirOnDevice, - modules: artModules, + name: artBootImageName, + stem: "boot", + installDirOnHost: "apex/art_boot_images/javalib", + installDirOnDevice: "apex/com.android.art/javalib", + profileInstallPathInApex: "etc/boot-image.prof", + modules: artModules, } // Framework config for the boot image extension. // It includes framework libraries and depends on the ART config. + frameworkSubdir := "system/framework" frameworkCfg := bootImageConfig{ extends: &artCfg, name: frameworkBootImageName, diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go index 8dc7b798a..1c1070add 100644 --- a/java/dexpreopt_test.go +++ b/java/dexpreopt_test.go @@ -17,6 +17,7 @@ package java import ( "fmt" "runtime" + "strings" "testing" "android/soong/android" @@ -24,11 +25,17 @@ import ( "android/soong/dexpreopt" ) +func init() { + RegisterFakeRuntimeApexMutator() +} + func TestDexpreoptEnabled(t *testing.T) { tests := []struct { - name string - bp string - enabled bool + name string + bp string + moduleName string + apexVariant bool + enabled bool }{ { name: "app", @@ -148,13 +155,81 @@ func TestDexpreoptEnabled(t *testing.T) { }`, enabled: true, }, + { + name: "apex variant", + bp: ` + java_library { + name: "foo", + installable: true, + srcs: ["a.java"], + apex_available: ["com.android.apex1"], + }`, + apexVariant: true, + enabled: false, + }, + { + name: "apex variant of apex system server jar", + bp: ` + java_library { + name: "service-foo", + installable: true, + srcs: ["a.java"], + apex_available: ["com.android.apex1"], + }`, + moduleName: "service-foo", + apexVariant: true, + enabled: true, + }, + { + name: "apex variant of prebuilt apex system server jar", + bp: ` + java_library { + name: "prebuilt_service-foo", + installable: true, + srcs: ["a.java"], + apex_available: ["com.android.apex1"], + }`, + moduleName: "prebuilt_service-foo", + apexVariant: true, + enabled: true, + }, + { + name: "platform variant of apex system server jar", + bp: ` + java_library { + name: "service-foo", + installable: true, + srcs: ["a.java"], + apex_available: ["com.android.apex1"], + }`, + moduleName: "service-foo", + apexVariant: false, + enabled: false, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - ctx, _ := testJava(t, test.bp) + preparers := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + PrepareForTestWithFakeApexMutator, + dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"), + ) + + result := preparers.RunTestWithBp(t, test.bp) + ctx := result.TestContext + + moduleName := "foo" + if test.moduleName != "" { + moduleName = test.moduleName + } + + variant := "android_common" + if test.apexVariant { + variant += "_apex1000" + } - dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeRule("dexpreopt") + dexpreopt := ctx.ModuleForTests(moduleName, variant).MaybeRule("dexpreopt") enabled := dexpreopt.Rule != nil if enabled != test.enabled { @@ -220,3 +295,145 @@ func TestDex2oatToolDeps(t *testing.T) { testDex2oatToolDep(true, true, true, prebuiltDex2oatPath) testDex2oatToolDep(false, true, false, prebuiltDex2oatPath) } + +func TestDexpreoptBuiltInstalledForApex(t *testing.T) { + preparers := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + PrepareForTestWithFakeApexMutator, + dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"), + ) + + // An APEX system server jar. + result := preparers.RunTestWithBp(t, ` + java_library { + name: "service-foo", + installable: true, + srcs: ["a.java"], + apex_available: ["com.android.apex1"], + }`) + ctx := result.TestContext + module := ctx.ModuleForTests("service-foo", "android_common_apex1000") + library := module.Module().(*Library) + + installs := library.dexpreopter.DexpreoptBuiltInstalledForApex() + + android.AssertIntEquals(t, "install count", 2, len(installs)) + + android.AssertStringEquals(t, "installs[0] FullModuleName", + "service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex", + installs[0].FullModuleName()) + + android.AssertStringEquals(t, "installs[0] SubModuleName", + "-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex", + installs[0].SubModuleName()) + + android.AssertStringEquals(t, "installs[1] FullModuleName", + "service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex", + installs[1].FullModuleName()) + + android.AssertStringEquals(t, "installs[1] SubModuleName", + "-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex", + installs[1].SubModuleName()) + + // Not an APEX system server jar. + result = preparers.RunTestWithBp(t, ` + java_library { + name: "foo", + installable: true, + srcs: ["a.java"], + }`) + ctx = result.TestContext + module = ctx.ModuleForTests("foo", "android_common") + library = module.Module().(*Library) + + installs = library.dexpreopter.DexpreoptBuiltInstalledForApex() + + android.AssertIntEquals(t, "install count", 0, len(installs)) +} + +func filterDexpreoptEntriesList(entriesList []android.AndroidMkEntries) []android.AndroidMkEntries { + var results []android.AndroidMkEntries + for _, entries := range entriesList { + if strings.Contains(entries.EntryMap["LOCAL_MODULE"][0], "-dexpreopt-") { + results = append(results, entries) + } + } + return results +} + +func verifyEntries(t *testing.T, message string, expectedModule string, + expectedPrebuiltModuleFile string, expectedModulePath string, expectedInstalledModuleStem string, + entries android.AndroidMkEntries) { + android.AssertStringEquals(t, message+" LOCAL_MODULE", expectedModule, + entries.EntryMap["LOCAL_MODULE"][0]) + + android.AssertStringEquals(t, message+" LOCAL_MODULE_CLASS", "ETC", + entries.EntryMap["LOCAL_MODULE_CLASS"][0]) + + android.AssertStringDoesContain(t, message+" LOCAL_PREBUILT_MODULE_FILE", + entries.EntryMap["LOCAL_PREBUILT_MODULE_FILE"][0], expectedPrebuiltModuleFile) + + android.AssertStringDoesContain(t, message+" LOCAL_MODULE_PATH", + entries.EntryMap["LOCAL_MODULE_PATH"][0], expectedModulePath) + + android.AssertStringEquals(t, message+" LOCAL_INSTALLED_MODULE_STEM", + expectedInstalledModuleStem, entries.EntryMap["LOCAL_INSTALLED_MODULE_STEM"][0]) + + android.AssertStringEquals(t, message+" LOCAL_NOT_AVAILABLE_FOR_PLATFORM", + "false", entries.EntryMap["LOCAL_NOT_AVAILABLE_FOR_PLATFORM"][0]) +} + +func TestAndroidMkEntriesForApex(t *testing.T) { + preparers := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + PrepareForTestWithFakeApexMutator, + dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"), + ) + + // An APEX system server jar. + result := preparers.RunTestWithBp(t, ` + java_library { + name: "service-foo", + installable: true, + srcs: ["a.java"], + apex_available: ["com.android.apex1"], + }`) + ctx := result.TestContext + module := ctx.ModuleForTests("service-foo", "android_common_apex1000") + + entriesList := android.AndroidMkEntriesForTest(t, ctx, module.Module()) + entriesList = filterDexpreoptEntriesList(entriesList) + + android.AssertIntEquals(t, "entries count", 2, len(entriesList)) + + verifyEntries(t, + "entriesList[0]", + "service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex", + "/dexpreopt/oat/arm64/javalib.odex", + "/system/framework/oat/arm64", + "apex@com.android.apex1@javalib@service-foo.jar@classes.odex", + entriesList[0]) + + verifyEntries(t, + "entriesList[1]", + "service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex", + "/dexpreopt/oat/arm64/javalib.vdex", + "/system/framework/oat/arm64", + "apex@com.android.apex1@javalib@service-foo.jar@classes.vdex", + entriesList[1]) + + // Not an APEX system server jar. + result = preparers.RunTestWithBp(t, ` + java_library { + name: "foo", + installable: true, + srcs: ["a.java"], + }`) + ctx = result.TestContext + module = ctx.ModuleForTests("foo", "android_common") + + entriesList = android.AndroidMkEntriesForTest(t, ctx, module.Module()) + entriesList = filterDexpreoptEntriesList(entriesList) + + android.AssertIntEquals(t, "entries count", 0, len(entriesList)) +} diff --git a/java/droiddoc.go b/java/droiddoc.go index 869a5982d..c84a15c1f 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -769,8 +769,8 @@ func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) { d.Javadoc.docZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"docs.zip") - jsilver := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "jsilver.jar") - doclava := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "doclava.jar") + jsilver := ctx.Config().HostJavaToolPath(ctx, "jsilver.jar") + doclava := ctx.Config().HostJavaToolPath(ctx, "doclava.jar") outDir := android.PathForModuleOut(ctx, "out") srcJarDir := android.PathForModuleOut(ctx, "srcjars") diff --git a/java/droidstubs.go b/java/droidstubs.go index ec1b04a06..7ad316fcf 100644 --- a/java/droidstubs.go +++ b/java/droidstubs.go @@ -16,6 +16,7 @@ package java import ( "fmt" + "path/filepath" "strings" "github.com/google/blueprint/proptools" @@ -25,6 +26,9 @@ import ( "android/soong/remoteexec" ) +// The values allowed for Droidstubs' Api_levels_sdk_type +var allowedApiLevelSdkTypes = []string{"public", "system", "module-lib"} + func init() { RegisterStubsBuildComponents(android.InitRegistrationContext) } @@ -134,7 +138,7 @@ type DroidstubsProperties struct { // the dirs which Metalava extracts API levels annotations from. Api_levels_annotations_dirs []string - // the sdk kind which Metalava extracts API levels annotations from. Supports 'public' and 'system' for now; defaults to public. + // the sdk kind which Metalava extracts API levels annotations from. Supports 'public', 'system' and 'module-lib' for now; defaults to public. Api_levels_sdk_type *string // the filename which Metalava extracts API levels annotations from. Defaults to android.jar. @@ -156,6 +160,7 @@ type ApiStubsSrcProvider interface { // Provider of information about API stubs, used by java_sdk_library. type ApiStubsProvider interface { + AnnotationsZip() android.Path ApiFilePath RemovedApiFilePath() android.Path @@ -210,6 +215,10 @@ func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) { } } +func (d *Droidstubs) AnnotationsZip() android.Path { + return d.annotationsZip +} + func (d *Droidstubs) ApiFilePath() android.Path { return d.apiFilePath } @@ -399,20 +408,25 @@ func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *a // When parsing a stub jar for a specific version, Metalava picks the first pattern that defines // an actual file present on disk (in the order the patterns were passed). For system APIs for // privileged apps that are only defined since API level 21 (Lollipop), fallback to public stubs - // for older releases. - if sdkType := proptools.StringDefault(d.properties.Api_levels_sdk_type, "public"); sdkType != "public" { - if sdkType != "system" { - ctx.PropertyErrorf("api_levels_sdk_type", "only 'public' and 'system' are supported") - } - // If building non public stubs, add all sdkType patterns first... + // for older releases. Similarly, module-lib falls back to system API. + var sdkDirs []string + switch proptools.StringDefault(d.properties.Api_levels_sdk_type, "public") { + case "module-lib": + sdkDirs = []string{"module-lib", "system", "public"} + case "system": + sdkDirs = []string{"system", "public"} + case "public": + sdkDirs = []string{"public"} + default: + ctx.PropertyErrorf("api_levels_sdk_type", "needs to be one of %v", allowedApiLevelSdkTypes) + return + } + + for _, sdkDir := range sdkDirs { for _, dir := range dirs { - cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkType, filename)) + cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, filename)) } } - for _, dir := range dirs { - // ... and fallback to public ones, for Metalava to use if needed. - cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, "public", filename)) - } } func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths, @@ -792,7 +806,7 @@ type PrebuiltStubsSources struct { properties PrebuiltStubsSourcesProperties - stubsSrcJar android.ModuleOutPath + stubsSrcJar android.Path } func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) { @@ -809,35 +823,39 @@ func (d *PrebuiltStubsSources) StubsSrcJar() android.Path { } func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) { - p.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar") - if len(p.properties.Srcs) != 1 { - ctx.PropertyErrorf("srcs", "must only specify one directory path, contains %d paths", len(p.properties.Srcs)) + ctx.PropertyErrorf("srcs", "must only specify one directory path or srcjar, contains %d paths", len(p.properties.Srcs)) return } - localSrcDir := p.properties.Srcs[0] - // Although PathForModuleSrc can return nil if either the path doesn't exist or - // the path components are invalid it won't in this case because no components - // are specified and the module directory must exist in order to get this far. - srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, localSrcDir) - - // Glob the contents of the directory just in case the directory does not exist. - srcGlob := localSrcDir + "/**/*" - srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob}) + src := p.properties.Srcs[0] + if filepath.Ext(src) == ".srcjar" { + // This is a srcjar. We can use it directly. + p.stubsSrcJar = android.PathForModuleSrc(ctx, src) + } else { + outPath := android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar") - rule := android.NewRuleBuilder(pctx, ctx) - rule.Command(). - BuiltTool("soong_zip"). - Flag("-write_if_changed"). - Flag("-jar"). - FlagWithOutput("-o ", p.stubsSrcJar). - FlagWithArg("-C ", srcDir.String()). - FlagWithRspFileInputList("-r ", p.stubsSrcJar.ReplaceExtension(ctx, "rsp"), srcPaths) + // This is a directory. Glob the contents just in case the directory does not exist. + srcGlob := src + "/**/*" + srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob}) - rule.Restat() + // Although PathForModuleSrc can return nil if either the path doesn't exist or + // the path components are invalid it won't in this case because no components + // are specified and the module directory must exist in order to get this far. + srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, src) - rule.Build("zip src", "Create srcjar from prebuilt source") + rule := android.NewRuleBuilder(pctx, ctx) + rule.Command(). + BuiltTool("soong_zip"). + Flag("-write_if_changed"). + Flag("-jar"). + FlagWithOutput("-o ", outPath). + FlagWithArg("-C ", srcDir.String()). + FlagWithRspFileInputList("-r ", outPath.ReplaceExtension(ctx, "rsp"), srcPaths) + rule.Restat() + rule.Build("zip src", "Create srcjar from prebuilt source") + p.stubsSrcJar = outPath + } } func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt { diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go index 60d0bea5d..10d99f3a5 100644 --- a/java/droidstubs_test.go +++ b/java/droidstubs_test.go @@ -15,6 +15,7 @@ package java import ( + "fmt" "reflect" "regexp" "strings" @@ -82,8 +83,10 @@ func TestDroidstubs(t *testing.T) { } } -func TestSystemDroidstubs(t *testing.T) { - ctx, _ := testJavaWithFS(t, ` +// runs a test for droidstubs with a customizable sdkType argument and returns +// the list of jar patterns that is passed as `--android-jar-pattern` +func getAndroidJarPatternsForDroidstubs(t *testing.T, sdkType string) []string { + ctx, _ := testJavaWithFS(t, fmt.Sprintf(` droiddoc_exported_dir { name: "some-exported-dir", path: "somedir", @@ -102,9 +105,9 @@ func TestSystemDroidstubs(t *testing.T) { "some-other-exported-dir", ], api_levels_annotations_enabled: true, - api_levels_sdk_type: "system", + api_levels_sdk_type: "%s", } - `, + `, sdkType), map[string][]byte{ "foo-doc/a.java": nil, }) @@ -113,13 +116,40 @@ func TestSystemDroidstubs(t *testing.T) { manifest := m.Output("metalava.sbox.textproto") cmd := String(android.RuleBuilderSboxProtoForTests(t, manifest).Commands[0].Command) r := regexp.MustCompile(`--android-jar-pattern [^ ]+/android.jar`) - matches := r.FindAllString(cmd, -1) + return r.FindAllString(cmd, -1) +} + +func TestPublicDroidstubs(t *testing.T) { + patterns := getAndroidJarPatternsForDroidstubs(t, "public") + + android.AssertArrayString(t, "order of patterns", []string{ + "--android-jar-pattern somedir/%/public/android.jar", + "--android-jar-pattern someotherdir/%/public/android.jar", + }, patterns) +} + +func TestSystemDroidstubs(t *testing.T) { + patterns := getAndroidJarPatternsForDroidstubs(t, "system") + + android.AssertArrayString(t, "order of patterns", []string{ + "--android-jar-pattern somedir/%/system/android.jar", + "--android-jar-pattern someotherdir/%/system/android.jar", + "--android-jar-pattern somedir/%/public/android.jar", + "--android-jar-pattern someotherdir/%/public/android.jar", + }, patterns) +} + +func TestModuleLibDroidstubs(t *testing.T) { + patterns := getAndroidJarPatternsForDroidstubs(t, "module-lib") + android.AssertArrayString(t, "order of patterns", []string{ + "--android-jar-pattern somedir/%/module-lib/android.jar", + "--android-jar-pattern someotherdir/%/module-lib/android.jar", "--android-jar-pattern somedir/%/system/android.jar", "--android-jar-pattern someotherdir/%/system/android.jar", "--android-jar-pattern somedir/%/public/android.jar", "--android-jar-pattern someotherdir/%/public/android.jar", - }, matches) + }, patterns) } func TestDroidstubsSandbox(t *testing.T) { diff --git a/java/genrule.go b/java/genrule.go index e0a9c8faf..16743b357 100644 --- a/java/genrule.go +++ b/java/genrule.go @@ -64,6 +64,7 @@ func genRuleFactory() android.Module { module := genrule.NewGenRule() android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon) + android.InitDefaultableModule(module) return module } @@ -76,6 +77,7 @@ func genRuleFactoryHost() android.Module { module := genrule.NewGenRule() android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon) + android.InitDefaultableModule(module) return module } diff --git a/java/hiddenapi.go b/java/hiddenapi.go index 30683daa0..7c8be1e6e 100644 --- a/java/hiddenapi.go +++ b/java/hiddenapi.go @@ -30,14 +30,14 @@ type hiddenAPI struct { // that information encoded within it. active bool - // The path to the dex jar that is in the boot class path. If this is nil then the associated + // The path to the dex jar that is in the boot class path. If this is unset then the associated // module is not a boot jar, but could be one of the <x>-hiddenapi modules that provide additional // annotations for the <x> boot dex jar but which do not actually provide a boot dex jar // themselves. // // This must be the path to the unencoded dex jar as the encoded dex jar indirectly depends on // this file so using the encoded dex jar here would result in a cycle in the ninja rules. - bootDexJarPath android.Path + bootDexJarPath OptionalDexJarPath // The paths to the classes jars that contain classes and class members annotated with // the UnsupportedAppUsage annotation that need to be extracted as part of the hidden API @@ -49,7 +49,7 @@ type hiddenAPI struct { uncompressDexState *bool } -func (h *hiddenAPI) bootDexJar() android.Path { +func (h *hiddenAPI) bootDexJar() OptionalDexJarPath { return h.bootDexJarPath } @@ -68,7 +68,7 @@ type hiddenAPIModule interface { } type hiddenAPIIntf interface { - bootDexJar() android.Path + bootDexJar() OptionalDexJarPath classesJars() android.Paths uncompressDex() *bool } @@ -79,7 +79,7 @@ var _ hiddenAPIIntf = (*hiddenAPI)(nil) // // uncompressedDexState should be nil when the module is a prebuilt and so does not require hidden // API encoding. -func (h *hiddenAPI) initHiddenAPI(ctx android.ModuleContext, dexJar, classesJar android.Path, uncompressedDexState *bool) { +func (h *hiddenAPI) initHiddenAPI(ctx android.ModuleContext, dexJar OptionalDexJarPath, classesJar android.Path, uncompressedDexState *bool) { // Save the classes jars even if this is not active as they may be used by modular hidden API // processing. diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go index 8e39f408e..0cc960d5c 100644 --- a/java/hiddenapi_modular.go +++ b/java/hiddenapi_modular.go @@ -19,6 +19,7 @@ import ( "strings" "android/soong/android" + "github.com/google/blueprint" ) @@ -218,7 +219,7 @@ func (b hiddenAPIStubsDependencyTag) ExcludeFromVisibilityEnforcement() { var _ android.ExcludeFromVisibilityEnforcementTag = hiddenAPIStubsDependencyTag{} var _ android.ReplaceSourceWithPrebuilt = hiddenAPIStubsDependencyTag{} var _ android.ExcludeFromApexContentsTag = hiddenAPIStubsDependencyTag{} -var _ android.SdkMemberTypeDependencyTag = hiddenAPIStubsDependencyTag{} +var _ android.SdkMemberDependencyTag = hiddenAPIStubsDependencyTag{} // hiddenAPIComputeMonolithicStubLibModules computes the set of module names that provide stubs // needed to produce the hidden API monolithic stub flags file. @@ -277,7 +278,7 @@ func hiddenAPIAddStubLibDependencies(ctx android.BottomUpMutatorContext, apiScop // hiddenAPIRetrieveDexJarBuildPath retrieves the DexJarBuildPath from the specified module, if // available, or reports an error. func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android.Module, kind android.SdkKind) android.Path { - var dexJar android.Path + var dexJar OptionalDexJarPath if sdkLibrary, ok := module.(SdkLibraryDependency); ok { dexJar = sdkLibrary.SdkApiStubDexJar(ctx, kind) } else if j, ok := module.(UsesLibraryDependency); ok { @@ -287,10 +288,11 @@ func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android. return nil } - if dexJar == nil { - ctx.ModuleErrorf("dependency %s does not provide a dex jar, consider setting compile_dex: true", module) + if !dexJar.Valid() { + ctx.ModuleErrorf("dependency %s does not provide a dex jar: %s", module, dexJar.InvalidReason()) + return nil } - return dexJar + return dexJar.Path() } // buildRuleToGenerateHiddenAPIStubFlagsFile creates a rule to create a hidden API stub flags file. @@ -546,18 +548,18 @@ func (i *HiddenAPIInfo) mergeFromFragmentDeps(ctx android.ModuleContext, fragmen } } -// StubFlagSubset returns a SignatureCsvSubset that contains a path to a stub-flags.csv file and a -// path to a signature-patterns.csv file that defines a subset of the monolithic stub flags file, -// i.e. out/soong/hiddenapi/hiddenapi-stub-flags.txt, against which it will be compared. +// StubFlagSubset returns a SignatureCsvSubset that contains a path to a filtered-stub-flags.csv +// file and a path to a signature-patterns.csv file that defines a subset of the monolithic stub +// flags file, i.e. out/soong/hiddenapi/hiddenapi-stub-flags.txt, against which it will be compared. func (i *HiddenAPIInfo) StubFlagSubset() SignatureCsvSubset { - return SignatureCsvSubset{i.StubFlagsPath, i.SignaturePatternsPath} + return SignatureCsvSubset{i.FilteredStubFlagsPath, i.SignaturePatternsPath} } -// FlagSubset returns a SignatureCsvSubset that contains a path to an all-flags.csv file and a +// FlagSubset returns a SignatureCsvSubset that contains a path to a filtered-flags.csv file and a // path to a signature-patterns.csv file that defines a subset of the monolithic flags file, i.e. // out/soong/hiddenapi/hiddenapi-flags.csv, against which it will be compared. func (i *HiddenAPIInfo) FlagSubset() SignatureCsvSubset { - return SignatureCsvSubset{i.AllFlagsPath, i.SignaturePatternsPath} + return SignatureCsvSubset{i.FilteredFlagsPath, i.SignaturePatternsPath} } var HiddenAPIInfoProvider = blueprint.NewProvider(HiddenAPIInfo{}) @@ -782,9 +784,6 @@ func (i *HiddenAPIFlagInput) transitiveStubDexJarsByScope() StubDexJarsByModule // HiddenAPIFlagOutput contains paths to output files from the hidden API flag generation for a // bootclasspath_fragment module. type HiddenAPIFlagOutput struct { - // The path to the generated stub-flags.csv file. - StubFlagsPath android.Path - // The path to the generated annotation-flags.csv file. AnnotationFlagsPath android.Path @@ -794,12 +793,21 @@ type HiddenAPIFlagOutput struct { // The path to the generated index.csv file. IndexPath android.Path + // The path to the generated stub-flags.csv file. + StubFlagsPath android.Path + // The path to the generated all-flags.csv file. AllFlagsPath android.Path // The path to the generated signature-patterns.txt file which defines the subset of the // monolithic hidden API files provided in this. SignaturePatternsPath android.Path + + // The path to the generated filtered-stub-flags.csv file. + FilteredStubFlagsPath android.Path + + // The path to the generated filtered-flags.csv file. + FilteredFlagsPath android.Path } // bootDexJarByModule is a map from base module name (without prebuilt_ prefix) to the boot dex @@ -935,13 +943,22 @@ func (s SignatureCsvSubsets) RelativeToTop() []string { // buildRuleSignaturePatternsFile creates a rule to generate a file containing the set of signature // patterns that will select a subset of the monolithic flags. -func buildRuleSignaturePatternsFile(ctx android.ModuleContext, flagsPath android.Path) android.Path { +func buildRuleSignaturePatternsFile(ctx android.ModuleContext, flagsPath android.Path, splitPackages []string, packagePrefixes []string) android.Path { patternsFile := android.PathForModuleOut(ctx, "modular-hiddenapi", "signature-patterns.csv") // Create a rule to validate the output from the following rule. rule := android.NewRuleBuilder(pctx, ctx) + + // Quote any * in the packages to avoid them being expanded by the shell. + quotedSplitPackages := []string{} + for _, pkg := range splitPackages { + quotedSplitPackages = append(quotedSplitPackages, strings.ReplaceAll(pkg, "*", "\\*")) + } + rule.Command(). BuiltTool("signature_patterns"). FlagWithInput("--flags ", flagsPath). + FlagForEachArg("--split-package ", quotedSplitPackages). + FlagForEachArg("--package-prefix ", packagePrefixes). FlagWithOutput("--output ", patternsFile) rule.Build("hiddenAPISignaturePatterns", "hidden API signature patterns") @@ -1065,11 +1082,13 @@ func hiddenAPIRulesForBootclasspathFragment(ctx android.ModuleContext, contents // Store the paths in the info for use by other modules and sdk snapshot generation. output := HiddenAPIOutput{ HiddenAPIFlagOutput: HiddenAPIFlagOutput{ - StubFlagsPath: filteredStubFlagsCSV, - AnnotationFlagsPath: annotationFlagsCSV, - MetadataPath: metadataCSV, - IndexPath: indexCSV, - AllFlagsPath: filteredFlagsCSV, + AnnotationFlagsPath: annotationFlagsCSV, + MetadataPath: metadataCSV, + IndexPath: indexCSV, + StubFlagsPath: stubFlagsCSV, + AllFlagsPath: allFlagsCSV, + FilteredStubFlagsPath: filteredStubFlagsCSV, + FilteredFlagsPath: filteredFlagsCSV, }, EncodedBootDexFilesByModule: encodedBootDexJarsByModule, } @@ -1159,18 +1178,17 @@ func extractBootDexInfoFromModules(ctx android.ModuleContext, contents []android // retrieveBootDexJarFromHiddenAPIModule retrieves the boot dex jar from the hiddenAPIModule. // -// If the module does not provide a boot dex jar, i.e. the returned boot dex jar is nil, then that -// create a fake path and either report an error immediately or defer reporting of the error until -// the path is actually used. +// If the module does not provide a boot dex jar, i.e. the returned boot dex jar is unset or +// invalid, then create a fake path and either report an error immediately or defer reporting of the +// error until the path is actually used. func retrieveBootDexJarFromHiddenAPIModule(ctx android.ModuleContext, module hiddenAPIModule) android.Path { bootDexJar := module.bootDexJar() - if bootDexJar == nil { + if !bootDexJar.Valid() { fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/boot-dex/%s.jar", module.Name())) - bootDexJar = fake - - handleMissingDexBootFile(ctx, module, fake) + handleMissingDexBootFile(ctx, module, fake, bootDexJar.InvalidReason()) + return fake } - return bootDexJar + return bootDexJar.Path() } // extractClassesJarsFromModules extracts the class jars from the supplied modules. @@ -1194,13 +1212,6 @@ func retrieveClassesJarsFromModule(module android.Module) android.Paths { // deferReportingMissingBootDexJar returns true if a missing boot dex jar should not be reported by // Soong but should instead only be reported in ninja if the file is actually built. func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.Module) bool { - // TODO(b/179354495): Remove this workaround when it is unnecessary. - // Prebuilt modules like framework-wifi do not yet provide dex implementation jars. So, - // create a fake one that will cause a build error only if it is used. - if ctx.Config().AlwaysUsePrebuiltSdks() { - return true - } - // Any missing dependency should be allowed. if ctx.Config().AllowMissingDependencies() { return true @@ -1271,7 +1282,7 @@ func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.M // handleMissingDexBootFile will either log a warning or create an error rule to create the fake // file depending on the value returned from deferReportingMissingBootDexJar. -func handleMissingDexBootFile(ctx android.ModuleContext, module android.Module, fake android.WritablePath) { +func handleMissingDexBootFile(ctx android.ModuleContext, module android.Module, fake android.WritablePath, reason string) { if deferReportingMissingBootDexJar(ctx, module) { // Create an error rule that pretends to create the output file but will actually fail if it // is run. @@ -1279,11 +1290,11 @@ func handleMissingDexBootFile(ctx android.ModuleContext, module android.Module, Rule: android.ErrorRule, Output: fake, Args: map[string]string{ - "error": fmt.Sprintf("missing dependencies: boot dex jar for %s", module), + "error": fmt.Sprintf("missing boot dex jar dependency for %s: %s", module, reason), }, }) } else { - ctx.ModuleErrorf("module %s does not provide a dex jar", module) + ctx.ModuleErrorf("module %s does not provide a dex jar: %s", module, reason) } } @@ -1294,14 +1305,13 @@ func handleMissingDexBootFile(ctx android.ModuleContext, module android.Module, // However, under certain conditions, e.g. errors, or special build configurations it will return // a path to a fake file. func retrieveEncodedBootDexJarFromModule(ctx android.ModuleContext, module android.Module) android.Path { - bootDexJar := module.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath() - if bootDexJar == nil { + bootDexJar := module.(interface{ DexJarBuildPath() OptionalDexJarPath }).DexJarBuildPath() + if !bootDexJar.Valid() { fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/encoded-dex/%s.jar", module.Name())) - bootDexJar = fake - - handleMissingDexBootFile(ctx, module, fake) + handleMissingDexBootFile(ctx, module, fake, bootDexJar.InvalidReason()) + return fake } - return bootDexJar + return bootDexJar.Path() } // extractEncodedDexJarsFromModules extracts the encoded dex jars from the supplied modules. diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go index dcd363c2c..75b7bb7c8 100644 --- a/java/hiddenapi_singleton_test.go +++ b/java/hiddenapi_singleton_test.go @@ -20,6 +20,7 @@ import ( "testing" "android/soong/android" + "github.com/google/blueprint/proptools" ) @@ -306,7 +307,7 @@ func TestHiddenAPIEncoding_JavaSdkLibrary(t *testing.T) { android.AssertStringEquals(t, "encode embedded java_library", unencodedDexJar, actualUnencodedDexJar.String()) // Make sure that the encoded dex jar is the exported one. - exportedDexJar := moduleForTests.Module().(UsesLibraryDependency).DexJarBuildPath() + exportedDexJar := moduleForTests.Module().(UsesLibraryDependency).DexJarBuildPath().Path() android.AssertPathRelativeToTopEquals(t, "encode embedded java_library", encodedDexJar, exportedDexJar) } diff --git a/java/java.go b/java/java.go index 1a052b432..9b4a005f0 100644 --- a/java/java.go +++ b/java/java.go @@ -21,7 +21,9 @@ package java import ( "fmt" "path/filepath" + "strings" + "android/soong/bazel" "github.com/google/blueprint" "github.com/google/blueprint/proptools" @@ -73,6 +75,7 @@ func RegisterJavaSdkMemberTypes() { android.RegisterSdkMemberType(javaHeaderLibsSdkMemberType) android.RegisterSdkMemberType(javaLibsSdkMemberType) android.RegisterSdkMemberType(javaBootLibsSdkMemberType) + android.RegisterSdkMemberType(javaSystemserverLibsSdkMemberType) android.RegisterSdkMemberType(javaTestSdkMemberType) } @@ -146,6 +149,37 @@ var ( onlyCopyJarToSnapshot, } + // Supports adding java systemserver libraries to module_exports and sdk. + // + // The build has some implicit dependencies (via the systemserver jars configuration) on a number + // of modules that are part of the java systemserver classpath and which are provided by mainline + // modules 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_systemserver_libs property to allow those modules to be exported as part of + // the sdk/module_exports without exposing any unnecessary information. + javaSystemserverLibsSdkMemberType = &librarySdkMemberType{ + android.SdkMemberTypeBase{ + PropertyName: "java_systemserver_libs", + SupportsSdk: true, + }, + func(ctx android.SdkMemberContext, j *Library) android.Path { + // Java systemserver libs are only provided in the SDK to provide access to their dex + // implementation jar for use by dexpreopting. 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_systemserver_libs", "snapshot", "jars", "are", "invalid", name+jarFileSuffix) + }, + onlyCopyJarToSnapshot, + } + // Supports adding java test libraries to module_exports but not sdk. javaTestSdkMemberType = &testSdkMemberType{ SdkMemberTypeBase: android.SdkMemberTypeBase{ @@ -219,7 +253,7 @@ type ApexDependency interface { // Provides build path and install path to DEX jars. type UsesLibraryDependency interface { - DexJarBuildPath() android.Path + DexJarBuildPath() OptionalDexJarPath DexJarInstallPath() android.Path ClassLoaderContexts() dexpreopt.ClassLoaderContextMap } @@ -236,6 +270,9 @@ func (j *Module) XrefJavaFiles() android.Paths { type dependencyTag struct { blueprint.BaseDependencyTag name string + + // True if the dependency is relinked at runtime. + runtimeLinked bool } // installDependencyTag is a dependency tag that is annotated to cause the installed files of the @@ -246,6 +283,15 @@ type installDependencyTag struct { name string } +func (d dependencyTag) LicenseAnnotations() []android.LicenseAnnotation { + if d.runtimeLinked { + return []android.LicenseAnnotation{android.LicenseAnnotationSharedDependency} + } + return nil +} + +var _ android.LicenseAnnotationsDependencyTag = dependencyTag{} + type usesLibraryDependencyTag struct { dependencyTag @@ -262,10 +308,13 @@ type usesLibraryDependencyTag struct { func makeUsesLibraryDependencyTag(sdkVersion int, optional bool, implicit bool) usesLibraryDependencyTag { return usesLibraryDependencyTag{ - dependencyTag: dependencyTag{name: fmt.Sprintf("uses-library-%d", sdkVersion)}, - sdkVersion: sdkVersion, - optional: optional, - implicit: implicit, + dependencyTag: dependencyTag{ + name: fmt.Sprintf("uses-library-%d", sdkVersion), + runtimeLinked: true, + }, + sdkVersion: sdkVersion, + optional: optional, + implicit: implicit, } } @@ -276,21 +325,22 @@ func IsJniDepTag(depTag blueprint.DependencyTag) bool { var ( dataNativeBinsTag = dependencyTag{name: "dataNativeBins"} staticLibTag = dependencyTag{name: "staticlib"} - libTag = dependencyTag{name: "javalib"} - java9LibTag = dependencyTag{name: "java9lib"} + libTag = dependencyTag{name: "javalib", runtimeLinked: true} + java9LibTag = dependencyTag{name: "java9lib", runtimeLinked: true} pluginTag = dependencyTag{name: "plugin"} errorpronePluginTag = dependencyTag{name: "errorprone-plugin"} exportedPluginTag = dependencyTag{name: "exported-plugin"} - bootClasspathTag = dependencyTag{name: "bootclasspath"} - systemModulesTag = dependencyTag{name: "system modules"} + bootClasspathTag = dependencyTag{name: "bootclasspath", runtimeLinked: true} + systemModulesTag = dependencyTag{name: "system modules", runtimeLinked: true} frameworkResTag = dependencyTag{name: "framework-res"} - kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib"} - kotlinAnnotationsTag = dependencyTag{name: "kotlin-annotations"} + kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib", runtimeLinked: true} + kotlinAnnotationsTag = dependencyTag{name: "kotlin-annotations", runtimeLinked: true} + kotlinPluginTag = dependencyTag{name: "kotlin-plugin"} proguardRaiseTag = dependencyTag{name: "proguard-raise"} certificateTag = dependencyTag{name: "certificate"} instrumentationForTag = dependencyTag{name: "instrumentation_for"} extraLintCheckTag = dependencyTag{name: "extra-lint-check"} - jniLibTag = dependencyTag{name: "jnilib"} + jniLibTag = dependencyTag{name: "jnilib", runtimeLinked: true} syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"} jniInstallTag = installDependencyTag{name: "jni install"} binaryInstallTag = installDependencyTag{name: "binary install"} @@ -380,6 +430,7 @@ type deps struct { aidlPreprocess android.OptionalPath kotlinStdlib android.Paths kotlinAnnotations android.Paths + kotlinPlugins android.Paths disableTurbine bool } @@ -398,6 +449,12 @@ func getJavaVersion(ctx android.ModuleContext, javaVersion string, sdkContext an return normalizeJavaVersion(ctx, javaVersion) } else if ctx.Device() { return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion(ctx)) + } else if ctx.Config().TargetsJava11() { + // Temporary experimental flag to be able to try and build with + // java version 11 options. The flag, if used, just sets Java + // 11 as the default version, leaving any components that + // target an older version intact. + return JAVA_VERSION_11 } else { return JAVA_VERSION_9 } @@ -411,6 +468,7 @@ const ( JAVA_VERSION_7 = 7 JAVA_VERSION_8 = 8 JAVA_VERSION_9 = 9 + JAVA_VERSION_11 = 11 ) func (v javaVersion) String() string { @@ -423,6 +481,8 @@ func (v javaVersion) String() string { return "1.8" case JAVA_VERSION_9: return "1.9" + case JAVA_VERSION_11: + return "11" default: return "unsupported" } @@ -443,8 +503,10 @@ func normalizeJavaVersion(ctx android.BaseModuleContext, javaVersion string) jav return JAVA_VERSION_8 case "1.9", "9": return JAVA_VERSION_9 - case "10", "11": - ctx.PropertyErrorf("java_version", "Java language levels above 9 are not supported") + case "11": + return JAVA_VERSION_11 + case "10": + ctx.PropertyErrorf("java_version", "Java language levels 10 is not supported") return JAVA_VERSION_UNSUPPORTED default: ctx.PropertyErrorf("java_version", "Unrecognized Java language level") @@ -487,7 +549,7 @@ func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bo } // Store uncompressed dex files that are preopted on /system. - if !dexpreopter.dexpreoptDisabled(ctx) && (ctx.Host() || !odexOnSystemOther(ctx, dexpreopter.installPath)) { + if !dexpreopter.dexpreoptDisabled(ctx) && (ctx.Host() || !dexpreopter.odexOnSystemOther(ctx, dexpreopter.installPath)) { return true } if ctx.Config().UncompressPrivAppDex() && @@ -498,9 +560,18 @@ func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bo return false } +// Sets `dexer.dexProperties.Uncompress_dex` to the proper value. +func setUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter, dexer *dexer) { + if dexer.dexProperties.Uncompress_dex == nil { + // If the value was not force-set by the user, use reasonable default based on the module. + dexer.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, dexpreopter)) + } +} + func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.sdkVersion = j.SdkVersion(ctx) j.minSdkVersion = j.MinSdkVersion(ctx) + j.maxSdkVersion = j.MaxSdkVersion(ctx) apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) if !apexInfo.IsForPlatform() { @@ -508,12 +579,10 @@ func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { } j.checkSdkVersions(ctx) - j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar") + j.dexpreopter.installPath = j.dexpreopter.getInstallPath( + ctx, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")) j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary - if j.dexProperties.Uncompress_dex == nil { - // If the value was not force-set by the user, use reasonable default based on the module. - j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter)) - } + setUncompressDex(ctx, &j.dexpreopter, &j.dexer) j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex j.classLoaderContexts = j.usesLibrary.classLoaderContextForUsesLibDeps(ctx) j.compile(ctx, nil) @@ -527,8 +596,23 @@ func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { if j.InstallMixin != nil { extraInstallDeps = j.InstallMixin(ctx, j.outputFile) } - j.installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"), - j.Stem()+".jar", j.outputFile, extraInstallDeps...) + hostDexNeeded := Bool(j.deviceProperties.Hostdex) && !ctx.Host() + if hostDexNeeded { + j.hostdexInstallFile = ctx.InstallFile( + android.PathForHostDexInstall(ctx, "framework"), + j.Stem()+"-hostdex.jar", j.outputFile) + } + var installDir android.InstallPath + if ctx.InstallInTestcases() { + var archDir string + if !ctx.Host() { + archDir = ctx.DeviceConfig().DeviceArch() + } + installDir = android.PathForModuleInstall(ctx, ctx.ModuleName(), archDir) + } else { + installDir = android.PathForModuleInstall(ctx, "framework") + } + j.installFile = ctx.InstallFile(installDir, j.Stem()+".jar", j.outputFile, extraInstallDeps...) } } @@ -672,6 +756,7 @@ func LibraryFactory() android.Module { android.InitApexModule(module) android.InitSdkAwareModule(module) + android.InitBazelModule(module) InitJavaModule(module, android.HostAndDeviceSupported) return module } @@ -694,6 +779,7 @@ func LibraryHostFactory() android.Module { android.InitApexModule(module) android.InitSdkAwareModule(module) + android.InitBazelModule(module) InitJavaModule(module, android.HostSupported) return module } @@ -742,6 +828,9 @@ type testProperties struct { // Names of modules containing JNI libraries that should be installed alongside the test. Jni_libs []string + + // Install the test into a folder named for the module in all test suites. + Per_testcase_directory *bool } type hostTestProperties struct { @@ -753,6 +842,9 @@ type testHelperLibraryProperties struct { // list of compatibility suites (for example "cts", "vts") that the module should be // installed into. Test_suites []string `android:"arch_variant"` + + // Install the test into a folder named for the module in all test suites. + Per_testcase_directory *bool } type prebuiltTestProperties struct { @@ -796,6 +888,20 @@ type JavaTestImport struct { dexJarFile android.Path } +func (j *Test) InstallInTestcases() bool { + // Host java tests install into $(HOST_OUT_JAVA_LIBRARIES), and then are copied into + // testcases by base_rules.mk. + return !j.Host() +} + +func (j *TestHelperLibrary) InstallInTestcases() bool { + return true +} + +func (j *JavaTestImport) InstallInTestcases() bool { + return true +} + func (j *TestHost) DepsMutator(ctx android.BottomUpMutatorContext) { if len(j.testHostProperties.Data_native_bins) > 0 { for _, target := range ctx.MultiTargets() { @@ -1027,14 +1133,14 @@ func InitTestHost(th *TestHost, installable *bool, testSuites []string, autoGenC type binaryProperties struct { // installable script to execute the resulting jar - Wrapper *string `android:"path"` + Wrapper *string `android:"path,arch_variant"` // Name of the class containing main to be inserted into the manifest as Main-Class. Main_class *string // Names of modules containing JNI libraries that should be installed alongside the host // variant of the binary. - Jni_libs []string + Jni_libs []string `android:"arch_variant"` } type Binary struct { @@ -1072,14 +1178,23 @@ func (j *Binary) GenerateAndroidBuildActions(ctx android.ModuleContext) { if j.binaryProperties.Wrapper != nil { j.wrapperFile = android.PathForModuleSrc(ctx, *j.binaryProperties.Wrapper) } else { + if ctx.Windows() { + ctx.PropertyErrorf("wrapper", "wrapper is required for Windows") + } + j.wrapperFile = android.PathForSource(ctx, "build/soong/scripts/jar-wrapper.sh") } + ext := "" + if ctx.Windows() { + ext = ".bat" + } + // The host installation rules make the installed wrapper depend on all the dependencies // of the wrapper variant, which will include the common variant's jar file and any JNI // libraries. This is verified by TestBinary. j.binaryFile = ctx.InstallExecutable(android.PathForModuleInstall(ctx, "bin"), - ctx.ModuleName(), j.wrapperFile) + ctx.ModuleName()+ext, j.wrapperFile) } } @@ -1115,6 +1230,8 @@ func BinaryFactory() android.Module { android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommonFirst) android.InitDefaultableModule(module) + android.InitBazelModule(module) + return module } @@ -1132,6 +1249,7 @@ func BinaryHostFactory() android.Module { android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommonFirst) android.InitDefaultableModule(module) + android.InitBazelModule(module) return module } @@ -1153,7 +1271,6 @@ type ImportProperties struct { Installable *bool // If not empty, classes are restricted to the specified packages and their sub-packages. - // This information is used to generate the updatable-bcp-packages.txt file. Permitted_packages []string // List of shared java libs that this module has dependencies to @@ -1195,7 +1312,7 @@ type Import struct { properties ImportProperties // output file containing classes.dex and resources - dexJarFile android.Path + dexJarFile OptionalDexJarPath dexJarInstallFile android.Path combinedClasspathFile android.Path @@ -1280,6 +1397,10 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.hideApexVariantFromMake = true } + if ctx.Windows() { + j.HideFromMake() + } + jars := android.PathsForModuleSrc(ctx, j.properties.Jars) jarName := j.Stem() + ".jar" @@ -1295,7 +1416,6 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap) var flags javaBuilderFlags - var deapexerModule android.Module ctx.VisitDirectDeps(func(module android.Module) { tag := ctx.OtherModuleDependencyTag(module) @@ -1316,16 +1436,20 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { } addCLCFromDep(ctx, module, j.classLoaderContexts) - - // Save away the `deapexer` module on which this depends, if any. - if tag == android.DeapexerTag { - deapexerModule = module - } }) if Bool(j.properties.Installable) { - ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"), - jarName, outputFile) + var installDir android.InstallPath + if ctx.InstallInTestcases() { + var archDir string + if !ctx.Host() { + archDir = ctx.DeviceConfig().DeviceArch() + } + installDir = android.PathForModuleInstall(ctx, ctx.ModuleName(), archDir) + } else { + installDir = android.PathForModuleInstall(ctx, "framework") + } + ctx.InstallFile(installDir, jarName, outputFile) } j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs) @@ -1335,26 +1459,28 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { // obtained from the associated deapexer module. ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) if ai.ForPrebuiltApex { - if deapexerModule == nil { - // This should never happen as a variant for a prebuilt_apex is only created if the - // deapexer module has been configured to export the dex implementation jar for this module. - ctx.ModuleErrorf("internal error: module %q does not depend on a `deapexer` module for prebuilt_apex %q", - j.Name(), ai.ApexVariationName) - return - } - // Get the path of the dex implementation jar from the `deapexer` module. - di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo) + di := android.FindDeapexerProviderForModule(ctx) + if di == nil { + return // An error has been reported by FindDeapexerProviderForModule. + } if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(j.BaseModuleName())); dexOutputPath != nil { - j.dexJarFile = dexOutputPath - j.dexJarInstallFile = android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(j.BaseModuleName())) + dexJarFile := makeDexJarPathFromPath(dexOutputPath) + j.dexJarFile = dexJarFile + installPath := android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(j.BaseModuleName())) + j.dexJarInstallFile = installPath + + j.dexpreopter.installPath = j.dexpreopter.getInstallPath(ctx, installPath) + setUncompressDex(ctx, &j.dexpreopter, &j.dexer) + j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex + j.dexpreopt(ctx, dexOutputPath) // Initialize the hiddenapi structure. - j.initHiddenAPI(ctx, dexOutputPath, outputFile, nil) + j.initHiddenAPI(ctx, dexJarFile, outputFile, j.dexProperties.Uncompress_dex) } else { // This should never happen as a variant for a prebuilt_apex is only created if the // prebuilt_apex has been configured to export the java library dex file. - ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt_apex %q", deapexerModule.Name()) + ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt APEX %s", di.ApexModuleName()) } } else if Bool(j.dexProperties.Compile_dex) { sdkDep := decodeSdkDep(ctx, android.SdkContext(j)) @@ -1368,11 +1494,9 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { // Dex compilation - j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", jarName) - if j.dexProperties.Uncompress_dex == nil { - // If the value was not force-set by the user, use reasonable default based on the module. - j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter)) - } + j.dexpreopter.installPath = j.dexpreopter.getInstallPath( + ctx, android.PathForModuleInstall(ctx, "framework", jarName)) + setUncompressDex(ctx, &j.dexpreopter, &j.dexer) j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex var dexOutputFile android.OutputPath @@ -1382,12 +1506,12 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { } // Initialize the hiddenapi structure. - j.initHiddenAPI(ctx, dexOutputFile, outputFile, j.dexProperties.Uncompress_dex) + j.initHiddenAPI(ctx, makeDexJarPathFromPath(dexOutputFile), outputFile, j.dexProperties.Uncompress_dex) // Encode hidden API flags in dex file. dexOutputFile = j.hiddenAPIEncodeDex(ctx, dexOutputFile) - j.dexJarFile = dexOutputFile + j.dexJarFile = makeDexJarPathFromPath(dexOutputFile) j.dexJarInstallFile = android.PathForModuleInstall(ctx, "framework", jarName) } } @@ -1425,7 +1549,7 @@ func (j *Import) ImplementationAndResourcesJars() android.Paths { return android.Paths{j.combinedClasspathFile} } -func (j *Import) DexJarBuildPath() android.Path { +func (j *Import) DexJarBuildPath() OptionalDexJarPath { return j.dexJarFile } @@ -1488,9 +1612,6 @@ var _ android.IDEInfo = (*Import)(nil) var _ android.IDECustomizedModuleName = (*Import)(nil) // Collect information for opening IDE project files in java/jdeps.go. -const ( - removedPrefix = "prebuilt_" -) func (j *Import) IDEInfo(dpInfo *android.IdeInfo) { dpInfo.Jars = append(dpInfo.Jars, j.PrebuiltSrcs()...) @@ -1509,7 +1630,7 @@ func (j *Import) IsInstallable() bool { return Bool(j.properties.Installable) } -var _ dexpreopterInterface = (*Import)(nil) +var _ DexpreopterInterface = (*Import)(nil) // java_import imports one or more `.jar` files into the build graph as if they were built by a java_library module. // @@ -1570,7 +1691,7 @@ type DexImport struct { properties DexImportProperties - dexJarFile android.Path + dexJarFile OptionalDexJarPath dexpreopter @@ -1622,7 +1743,8 @@ func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.hideApexVariantFromMake = true } - j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar") + j.dexpreopter.installPath = j.dexpreopter.getInstallPath( + ctx, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")) j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter) inputJar := ctx.ExpandSource(j.properties.Jars[0], "jars") @@ -1660,7 +1782,7 @@ func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { }) } - j.dexJarFile = dexOutputFile + j.dexJarFile = makeDexJarPathFromPath(dexOutputFile) j.dexpreopt(ctx, dexOutputFile) @@ -1670,7 +1792,7 @@ func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { } } -func (j *DexImport) DexJarBuildPath() android.Path { +func (j *DexImport) DexJarBuildPath() OptionalDexJarPath { return j.dexJarFile } @@ -1839,8 +1961,108 @@ func addCLCFromDep(ctx android.ModuleContext, depModule android.Module, // from its CLC should be added to the current CLC. if sdkLib != nil { clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, false, true, - dep.DexJarBuildPath(), dep.DexJarInstallPath(), dep.ClassLoaderContexts()) + dep.DexJarBuildPath().PathOrNil(), dep.DexJarInstallPath(), dep.ClassLoaderContexts()) } else { clcMap.AddContextMap(dep.ClassLoaderContexts(), depName) } } + +type javaLibraryAttributes struct { + Srcs bazel.LabelListAttribute + Deps bazel.LabelListAttribute + Javacopts bazel.StringListAttribute +} + +func javaLibraryBp2Build(ctx android.TopDownMutatorContext, m *Library) { + srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)) + attrs := &javaLibraryAttributes{ + Srcs: srcs, + } + + if m.properties.Javacflags != nil { + attrs.Javacopts = bazel.MakeStringListAttribute(m.properties.Javacflags) + } + + if m.properties.Libs != nil { + attrs.Deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, m.properties.Libs)) + } + + props := bazel.BazelTargetModuleProperties{ + Rule_class: "java_library", + Bzl_load_location: "//build/bazel/rules/java:library.bzl", + } + + ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs) +} + +type javaBinaryHostAttributes struct { + Srcs bazel.LabelListAttribute + Deps bazel.LabelListAttribute + Main_class string + Jvm_flags bazel.StringListAttribute +} + +// JavaBinaryHostBp2Build is for java_binary_host bp2build. +func javaBinaryHostBp2Build(ctx android.TopDownMutatorContext, m *Binary) { + mainClass := "" + if m.binaryProperties.Main_class != nil { + mainClass = *m.binaryProperties.Main_class + } + if m.properties.Manifest != nil { + mainClassInManifest, err := android.GetMainClassInManifest(ctx.Config(), android.PathForModuleSrc(ctx, *m.properties.Manifest).String()) + if err != nil { + return + } + mainClass = mainClassInManifest + } + srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)) + attrs := &javaBinaryHostAttributes{ + Srcs: srcs, + Main_class: mainClass, + } + + // Attribute deps + deps := []string{} + if m.properties.Static_libs != nil { + deps = append(deps, m.properties.Static_libs...) + } + if m.binaryProperties.Jni_libs != nil { + deps = append(deps, m.binaryProperties.Jni_libs...) + } + if len(deps) > 0 { + attrs.Deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, deps)) + } + + // Attribute jvm_flags + if m.binaryProperties.Jni_libs != nil { + jniLibPackages := map[string]bool{} + for _, jniLibLabel := range android.BazelLabelForModuleDeps(ctx, m.binaryProperties.Jni_libs).Includes { + jniLibPackage := jniLibLabel.Label + indexOfColon := strings.Index(jniLibLabel.Label, ":") + if indexOfColon > 0 { + // JNI lib from other package + jniLibPackage = jniLibLabel.Label[2:indexOfColon] + } else if indexOfColon == 0 { + // JNI lib in the same package of java_binary + packageOfCurrentModule := m.GetBazelLabel(ctx, m) + jniLibPackage = packageOfCurrentModule[2:strings.Index(packageOfCurrentModule, ":")] + } + if _, inMap := jniLibPackages[jniLibPackage]; !inMap { + jniLibPackages[jniLibPackage] = true + } + } + jniLibPaths := []string{} + for jniLibPackage, _ := range jniLibPackages { + // See cs/f:.*/third_party/bazel/.*java_stub_template.txt for the use of RUNPATH + jniLibPaths = append(jniLibPaths, "$${RUNPATH}"+jniLibPackage) + } + attrs.Jvm_flags = bazel.MakeStringListAttribute([]string{"-Djava.library.path=" + strings.Join(jniLibPaths, ":")}) + } + + props := bazel.BazelTargetModuleProperties{ + Rule_class: "java_binary", + } + + // Create the BazelTargetModule. + ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs) +} diff --git a/java/java_test.go b/java/java_test.go index 8bb017f0b..6e4e673ec 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -600,8 +600,8 @@ func TestPrebuilts(t *testing.T) { } barDexJar := barModule.Module().(*Import).DexJarBuildPath() - if barDexJar != nil { - t.Errorf("bar dex jar build path expected to be nil, got %q", barDexJar) + if barDexJar.IsSet() { + t.Errorf("bar dex jar build path expected to be set, got %s", barDexJar) } if !strings.Contains(javac.Args["classpath"], sdklibStubsJar.String()) { @@ -612,7 +612,7 @@ func TestPrebuilts(t *testing.T) { t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, bazJar.String()) } - bazDexJar := bazModule.Module().(*Import).DexJarBuildPath() + bazDexJar := bazModule.Module().(*Import).DexJarBuildPath().Path() expectedDexJar := "out/soong/.intermediates/baz/android_common/dex/baz.jar" android.AssertPathRelativeToTopEquals(t, "baz dex jar build path", expectedDexJar, bazDexJar) @@ -988,11 +988,11 @@ func TestSharding(t *testing.T) { } `) - barHeaderJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar") + barHeaderJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine", "bar.jar") for i := 0; i < 3; i++ { barJavac := ctx.ModuleForTests("bar", "android_common").Description("javac" + strconv.Itoa(i)) - if !strings.Contains(barJavac.Args["classpath"], barHeaderJar) { - t.Errorf("bar javac classpath %v does not contain %q", barJavac.Args["classpath"], barHeaderJar) + if !strings.HasPrefix(barJavac.Args["classpath"], "-classpath "+barHeaderJar+":") { + t.Errorf("bar javac classpath %v does start with %q", barJavac.Args["classpath"], barHeaderJar) } } } @@ -1357,6 +1357,36 @@ func TestAidlFlagsArePassedToTheAidlCompiler(t *testing.T) { } } +func TestAidlFlagsWithMinSdkVersion(t *testing.T) { + fixture := android.GroupFixturePreparers( + prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{"14": {"foo"}})) + + for _, tc := range []struct { + name string + sdkVersion string + expected string + }{ + {"default is current", "", "current"}, + {"use sdk_version", `sdk_version: "14"`, "14"}, + {"system_current", `sdk_version: "system_current"`, "current"}, + } { + t.Run(tc.name, func(t *testing.T) { + ctx := fixture.RunTestWithBp(t, ` + java_library { + name: "foo", + srcs: ["aidl/foo/IFoo.aidl"], + `+tc.sdkVersion+` + } + `) + aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command + expectedAidlFlag := "--min_sdk_version=" + tc.expected + if !strings.Contains(aidlCommand, expectedAidlFlag) { + t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag) + } + }) + } +} + func TestDataNativeBinaries(t *testing.T) { ctx, _ := testJava(t, ` java_test_host { diff --git a/java/jdeps.go b/java/jdeps.go index 0ab2e422b..eff9a3174 100644 --- a/java/jdeps.go +++ b/java/jdeps.go @@ -40,16 +40,11 @@ type jdepsGeneratorSingleton struct { var _ android.SingletonMakeVarsProvider = (*jdepsGeneratorSingleton)(nil) const ( - // Environment variables used to modify behavior of this singleton. - envVariableCollectJavaDeps = "SOONG_COLLECT_JAVA_DEPS" - jdepsJsonFileName = "module_bp_java_deps.json" + jdepsJsonFileName = "module_bp_java_deps.json" ) func (j *jdepsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) { - if !ctx.Config().IsEnvTrue(envVariableCollectJavaDeps) { - return - } - + // (b/204397180) Generate module_bp_java_deps.json by default. moduleInfos := make(map[string]android.IdeInfo) ctx.VisitAllModules(func(module android.Module) { diff --git a/java/kotlin.go b/java/kotlin.go index 3a6fc0f48..e4f1bc198 100644 --- a/java/kotlin.go +++ b/java/kotlin.go @@ -81,6 +81,7 @@ func kotlinCompile(ctx android.ModuleContext, outputFile android.WritablePath, var deps android.Paths deps = append(deps, flags.kotlincClasspath...) + deps = append(deps, flags.kotlincDeps...) deps = append(deps, srcJars...) deps = append(deps, commonSrcFiles...) diff --git a/java/kotlin_test.go b/java/kotlin_test.go index db3069693..cac0af3b9 100644 --- a/java/kotlin_test.go +++ b/java/kotlin_test.go @@ -281,3 +281,46 @@ func TestKaptEncodeFlags(t *testing.T) { }) } } + +func TestKotlinCompose(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + ).RunTestWithBp(t, ` + java_library { + name: "androidx.compose.runtime_runtime", + } + + java_library_host { + name: "androidx.compose.compiler_compiler-hosted", + } + + java_library { + name: "withcompose", + srcs: ["a.kt"], + static_libs: ["androidx.compose.runtime_runtime"], + } + + java_library { + name: "nocompose", + srcs: ["a.kt"], + } + `) + + buildOS := result.Config.BuildOS.String() + + composeCompiler := result.ModuleForTests("androidx.compose.compiler_compiler-hosted", buildOS+"_common").Rule("combineJar").Output + withCompose := result.ModuleForTests("withcompose", "android_common") + noCompose := result.ModuleForTests("nocompose", "android_common") + + android.AssertStringListContains(t, "missing compose compiler dependency", + withCompose.Rule("kotlinc").Implicits.Strings(), composeCompiler.String()) + + android.AssertStringDoesContain(t, "missing compose compiler plugin", + withCompose.VariablesForTestsRelativeToTop()["kotlincFlags"], "-Xplugin="+composeCompiler.String()) + + android.AssertStringListDoesNotContain(t, "unexpected compose compiler dependency", + noCompose.Rule("kotlinc").Implicits.Strings(), composeCompiler.String()) + + android.AssertStringDoesNotContain(t, "unexpected compose compiler plugin", + noCompose.VariablesForTestsRelativeToTop()["kotlincFlags"], "-Xplugin="+composeCompiler.String()) +} diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go index 8c401a7d7..e3396c12f 100644 --- a/java/legacy_core_platform_api_usage.go +++ b/java/legacy_core_platform_api_usage.go @@ -20,6 +20,8 @@ import ( ) var legacyCorePlatformApiModules = []string{ + "AAECarSystemUI", + "AAECarSystemUI-tests", "ArcSettings", "ahat-test-dump", "android.car", @@ -30,28 +32,33 @@ var legacyCorePlatformApiModules = []string{ "api-stubs-docs", "art_cts_jvmti_test_library", "art-gtest-jars-MyClassNatives", + "BackupEncryption", "BackupFrameworksServicesRoboTests", "backuplib", "BandwidthEnforcementTest", "BlockedNumberProvider", "BluetoothInstrumentationTests", + "BluetoothMidiLib", "BluetoothMidiService", - "CarDeveloperOptions", + "BTTestApp", + "CallEnhancement", + "CapCtrlInterface", "CarService", "CarServiceTest", - "car-apps-common", "car-service-test-lib", "car-service-test-static-lib", "CertInstaller", + "com.qti.location.sdk", "com.qti.media.secureprocessor", "ConnectivityManagerTest", "ContactsProvider", "CorePerfTests", "core-tests-support", + "cronet_impl_common_java", + "cronet_impl_native_java", + "cronet_impl_platform_java", "CtsAppExitTestCases", "CtsContentTestCases", - "CtsIkeTestCases", - "CtsAppExitTestCases", "CtsLibcoreWycheproofBCTestCases", "CtsMediaTestCases", "CtsNetTestCases", @@ -64,8 +71,10 @@ var legacyCorePlatformApiModules = []string{ "DeviceInfo", "DiagnosticTools", "DisplayCutoutEmulationEmu01Overlay", + "DocumentsUIGoogleTests", "DocumentsUIPerfTests", "DocumentsUITests", + "DocumentsUIUnitTests", "DownloadProvider", "DownloadProviderTests", "DownloadProviderUi", @@ -75,10 +84,12 @@ var legacyCorePlatformApiModules = []string{ "ethernet-service", "EthernetServiceTests", "ExternalStorageProvider", - "ExtServices", - "ExtServices-core", - "framework-all", + "face-V1-0-javalib", + "FloralClocks", + "framework-jobscheduler", "framework-minus-apex", + "framework-minus-apex-intdefs", + "FrameworkOverlayG6QU3", "FrameworksCoreTests", "FrameworksIkeTests", "FrameworksNetCommonTests", @@ -87,29 +98,50 @@ var legacyCorePlatformApiModules = []string{ "FrameworksServicesTests", "FrameworksMockingServicesTests", "FrameworksUtilTests", - "FrameworksWifiTests", + "GtsIncrementalInstallTestCases", + "GtsIncrementalInstallTriggerApp", + "GtsInstallerV2TestCases", + "HelloOslo", "hid", "hidl_test_java_java", "hwbinder", - "ims", + "imssettings", + "izat.lib.glue", "KeyChain", - "ksoap2", + "LocalSettingsLib", "LocalTransport", "lockagent", "mediaframeworktest", - "MediaProvider", + "mediatek-ims-base", "MmsService", - "MtpDocumentsProvider", + "ModemTestMode", + "MtkCapCtrl", + "MtpService", "MultiDisplayProvider", + "my.tests.snapdragonsdktest", + "NetworkSetting", "NetworkStackIntegrationTestsLib", "NetworkStackNextIntegrationTests", "NetworkStackNextTests", "NetworkStackTests", "NetworkStackTestsLib", - "NfcNci", + "online-gcm-ref-docs", + "online-gts-docs", + "PerformanceMode", "platform_library-docs", + "PowerStatsService", "PrintSpooler", + "pxp-monitor", + "QColor", + "qcom.fmradio", + "QDCMMobileApp", + "Qmmi", + "QPerformance", + "remotesimlockmanagerlibrary", "RollbackTest", + "sam", + "saminterfacelibrary", + "sammanagerlibrary", "service-blobstore", "service-connectivity-pre-jarjar", "service-jobscheduler", @@ -123,21 +155,50 @@ var legacyCorePlatformApiModules = []string{ "services.usb", "Settings-core", "SettingsGoogle", + "SettingsGoogleOverlayCoral", + "SettingsGoogleOverlayFlame", "SettingsLib", + "SettingsOverlayG020A", + "SettingsOverlayG020B", + "SettingsOverlayG020C", + "SettingsOverlayG020D", + "SettingsOverlayG020E", + "SettingsOverlayG020E_VN", + "SettingsOverlayG020F", + "SettingsOverlayG020F_VN", + "SettingsOverlayG020G", + "SettingsOverlayG020G_VN", + "SettingsOverlayG020H", + "SettingsOverlayG020H_VN", + "SettingsOverlayG020I", + "SettingsOverlayG020I_VN", + "SettingsOverlayG020J", + "SettingsOverlayG020M", + "SettingsOverlayG020N", + "SettingsOverlayG020P", + "SettingsOverlayG020Q", + "SettingsOverlayG025H", + "SettingsOverlayG025J", + "SettingsOverlayG025M", + "SettingsOverlayG025N", + "SettingsOverlayG5NZ6", "SettingsProvider", "SettingsProviderTest", "SettingsRoboTests", "Shell", "ShellTests", + "SimContact", + "SimContacts", + "SimSettings", "sl4a.Common", "StatementService", "SystemUI-core", "SystemUISharedLib", "SystemUI-tests", + "tcmiface", "Telecom", "TelecomUnitTests", "telephony-common", - "TelephonyProvider", "TelephonyProviderTests", "TeleService", "testables", @@ -147,12 +208,16 @@ var legacyCorePlatformApiModules = []string{ "time_zone_distro_installer-tests", "time_zone_distro-tests", "time_zone_updater", + "TMobilePlanProvider", "TvProvider", "uiautomator-stubs-docs", + "uimgbamanagerlibrary", "UsbHostExternalManagementTestApp", "UserDictionaryProvider", + "UxPerformance", "WallpaperBackup", - "wifi-service", + "WallpaperBackupAgentTests", + "WfdCommon", } var legacyCorePlatformApiLookup = make(map[string]struct{}) @@ -163,17 +228,27 @@ func init() { } } -func useLegacyCorePlatformApi(ctx android.EarlyModuleContext) bool { - return useLegacyCorePlatformApiByName(ctx.ModuleName()) +var legacyCorePlatformApiLookupKey = android.NewOnceKey("legacyCorePlatformApiLookup") + +func getLegacyCorePlatformApiLookup(config android.Config) map[string]struct{} { + return config.Once(legacyCorePlatformApiLookupKey, func() interface{} { + return legacyCorePlatformApiLookup + }).(map[string]struct{}) } -func useLegacyCorePlatformApiByName(name string) bool { - _, found := legacyCorePlatformApiLookup[name] +// useLegacyCorePlatformApi checks to see whether the supplied module name is in the list of modules +// that are able to use the legacy core platform API and returns true if it does, false otherwise. +// +// This method takes the module name separately from the context as this may be being called for a +// module that is not the target of the supplied context. +func useLegacyCorePlatformApi(ctx android.EarlyModuleContext, moduleName string) bool { + lookup := getLegacyCorePlatformApiLookup(ctx.Config()) + _, found := lookup[moduleName] return found } func corePlatformSystemModules(ctx android.EarlyModuleContext) string { - if useLegacyCorePlatformApi(ctx) { + if useLegacyCorePlatformApi(ctx, ctx.ModuleName()) { return config.LegacyCorePlatformSystemModules } else { return config.StableCorePlatformSystemModules @@ -181,7 +256,7 @@ func corePlatformSystemModules(ctx android.EarlyModuleContext) string { } func corePlatformBootclasspathLibraries(ctx android.EarlyModuleContext) []string { - if useLegacyCorePlatformApi(ctx) { + if useLegacyCorePlatformApi(ctx, ctx.ModuleName()) { return config.LegacyCorePlatformBootclasspathLibraries } else { return config.StableCorePlatformBootclasspathLibraries diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index 36baf7e78..1e2723845 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -68,7 +68,6 @@ type platformBootclasspathProperties struct { func platformBootclasspathFactory() android.SingletonModule { m := &platformBootclasspathModule{} m.AddProperties(&m.properties) - // TODO(satayev): split apex jars into separate configs. initClasspathFragment(m, BOOTCLASSPATH) android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) return m @@ -424,14 +423,6 @@ func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android. // Generate the framework profile rule bootFrameworkProfileRule(ctx, imageConfig) - // If always using prebuilt sdks then do not generate the updatable-bcp-packages.txt file as it - // will break because the prebuilts do not yet specify a permitted_packages property. - // TODO(b/193889859): Remove when the prebuilts have been updated. - if !ctx.Config().AlwaysUsePrebuiltSdks() { - // Generate the updatable bootclasspath packages rule. - generateUpdatableBcpPackagesRule(ctx, imageConfig, apexModules) - } - // Copy platform module dex jars to their predefined locations. platformBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, platformModules) copyBootJarsToPredefinedLocations(ctx, platformBootDexJarsByModule, imageConfig.dexPathsByModule) diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go index 0d8ebac02..f442ddfd4 100644 --- a/java/platform_compat_config.go +++ b/java/platform_compat_config.go @@ -115,7 +115,7 @@ func (p *platformCompatConfig) AndroidMkEntries() []android.AndroidMkEntries { Include: "$(BUILD_PREBUILT)", ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String()) + entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.String()) entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.configFile.Base()) }, }, diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go index c33e6c229..c67e2bd59 100644 --- a/java/prebuilt_apis.go +++ b/java/prebuilt_apis.go @@ -167,30 +167,24 @@ func prebuiltSdkStubs(mctx android.LoadHookContext, p *prebuiltApis) { localPath := strings.TrimPrefix(f, mydir) module, apiver, scope := parseJarPath(localPath) createImport(mctx, module, scope, apiver, localPath, sdkVersion, compileDex) + + if module == "core-for-system-modules" { + createSystemModules(mctx, apiver, scope) + } } } -func createSystemModules(mctx android.LoadHookContext, apiver string) { +func createSystemModules(mctx android.LoadHookContext, apiver string, scope string) { props := struct { Name *string Libs []string }{} - props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", "public", apiver)) - props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", "public", apiver)) + props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", scope, apiver)) + props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", scope, apiver)) mctx.CreateModule(systemModulesImportFactory, &props) } -func prebuiltSdkSystemModules(mctx android.LoadHookContext, p *prebuiltApis) { - for _, apiver := range p.properties.Api_dirs { - jar := android.ExistentPathForSource(mctx, - mctx.ModuleDir(), apiver, "public", "core-for-system-modules.jar") - if jar.Valid() { - createSystemModules(mctx, apiver) - } - } -} - func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { mydir := mctx.ModuleDir() + "/" // <apiver>/<scope>/api/<module>.txt @@ -273,7 +267,6 @@ func createPrebuiltApiModules(mctx android.LoadHookContext) { if p, ok := mctx.Module().(*prebuiltApis); ok { prebuiltApiFiles(mctx, p) prebuiltSdkStubs(mctx, p) - prebuiltSdkSystemModules(mctx, p) } } diff --git a/java/prebuilt_apis_test.go b/java/prebuilt_apis_test.go new file mode 100644 index 000000000..79f42250b --- /dev/null +++ b/java/prebuilt_apis_test.go @@ -0,0 +1,56 @@ +// Copyright 2021 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package java + +import ( + "sort" + "strings" + "testing" + + "android/soong/android" + "github.com/google/blueprint" +) + +func TestPrebuiltApis_SystemModulesCreation(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + FixtureWithPrebuiltApis(map[string][]string{ + "31": {}, + "32": {}, + "current": {}, + }), + ).RunTest(t) + + sdkSystemModules := []string{} + result.VisitAllModules(func(module blueprint.Module) { + name := android.RemoveOptionalPrebuiltPrefix(module.Name()) + if strings.HasPrefix(name, "sdk_") && strings.HasSuffix(name, "_system_modules") { + sdkSystemModules = append(sdkSystemModules, name) + } + }) + sort.Strings(sdkSystemModules) + expected := []string{ + // 31 only has public system modules. + "sdk_public_31_system_modules", + + // 32 and current both have public and module-lib system modules. + "sdk_public_32_system_modules", + "sdk_module-lib_32_system_modules", + "sdk_public_current_system_modules", + "sdk_module-lib_current_system_modules", + } + sort.Strings(expected) + android.AssertArrayString(t, "sdk system modules", expected, sdkSystemModules) +} diff --git a/java/proto.go b/java/proto.go index 8731822c3..ab913d868 100644 --- a/java/proto.go +++ b/java/proto.go @@ -24,7 +24,7 @@ import ( func genProto(ctx android.ModuleContext, protoFiles android.Paths, flags android.ProtoFlags) android.Paths { // Shard proto files into groups of 100 to avoid having to recompile all of them if one changes and to avoid // hitting command line length limits. - shards := android.ShardPaths(protoFiles, 100) + shards := android.ShardPaths(protoFiles, 50) srcJarFiles := make(android.Paths, 0, len(shards)) @@ -102,6 +102,9 @@ func protoFlags(ctx android.ModuleContext, j *CommonProperties, p *android.Proto if String(p.Proto.Plugin) == "" { var typeToPlugin string switch String(p.Proto.Type) { + case "stream": + flags.proto.OutTypeFlag = "--javastream_out" + typeToPlugin = "javastream" case "micro": flags.proto.OutTypeFlag = "--javamicro_out" typeToPlugin = "javamicro" diff --git a/java/robolectric.go b/java/robolectric.go index a0c9c7fcd..f71952172 100644 --- a/java/robolectric.go +++ b/java/robolectric.go @@ -212,7 +212,7 @@ func (r *robolectricTest) GenerateAndroidBuildActions(ctx android.ModuleContext) installDeps = append(installDeps, installedData) } - ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.combinedJar, installDeps...) + r.installFile = ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.combinedJar, installDeps...) } func generateRoboTestConfig(ctx android.ModuleContext, outputFile android.WritablePath, @@ -276,6 +276,10 @@ func (r *robolectricTest) generateRoboSrcJar(ctx android.ModuleContext, outputFi func (r *robolectricTest) AndroidMkEntries() []android.AndroidMkEntries { entriesList := r.Library.AndroidMkEntries() entries := &entriesList[0] + entries.ExtraEntries = append(entries.ExtraEntries, + func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { + entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true) + }) entries.ExtraFooters = []android.AndroidMkExtraFootersFunc{ func(w io.Writer, name, prefix, moduleDir string) { @@ -348,7 +352,6 @@ func RobolectricTestFactory() android.Module { return module } -func (r *robolectricTest) InstallBypassMake() bool { return true } func (r *robolectricTest) InstallInTestcases() bool { return true } func (r *robolectricTest) InstallForceOS() (*android.OsType, *android.ArchType) { return &r.forceOSType, &r.forceArchType @@ -417,16 +420,15 @@ func (r *robolectricRuntimes) GenerateAndroidBuildActions(ctx android.ModuleCont } runtimeFromSourceJar := android.OutputFileForModule(ctx, runtimeFromSourceModule, "") - // TODO(murj) Update this to ctx.Config().PlatformSdkCodename() once the platform - // classes like android.os.Build are updated to S. - runtimeName := fmt.Sprintf("android-all-%s-robolectric-r0.jar", - "R") + // "TREE" name is essential here because it hooks into the "TREE" name in + // Robolectric's SdkConfig.java that will always correspond to the NEWEST_SDK + // in Robolectric configs. + runtimeName := "android-all-current-robolectric-r0.jar" installedRuntime := ctx.InstallFile(androidAllDir, runtimeName, runtimeFromSourceJar) r.runtimes = append(r.runtimes, installedRuntime) } } -func (r *robolectricRuntimes) InstallBypassMake() bool { return true } func (r *robolectricRuntimes) InstallInTestcases() bool { return true } func (r *robolectricRuntimes) InstallForceOS() (*android.OsType, *android.ArchType) { return &r.forceOSType, &r.forceArchType diff --git a/java/rro_test.go b/java/rro_test.go index 27abbe4f3..be0d7ba47 100644 --- a/java/rro_test.go +++ b/java/rro_test.go @@ -62,6 +62,7 @@ func TestRuntimeResourceOverlay(t *testing.T) { result := android.GroupFixturePreparers( PrepareForTestWithJavaDefaultModules, PrepareForTestWithOverlayBuildComponents, + android.FixtureModifyConfig(android.SetKatiEnabledForTests), fs.AddToFixture(), ).RunTestWithBp(t, bp) @@ -127,7 +128,10 @@ func TestRuntimeResourceOverlay(t *testing.T) { } func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) { - ctx, config := testJava(t, ` + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + android.FixtureModifyConfig(android.SetKatiEnabledForTests), + ).RunTestWithBp(t, ` java_defaults { name: "rro_defaults", theme: "default_theme", @@ -148,7 +152,7 @@ func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) { // // RRO module with defaults // - m := ctx.ModuleForTests("foo_with_defaults", "android_common") + m := result.ModuleForTests("foo_with_defaults", "android_common") // Check AAPT2 link flags. aapt2Flags := strings.Split(m.Output("package-res.apk").Args["flags"], " ") @@ -159,14 +163,14 @@ func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) { } // Check device location. - path := android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"] + path := android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"] expectedPath := []string{shared.JoinPath("out/target/product/test_device/product/overlay/default_theme")} - android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path) + android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path) // // RRO module without defaults // - m = ctx.ModuleForTests("foo_barebones", "android_common") + m = result.ModuleForTests("foo_barebones", "android_common") // Check AAPT2 link flags. aapt2Flags = strings.Split(m.Output("package-res.apk").Args["flags"], " ") @@ -176,9 +180,9 @@ func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) { } // Check device location. - path = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"] + path = android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"] expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay")} - android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path) + android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path) } func TestOverrideRuntimeResourceOverlay(t *testing.T) { diff --git a/java/sdk.go b/java/sdk.go index d1b899e48..756a24deb 100644 --- a/java/sdk.go +++ b/java/sdk.go @@ -55,11 +55,41 @@ func defaultJavaLanguageVersion(ctx android.EarlyModuleContext, s android.SdkSpe return JAVA_VERSION_7 } else if sdk.FinalOrFutureInt() <= 29 { return JAVA_VERSION_8 + } else if ctx.Config().TargetsJava11() { + // Temporary experimental flag to be able to try and build with + // java version 11 options. The flag, if used, just sets Java + // 11 as the default version, leaving any components that + // target an older version intact. + return JAVA_VERSION_11 } else { return JAVA_VERSION_9 } } +// systemModuleKind returns the kind of system modules to use for the supplied combination of sdk +// kind and API level. +func systemModuleKind(sdkKind android.SdkKind, apiLevel android.ApiLevel) android.SdkKind { + systemModuleKind := sdkKind + if apiLevel.LessThanOrEqualTo(android.LastWithoutModuleLibCoreSystemModules) { + // API levels less than or equal to 31 did not provide a core-for-system-modules.jar + // specifically for the module-lib API. So, always use the public system modules for them. + systemModuleKind = android.SdkPublic + } else if systemModuleKind == android.SdkCore { + // Core is by definition what is included in the system module for the public API so should + // just use its system modules. + systemModuleKind = android.SdkPublic + } else if systemModuleKind == android.SdkSystem || systemModuleKind == android.SdkTest { + // The core system and test APIs are currently the same as the public API so they should use + // its system modules. + systemModuleKind = android.SdkPublic + } else if systemModuleKind == android.SdkSystemServer { + // The core system server API is the same as the core module-lib API. + systemModuleKind = android.SdkModule + } + + return systemModuleKind +} + func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) sdkDep { sdkVersion := sdkContext.SdkVersion(ctx) if !sdkVersion.Valid() { @@ -105,7 +135,8 @@ func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) var systemModules string if defaultJavaLanguageVersion(ctx, sdkVersion).usesJavaModules() { - systemModules = "sdk_public_" + sdkVersion.ApiLevel.String() + "_system_modules" + systemModuleKind := systemModuleKind(sdkVersion.Kind, sdkVersion.ApiLevel) + systemModules = fmt.Sprintf("sdk_%s_%s_system_modules", systemModuleKind, sdkVersion.ApiLevel) } return sdkDep{ @@ -116,13 +147,15 @@ func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) } } - toModule := func(modules []string, res string, aidl android.Path) sdkDep { + toModule := func(module string, aidl android.Path) sdkDep { + // Select the kind of system modules needed for the sdk version. + systemModulesKind := systemModuleKind(sdkVersion.Kind, android.FutureApiLevel) return sdkDep{ useModule: true, - bootclasspath: append(modules, config.DefaultLambdaStubsLibrary), - systemModules: "core-current-stubs-system-modules", - java9Classpath: modules, - frameworkResModule: res, + bootclasspath: []string{module, config.DefaultLambdaStubsLibrary}, + systemModules: fmt.Sprintf("core-%s-stubs-system-modules", systemModulesKind), + java9Classpath: []string{module}, + frameworkResModule: "framework-res", aidl: android.OptionalPathForPath(aidl), } } @@ -161,38 +194,24 @@ func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) noFrameworksLibs: true, } case android.SdkPublic: - return toModule([]string{"android_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx)) + return toModule("android_stubs_current", sdkFrameworkAidlPath(ctx)) case android.SdkSystem: - return toModule([]string{"android_system_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx)) + return toModule("android_system_stubs_current", sdkFrameworkAidlPath(ctx)) case android.SdkTest: - return toModule([]string{"android_test_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx)) + return toModule("android_test_stubs_current", sdkFrameworkAidlPath(ctx)) case android.SdkCore: return sdkDep{ useModule: true, bootclasspath: []string{"core.current.stubs", config.DefaultLambdaStubsLibrary}, - systemModules: "core-current-stubs-system-modules", + systemModules: "core-public-stubs-system-modules", noFrameworksLibs: true, } case android.SdkModule: // TODO(146757305): provide .apk and .aidl that have more APIs for modules - return sdkDep{ - useModule: true, - bootclasspath: []string{"android_module_lib_stubs_current", config.DefaultLambdaStubsLibrary}, - systemModules: "core-module-lib-stubs-system-modules", - java9Classpath: []string{"android_module_lib_stubs_current"}, - frameworkResModule: "framework-res", - aidl: android.OptionalPathForPath(nonUpdatableFrameworkAidlPath(ctx)), - } + return toModule("android_module_lib_stubs_current", nonUpdatableFrameworkAidlPath(ctx)) case android.SdkSystemServer: // TODO(146757305): provide .apk and .aidl that have more APIs for modules - return sdkDep{ - useModule: true, - bootclasspath: []string{"android_system_server_stubs_current", config.DefaultLambdaStubsLibrary}, - systemModules: "core-module-lib-stubs-system-modules", - java9Classpath: []string{"android_system_server_stubs_current"}, - frameworkResModule: "framework-res", - aidl: android.OptionalPathForPath(sdkFrameworkAidlPath(ctx)), - } + return toModule("android_system_server_stubs_current", sdkFrameworkAidlPath(ctx)) default: panic(fmt.Errorf("invalid sdk %q", sdkVersion.Raw)) } @@ -370,7 +389,7 @@ func createAPIFingerprint(ctx android.SingletonContext) { "frameworks-base-api-current.txt", "frameworks-base-api-system-current.txt", "frameworks-base-api-module-lib-current.txt", - "services-system-server-current.txt", + "frameworks-base-api-system-server-current.txt", } count := 0 ctx.VisitAllModules(func(module android.Module) { diff --git a/java/sdk_library.go b/java/sdk_library.go index ce8f179af..0bc8895fe 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -21,6 +21,7 @@ import ( "reflect" "regexp" "sort" + "strconv" "strings" "sync" @@ -32,25 +33,7 @@ import ( ) const ( - sdkXmlFileSuffix = ".xml" - permissionsTemplate = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n` + - `<!-- Copyright (C) 2018 The Android Open Source Project\n` + - `\n` + - ` Licensed under the Apache License, Version 2.0 (the \"License\");\n` + - ` you may not use this file except in compliance with the License.\n` + - ` You may obtain a copy of the License at\n` + - `\n` + - ` http://www.apache.org/licenses/LICENSE-2.0\n` + - `\n` + - ` Unless required by applicable law or agreed to in writing, software\n` + - ` distributed under the License is distributed on an \"AS IS\" BASIS,\n` + - ` WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n` + - ` See the License for the specific language governing permissions and\n` + - ` limitations under the License.\n` + - `-->\n` + - `<permissions>\n` + - ` <library name=\"%s\" file=\"%s\"/>\n` + - `</permissions>\n` + sdkXmlFileSuffix = ".xml" ) // A tag to associated a dependency with a specific api scope. @@ -361,13 +344,14 @@ type ApiScopeProperties struct { // The sdk_version to use for building the stubs. // // If not specified then it will use an sdk_version determined as follows: + // // 1) If the sdk_version specified on the java_sdk_library is none then this - // will be none. This is used for java_sdk_library instances that are used - // to create stubs that contribute to the core_current sdk version. - // 2) Otherwise, it is assumed that this library extends but does not contribute - // directly to a specific sdk_version and so this uses the sdk_version appropriate - // for the api scope. e.g. public will use sdk_version: current, system will use - // sdk_version: system_current, etc. + // will be none. This is used for java_sdk_library instances that are used + // to create stubs that contribute to the core_current sdk version. + // 2) Otherwise, it is assumed that this library extends but does not + // contribute directly to a specific sdk_version and so this uses the + // sdk_version appropriate for the api scope. e.g. public will use + // sdk_version: current, system will use sdk_version: system_current, etc. // // This does not affect the sdk_version used for either generating the stubs source // or the API file. They both have to use the same sdk_version as is used for @@ -376,6 +360,9 @@ type ApiScopeProperties struct { } type sdkLibraryProperties struct { + // List of source files that are needed to compile the API, but are not part of runtime library. + Api_srcs []string `android:"arch_variant"` + // Visibility for impl library module. If not specified then defaults to the // visibility property. Impl_library_visibility []string @@ -537,7 +524,7 @@ type scopePaths struct { // The dex jar for the stubs. // // This is not the implementation jar, it still only contains stubs. - stubsDexJarPath android.Path + stubsDexJarPath OptionalDexJarPath // The API specification file, e.g. system_current.txt. currentApiFilePath android.OptionalPath @@ -547,6 +534,9 @@ type scopePaths struct { // The stubs source jar. stubsSrcJar android.OptionalPath + + // Extracted annotations. + annotationsZip android.OptionalPath } func (paths *scopePaths) extractStubsLibraryInfoFromDependency(ctx android.ModuleContext, dep android.Module) error { @@ -582,6 +572,7 @@ func (paths *scopePaths) treatDepAsApiStubsSrcProvider(dep android.Module, actio } func (paths *scopePaths) extractApiInfoFromApiStubsProvider(provider ApiStubsProvider) { + paths.annotationsZip = android.OptionalPathForPath(provider.AnnotationsZip()) paths.currentApiFilePath = android.OptionalPathForPath(provider.ApiFilePath()) paths.removedApiFilePath = android.OptionalPathForPath(provider.RemovedApiFilePath()) } @@ -629,6 +620,33 @@ type commonToSdkLibraryAndImportProperties struct { // Files containing information about supported java doc tags. Doctag_files []string `android:"path"` + + // Signals that this shared library is part of the bootclasspath starting + // on the version indicated in this attribute. + // + // This will make platforms at this level and above to ignore + // <uses-library> tags with this library name because the library is already + // available + On_bootclasspath_since *string + + // Signals that this shared library was part of the bootclasspath before + // (but not including) the version indicated in this attribute. + // + // The system will automatically add a <uses-library> tag with this library to + // apps that target any SDK less than the version indicated in this attribute. + On_bootclasspath_before *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is below the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Min_device_sdk *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is above the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Max_device_sdk *string } // commonSdkLibraryAndImportModule defines the interface that must be provided by a module that @@ -736,6 +754,8 @@ const ( apiTxtComponentName = "api.txt" removedApiTxtComponentName = "removed-api.txt" + + annotationsComponentName = "annotations.zip" ) // A regular expression to match tags that reference a specific stubs component. @@ -754,7 +774,7 @@ var tagSplitter = func() *regexp.Regexp { scopesRegexp := choice(allScopeNames...) // Regular expression to match one of the components. - componentsRegexp := choice(stubsSourceComponentName, apiTxtComponentName, removedApiTxtComponentName) + componentsRegexp := choice(stubsSourceComponentName, apiTxtComponentName, removedApiTxtComponentName, annotationsComponentName) // Regular expression to match any combination of one scope and one component. return regexp.MustCompile(fmt.Sprintf(`^\.(%s)\.(%s)$`, scopesRegexp, componentsRegexp)) @@ -762,9 +782,7 @@ var tagSplitter = func() *regexp.Regexp { // For OutputFileProducer interface // -// .<scope>.stubs.source -// .<scope>.api.txt -// .<scope>.removed-api.txt +// .<scope>.<component name>, for all ComponentNames (for example: .public.removed-api.txt) func (c *commonToSdkLibraryAndImport) commonOutputFiles(tag string) (android.Paths, error) { if groups := tagSplitter.FindStringSubmatch(tag); groups != nil { scopeName := groups[1] @@ -791,6 +809,11 @@ func (c *commonToSdkLibraryAndImport) commonOutputFiles(tag string) (android.Pat if paths.removedApiFilePath.Valid() { return android.Paths{paths.removedApiFilePath.Path()}, nil } + + case annotationsComponentName: + if paths.annotationsZip.Valid() { + return android.Paths{paths.annotationsZip.Path()}, nil + } } return nil, fmt.Errorf("%s not available for api scope %s", component, scopeName) @@ -903,10 +926,10 @@ func sdkKindToApiScope(kind android.SdkKind) *apiScope { } // to satisfy SdkLibraryDependency interface -func (c *commonToSdkLibraryAndImport) SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) android.Path { +func (c *commonToSdkLibraryAndImport) SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath { paths := c.selectScopePaths(ctx, kind) if paths == nil { - return nil + return makeUnsetDexJarPath() } return paths.stubsDexJarPath @@ -1032,7 +1055,7 @@ type SdkLibraryDependency interface { // SdkApiStubDexJar returns the dex jar for the stubs. It is needed by the hiddenapi processing // tool which processes dex files. - SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) android.Path + SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath // SdkRemovedTxtFile returns the optional path to the removed.txt file for the specified sdk kind. SdkRemovedTxtFile(ctx android.BaseModuleContext, kind android.SdkKind) android.OptionalPath @@ -1106,6 +1129,22 @@ func (module *SdkLibrary) getGeneratedApiScopes(ctx android.EarlyModuleContext) return generatedScopes } +var _ android.ModuleWithMinSdkVersionCheck = (*SdkLibrary)(nil) + +func (module *SdkLibrary) CheckMinSdkVersion(ctx android.ModuleContext) { + android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx).ApiLevel, func(c android.ModuleContext, do android.PayloadDepsCallback) { + ctx.WalkDeps(func(child android.Module, parent android.Module) bool { + isExternal := !module.depIsInSameApex(ctx, child) + if am, ok := child.(android.ApexModule); ok { + if !do(ctx, parent, am, isExternal) { + return false + } + } + return !isExternal + }) + }) +} + type sdkLibraryComponentTag struct { blueprint.BaseDependencyTag name string @@ -1183,14 +1222,23 @@ func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { func (module *SdkLibrary) OutputFiles(tag string) (android.Paths, error) { paths, err := module.commonOutputFiles(tag) - if paths == nil && err == nil { - return module.Library.OutputFiles(tag) - } else { + if paths != nil || err != nil { return paths, err } + if module.requiresRuntimeImplementationLibrary() { + return module.Library.OutputFiles(tag) + } + if tag == "" { + return nil, nil + } + return nil, fmt.Errorf("unsupported module reference tag %q", tag) } func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { + if proptools.String(module.deviceProperties.Min_sdk_version) != "" { + module.CheckMinSdkVersion(ctx) + } + module.generateCommonBuildActions(ctx) // Only build an implementation library if required. @@ -1344,6 +1392,10 @@ func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext Srcs []string Javacflags []string } + Openjdk11 struct { + Srcs []string + Javacflags []string + } Dist struct { Targets []string Dest *string @@ -1370,6 +1422,8 @@ func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext } props.Openjdk9.Srcs = module.properties.Openjdk9.Srcs props.Openjdk9.Javacflags = module.properties.Openjdk9.Javacflags + props.Openjdk11.Srcs = module.properties.Openjdk11.Srcs + props.Openjdk11.Javacflags = module.properties.Openjdk11.Javacflags // We compile the stubs for 1.8 in line with the main android.jar stubs, and potential // interop with older developer tools that don't support 1.9. props.Java_version = proptools.StringPtr("1.8") @@ -1438,6 +1492,7 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC props.Name = proptools.StringPtr(name) props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_source_visibility) props.Srcs = append(props.Srcs, module.properties.Srcs...) + props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...) props.Sdk_version = module.deviceProperties.Sdk_version props.System_modules = module.deviceProperties.System_modules props.Installable = proptools.BoolPtr(false) @@ -1566,14 +1621,29 @@ func (module *SdkLibrary) UniqueApexVariations() bool { // Creates the xml file that publicizes the runtime library func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) { + moduleMinApiLevel := module.Library.MinSdkVersion(mctx).ApiLevel + var moduleMinApiLevelStr = moduleMinApiLevel.String() + if moduleMinApiLevel == android.NoneApiLevel { + moduleMinApiLevelStr = "current" + } props := struct { - Name *string - Lib_name *string - Apex_available []string + Name *string + Lib_name *string + Apex_available []string + On_bootclasspath_since *string + On_bootclasspath_before *string + Min_device_sdk *string + Max_device_sdk *string + Sdk_library_min_api_level *string }{ - Name: proptools.StringPtr(module.xmlPermissionsModuleName()), - Lib_name: proptools.StringPtr(module.BaseModuleName()), - Apex_available: module.ApexProperties.Apex_available, + Name: proptools.StringPtr(module.xmlPermissionsModuleName()), + Lib_name: proptools.StringPtr(module.BaseModuleName()), + Apex_available: module.ApexProperties.Apex_available, + On_bootclasspath_since: module.commonSdkLibraryProperties.On_bootclasspath_since, + On_bootclasspath_before: module.commonSdkLibraryProperties.On_bootclasspath_before, + Min_device_sdk: module.commonSdkLibraryProperties.Min_device_sdk, + Max_device_sdk: module.commonSdkLibraryProperties.Max_device_sdk, + Sdk_library_min_api_level: &moduleMinApiLevelStr, } mctx.CreateModule(sdkLibraryXmlFactory, &props) @@ -1884,6 +1954,9 @@ type sdkLibraryScopeProperties struct { // The removed.txt Removed_api *string `android:"path"` + + // Annotation zip + Annotations *string `android:"path"` } type sdkLibraryImportProperties struct { @@ -1895,7 +1968,6 @@ type sdkLibraryImportProperties struct { Compile_dex *bool // If not empty, classes are restricted to the specified packages and their sub-packages. - // This information is used to generate the updatable-bcp-packages.txt file. Permitted_packages []string } @@ -1907,6 +1979,7 @@ type SdkLibraryImport struct { android.SdkBase hiddenAPI + dexpreopter properties sdkLibraryImportProperties @@ -1924,7 +1997,7 @@ type SdkLibraryImport struct { xmlPermissionsFileModule *sdkLibraryXml // Build path to the dex implementation jar obtained from the prebuilt_apex, if any. - dexJarFile android.Path + dexJarFile OptionalDexJarPath // Expected install file path of the source module(sdk_library) // or dex implementation jar obtained from the prebuilt_apex, if any. @@ -2111,6 +2184,14 @@ func (module *SdkLibraryImport) DepsMutator(ctx android.BottomUpMutatorContext) } } +func (module *SdkLibraryImport) AndroidMkEntries() []android.AndroidMkEntries { + // For an SDK library imported from a prebuilt APEX, we don't need a Make module for itself, as we + // don't need to install it. However, we need to add its dexpreopt outputs as sub-modules, if it + // is preopted. + dexpreoptEntries := module.dexpreopter.AndroidMkEntriesForApex() + return append(dexpreoptEntries, android.AndroidMkEntries{Disabled: true}) +} + var _ android.ApexModule = (*SdkLibraryImport)(nil) // Implements android.ApexModule @@ -2144,8 +2225,6 @@ func (module *SdkLibraryImport) OutputFiles(tag string) (android.Paths, error) { func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { module.generateCommonBuildActions(ctx) - var deapexerModule android.Module - // Assume that source module(sdk_library) is installed in /<sdk_library partition>/framework module.installFile = android.PathForModuleInstall(ctx, "framework", module.Stem()+".jar") @@ -2174,11 +2253,6 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo ctx.ModuleErrorf("xml permissions file module must be of type *sdkLibraryXml but was %T", to) } } - - // Save away the `deapexer` module on which this depends, if any. - if tag == android.DeapexerTag { - deapexerModule = to - } }) // Populate the scope paths with information from the properties. @@ -2188,6 +2262,7 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo } paths := module.getScopePathsCreateIfNeeded(apiScope) + paths.annotationsZip = android.OptionalPathForModuleSrc(ctx, scopeProperties.Annotations) paths.currentApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Current_api) paths.removedApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Removed_api) } @@ -2197,23 +2272,28 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo // obtained from the associated deapexer module. ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) if ai.ForPrebuiltApex { - if deapexerModule == nil { - // This should never happen as a variant for a prebuilt_apex is only created if the - // deapxer module has been configured to export the dex implementation jar for this module. - ctx.ModuleErrorf("internal error: module %q does not depend on a `deapexer` module for prebuilt_apex %q", - module.Name(), ai.ApexVariationName) - } - // Get the path of the dex implementation jar from the `deapexer` module. - di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo) + di := android.FindDeapexerProviderForModule(ctx) + if di == nil { + return // An error has been reported by FindDeapexerProviderForModule. + } if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(module.BaseModuleName())); dexOutputPath != nil { - module.dexJarFile = dexOutputPath - module.installFile = android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(module.BaseModuleName())) - module.initHiddenAPI(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil) + dexJarFile := makeDexJarPathFromPath(dexOutputPath) + module.dexJarFile = dexJarFile + installPath := android.PathForModuleInPartitionInstall( + ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(module.BaseModuleName())) + module.installFile = installPath + module.initHiddenAPI(ctx, dexJarFile, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil) + + // Dexpreopting. + module.dexpreopter.installPath = module.dexpreopter.getInstallPath(ctx, installPath) + module.dexpreopter.isSDKLibrary = true + module.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &module.dexpreopter) + module.dexpreopt(ctx, dexOutputPath) } else { // This should never happen as a variant for a prebuilt_apex is only created if the // prebuilt_apex has been configured to export the java library dex file. - ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt_apex %q", deapexerModule.Name()) + ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt APEX %s", di.ApexModuleName()) } } } @@ -2248,14 +2328,14 @@ func (module *SdkLibraryImport) SdkImplementationJars(ctx android.BaseModuleCont } // to satisfy UsesLibraryDependency interface -func (module *SdkLibraryImport) DexJarBuildPath() android.Path { +func (module *SdkLibraryImport) DexJarBuildPath() OptionalDexJarPath { // The dex implementation jar extracted from the .apex file should be used in preference to the // source. - if module.dexJarFile != nil { + if module.dexJarFile.IsSet() { return module.dexJarFile } if module.implLibraryModule == nil { - return nil + return makeUnsetDexJarPath() } else { return module.implLibraryModule.DexJarBuildPath() } @@ -2328,6 +2408,11 @@ func (module *SdkLibraryImport) ImplementationAndResourcesJars() android.Paths { } } +// to satisfy java.DexpreopterInterface interface +func (module *SdkLibraryImport) IsInstallable() bool { + return true +} + var _ android.RequiredFilesFromPrebuiltApex = (*SdkLibraryImport)(nil) func (module *SdkLibraryImport) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string { @@ -2354,6 +2439,38 @@ type sdkLibraryXml struct { type sdkLibraryXmlProperties struct { // canonical name of the lib Lib_name *string + + // Signals that this shared library is part of the bootclasspath starting + // on the version indicated in this attribute. + // + // This will make platforms at this level and above to ignore + // <uses-library> tags with this library name because the library is already + // available + On_bootclasspath_since *string + + // Signals that this shared library was part of the bootclasspath before + // (but not including) the version indicated in this attribute. + // + // The system will automatically add a <uses-library> tag with this library to + // apps that target any SDK less than the version indicated in this attribute. + On_bootclasspath_before *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is below the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Min_device_sdk *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is above the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Max_device_sdk *string + + // The SdkLibrary's min api level as a string + // + // This value comes from the ApiLevel of the MinSdkVersion property. + Sdk_library_min_api_level *string } // java_sdk_library_xml builds the permission xml file for a java_sdk_library. @@ -2430,11 +2547,81 @@ func (module *sdkLibraryXml) implPath(ctx android.ModuleContext) string { return "/" + partition + "/framework/" + implName + ".jar" } +func formattedOptionalSdkLevelAttribute(ctx android.ModuleContext, attrName string, value *string) string { + if value == nil { + return "" + } + apiLevel, err := android.ApiLevelFromUser(ctx, *value) + if err != nil { + // attributes in bp files have underscores but in the xml have dashes. + ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), err.Error()) + return "" + } + intStr := strconv.Itoa(apiLevel.FinalOrPreviewInt()) + return formattedOptionalAttribute(attrName, &intStr) +} + +// formats an attribute for the xml permissions file if the value is not null +// returns empty string otherwise +func formattedOptionalAttribute(attrName string, value *string) string { + if value == nil { + return "" + } + return fmt.Sprintf(` %s=\"%s\"\n`, attrName, *value) +} + +func (module *sdkLibraryXml) permissionsContents(ctx android.ModuleContext) string { + libName := proptools.String(module.properties.Lib_name) + libNameAttr := formattedOptionalAttribute("name", &libName) + filePath := module.implPath(ctx) + filePathAttr := formattedOptionalAttribute("file", &filePath) + implicitFromAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-since", module.properties.On_bootclasspath_since) + implicitUntilAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-before", module.properties.On_bootclasspath_before) + minSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "min-device-sdk", module.properties.Min_device_sdk) + maxSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "max-device-sdk", module.properties.Max_device_sdk) + // <library> is understood in all android versions whereas <updatable-library> is only understood from API T (and ignored before that). + // similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the updatable-library to make sure this library is not loaded before T + var libraryTag string + if module.properties.Min_device_sdk != nil { + libraryTag = ` <updatable-library\n` + } else { + libraryTag = ` <library\n` + } + + return strings.Join([]string{ + `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n`, + `<!-- Copyright (C) 2018 The Android Open Source Project\n`, + `\n`, + ` Licensed under the Apache License, Version 2.0 (the \"License\");\n`, + ` you may not use this file except in compliance with the License.\n`, + ` You may obtain a copy of the License at\n`, + `\n`, + ` http://www.apache.org/licenses/LICENSE-2.0\n`, + `\n`, + ` Unless required by applicable law or agreed to in writing, software\n`, + ` distributed under the License is distributed on an \"AS IS\" BASIS,\n`, + ` WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n`, + ` See the License for the specific language governing permissions and\n`, + ` limitations under the License.\n`, + `-->\n`, + `<permissions>\n`, + libraryTag, + libNameAttr, + filePathAttr, + implicitFromAttr, + implicitUntilAttr, + minSdkAttr, + maxSdkAttr, + ` />\n`, + `</permissions>\n`}, "") +} + func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleContext) { module.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform() libName := proptools.String(module.properties.Lib_name) - xmlContent := fmt.Sprintf(permissionsTemplate, libName, module.implPath(ctx)) + module.selfValidate(ctx) + xmlContent := module.permissionsContents(ctx) module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath rule := android.NewRuleBuilder(pctx, ctx) @@ -2449,24 +2636,99 @@ func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleConte func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries { if module.hideApexVariantFromMake { - return []android.AndroidMkEntries{android.AndroidMkEntries{ + return []android.AndroidMkEntries{{ Disabled: true, }} } - return []android.AndroidMkEntries{android.AndroidMkEntries{ + return []android.AndroidMkEntries{{ Class: "ETC", OutputFile: android.OptionalPathForPath(module.outputFilePath), ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetString("LOCAL_MODULE_TAGS", "optional") - entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.ToMakePath().String()) + entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.String()) entries.SetString("LOCAL_INSTALLED_MODULE_STEM", module.outputFilePath.Base()) }, }, }} } +func (module *sdkLibraryXml) selfValidate(ctx android.ModuleContext) { + module.validateAtLeastTAttributes(ctx) + module.validateMinAndMaxDeviceSdk(ctx) + module.validateMinMaxDeviceSdkAndModuleMinSdk(ctx) + module.validateOnBootclasspathBeforeRequirements(ctx) +} + +func (module *sdkLibraryXml) validateAtLeastTAttributes(ctx android.ModuleContext) { + t := android.ApiLevelOrPanic(ctx, "Tiramisu") + module.attrAtLeastT(ctx, t, module.properties.Min_device_sdk, "min_device_sdk") + module.attrAtLeastT(ctx, t, module.properties.Max_device_sdk, "max_device_sdk") + module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_before, "on_bootclasspath_before") + module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_since, "on_bootclasspath_since") +} + +func (module *sdkLibraryXml) attrAtLeastT(ctx android.ModuleContext, t android.ApiLevel, attr *string, attrName string) { + if attr != nil { + if level, err := android.ApiLevelFromUser(ctx, *attr); err == nil { + // we will inform the user of invalid inputs when we try to write the + // permissions xml file so we don't need to do it here + if t.GreaterThan(level) { + ctx.PropertyErrorf(attrName, "Attribute value needs to be at least T") + } + } + } +} + +func (module *sdkLibraryXml) validateMinAndMaxDeviceSdk(ctx android.ModuleContext) { + if module.properties.Min_device_sdk != nil && module.properties.Max_device_sdk != nil { + min, minErr := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk) + max, maxErr := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk) + if minErr == nil && maxErr == nil { + // we will inform the user of invalid inputs when we try to write the + // permissions xml file so we don't need to do it here + if min.GreaterThan(max) { + ctx.ModuleErrorf("min_device_sdk can't be greater than max_device_sdk") + } + } + } +} + +func (module *sdkLibraryXml) validateMinMaxDeviceSdkAndModuleMinSdk(ctx android.ModuleContext) { + moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level) + if module.properties.Min_device_sdk != nil { + api, err := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk) + if err == nil { + if moduleMinApi.GreaterThan(api) { + ctx.PropertyErrorf("min_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi) + } + } + } + if module.properties.Max_device_sdk != nil { + api, err := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk) + if err == nil { + if moduleMinApi.GreaterThan(api) { + ctx.PropertyErrorf("max_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi) + } + } + } +} + +func (module *sdkLibraryXml) validateOnBootclasspathBeforeRequirements(ctx android.ModuleContext) { + moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level) + if module.properties.On_bootclasspath_before != nil { + t := android.ApiLevelOrPanic(ctx, "Tiramisu") + // if we use the attribute, then we need to do this validation + if moduleMinApi.LessThan(t) { + // if minAPi is < T, then we need to have min_device_sdk (which only accepts T+) + if module.properties.Min_device_sdk == nil { + ctx.PropertyErrorf("on_bootclasspath_before", "Using this property requires that the module's min_sdk_version or the shared library's min_device_sdk is at least T") + } + } + } +} + type sdkLibrarySdkMemberType struct { android.SdkMemberTypeBase } @@ -2518,6 +2780,33 @@ type sdkLibrarySdkMemberProperties struct { Doctag_paths android.Paths Permitted_packages []string + + // Signals that this shared library is part of the bootclasspath starting + // on the version indicated in this attribute. + // + // This will make platforms at this level and above to ignore + // <uses-library> tags with this library name because the library is already + // available + On_bootclasspath_since *string + + // Signals that this shared library was part of the bootclasspath before + // (but not including) the version indicated in this attribute. + // + // The system will automatically add a <uses-library> tag with this library to + // apps that target any SDK less than the version indicated in this attribute. + On_bootclasspath_before *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is below the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Min_device_sdk *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is above the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Max_device_sdk *string } type scopeProperties struct { @@ -2525,6 +2814,7 @@ type scopeProperties struct { StubsSrcJar android.Path CurrentApiFile android.Path RemovedApiFile android.Path + AnnotationsZip android.Path SdkVersion string } @@ -2550,6 +2840,10 @@ func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMembe if paths.removedApiFilePath.Valid() { properties.RemovedApiFile = paths.removedApiFilePath.Path() } + // The annotations zip is only available for modules that set annotations_enabled: true. + if paths.annotationsZip.Valid() { + properties.AnnotationsZip = paths.annotationsZip.Path() + } s.Scopes[apiScope] = properties } } @@ -2559,6 +2853,10 @@ func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMembe s.Compile_dex = sdk.dexProperties.Compile_dex s.Doctag_paths = sdk.doctagPaths s.Permitted_packages = sdk.PermittedPackagesForUpdatableBootJars() + s.On_bootclasspath_since = sdk.commonSdkLibraryProperties.On_bootclasspath_since + s.On_bootclasspath_before = sdk.commonSdkLibraryProperties.On_bootclasspath_before + s.Min_device_sdk = sdk.commonSdkLibraryProperties.Min_device_sdk + s.Max_device_sdk = sdk.commonSdkLibraryProperties.Max_device_sdk } func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) { @@ -2614,6 +2912,12 @@ func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberCo scopeSet.AddProperty("removed_api", removedApiSnapshotPath) } + if properties.AnnotationsZip != nil { + annotationsSnapshotPath := filepath.Join(scopeDir, ctx.Name()+"_annotations.zip") + ctx.SnapshotBuilder().CopyToSnapshot(properties.AnnotationsZip, annotationsSnapshotPath) + scopeSet.AddProperty("annotations", annotationsSnapshotPath) + } + if properties.SdkVersion != "" { scopeSet.AddProperty("sdk_version", properties.SdkVersion) } diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go index 938bb2895..f3a19e956 100644 --- a/java/sdk_library_test.go +++ b/java/sdk_library_test.go @@ -15,12 +15,13 @@ package java import ( - "android/soong/android" "fmt" "path/filepath" "regexp" "testing" + "android/soong/android" + "github.com/google/blueprint/proptools" ) @@ -107,7 +108,7 @@ func TestJavaSdkLibrary(t *testing.T) { libs: ["foo"], sdk_version: "module_30", } - `) + `) // check the existence of the internal modules foo := result.ModuleForTests("foo", "android_common") @@ -162,6 +163,185 @@ func TestJavaSdkLibrary(t *testing.T) { } } +func TestJavaSdkLibrary_UpdatableLibrary(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "28": {"foo"}, + "29": {"foo"}, + "30": {"foo", "fooUpdatable", "fooUpdatableErr"}, + }), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V", "W"} + }), + ).RunTestWithBp(t, + ` + java_sdk_library { + name: "fooUpdatable", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + on_bootclasspath_since: "U", + on_bootclasspath_before: "V", + min_device_sdk: "W", + max_device_sdk: "current", + min_sdk_version: "S", + } + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + } +`) + // test that updatability attributes are passed on correctly + fooUpdatable := result.ModuleForTests("fooUpdatable.xml", "android_common").Rule("java_sdk_xml") + android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-since=\"9001\"`) + android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-before=\"9002\"`) + android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `min-device-sdk=\"9003\"`) + android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `max-device-sdk=\"10000\"`) + + // double check that updatability attributes are not written if they don't exist in the bp file + // the permissions file for the foo library defined above + fooPermissions := result.ModuleForTests("foo.xml", "android_common").Rule("java_sdk_xml") + android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `on-bootclasspath-since`) + android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `on-bootclasspath-before`) + android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `min-device-sdk`) + android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `max-device-sdk`) +} + +func TestJavaSdkLibrary_UpdatableLibrary_Validation_ValidVersion(t *testing.T) { + android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "30": {"fooUpdatable", "fooUpdatableErr"}, + }), + ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern( + []string{ + `on_bootclasspath_since: "aaa" could not be parsed as an integer and is not a recognized codename`, + `on_bootclasspath_before: "bbc" could not be parsed as an integer and is not a recognized codename`, + `min_device_sdk: "ccc" could not be parsed as an integer and is not a recognized codename`, + `max_device_sdk: "ddd" could not be parsed as an integer and is not a recognized codename`, + })).RunTestWithBp(t, + ` + java_sdk_library { + name: "fooUpdatableErr", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + on_bootclasspath_since: "aaa", + on_bootclasspath_before: "bbc", + min_device_sdk: "ccc", + max_device_sdk: "ddd", + } +`) +} + +func TestJavaSdkLibrary_UpdatableLibrary_Validation_AtLeastTAttributes(t *testing.T) { + android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "28": {"foo"}, + }), + ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern( + []string{ + "on_bootclasspath_since: Attribute value needs to be at least T", + "on_bootclasspath_before: Attribute value needs to be at least T", + "min_device_sdk: Attribute value needs to be at least T", + "max_device_sdk: Attribute value needs to be at least T", + }, + )).RunTestWithBp(t, + ` + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + on_bootclasspath_since: "S", + on_bootclasspath_before: "S", + min_device_sdk: "S", + max_device_sdk: "S", + min_sdk_version: "S", + } +`) +} + +func TestJavaSdkLibrary_UpdatableLibrary_Validation_MinAndMaxDeviceSdk(t *testing.T) { + android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "28": {"foo"}, + }), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V"} + }), + ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern( + []string{ + "min_device_sdk can't be greater than max_device_sdk", + }, + )).RunTestWithBp(t, + ` + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + min_device_sdk: "V", + max_device_sdk: "U", + min_sdk_version: "S", + } +`) +} + +func TestJavaSdkLibrary_UpdatableLibrary_Validation_MinAndMaxDeviceSdkAndModuleMinSdk(t *testing.T) { + android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "28": {"foo"}, + }), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V"} + }), + ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern( + []string{ + regexp.QuoteMeta("min_device_sdk: Can't be less than module's min sdk (V)"), + regexp.QuoteMeta("max_device_sdk: Can't be less than module's min sdk (V)"), + }, + )).RunTestWithBp(t, + ` + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + min_device_sdk: "U", + max_device_sdk: "U", + min_sdk_version: "V", + } +`) +} + +func TestJavaSdkLibrary_UpdatableLibrary_usesNewTag(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "30": {"foo"}, + }), + ).RunTestWithBp(t, + ` + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + min_device_sdk: "Tiramisu", + min_sdk_version: "S", + } +`) + // test that updatability attributes are passed on correctly + fooUpdatable := result.ModuleForTests("foo.xml", "android_common").Rule("java_sdk_xml") + android.AssertStringDoesContain(t, "foo.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `<updatable-library`) + android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `<library`) +} + func TestJavaSdkLibrary_StubOrImplOnlyLibs(t *testing.T) { result := android.GroupFixturePreparers( prepareForJavaTest, @@ -248,7 +428,7 @@ func TestJavaSdkLibrary_DoNotAccessImplWhenItIsNotBuilt(t *testing.T) { } } -func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) { +func TestJavaSdkLibrary_AccessOutputFiles(t *testing.T) { android.GroupFixturePreparers( prepareForJavaTest, PrepareForTestWithJavaSdkLibraryFiles, @@ -258,6 +438,31 @@ func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) { name: "foo", srcs: ["a.java"], api_packages: ["foo"], + annotations_enabled: true, + public: { + enabled: true, + }, + } + java_library { + name: "bar", + srcs: ["b.java", ":foo{.public.stubs.source}"], + java_resources: [":foo{.public.annotations.zip}"], + } + `) +} + +func TestJavaSdkLibrary_AccessOutputFiles_NoAnnotations(t *testing.T) { + android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis("foo"), + ). + ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": path dependency ":foo{.public.annotations.zip}": annotations.zip not available for api scope public`)). + RunTestWithBp(t, ` + java_sdk_library { + name: "foo", + srcs: ["a.java"], + api_packages: ["foo"], public: { enabled: true, }, @@ -266,6 +471,7 @@ func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) { java_library { name: "bar", srcs: ["b.java", ":foo{.public.stubs.source}"], + java_resources: [":foo{.public.annotations.zip}"], } `) } @@ -329,6 +535,7 @@ func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) { stub_srcs: ["a.java"], current_api: "api/current.txt", removed_api: "api/removed.txt", + annotations: "x/annotations.zip", }, } @@ -338,6 +545,7 @@ func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) { java_resources: [ ":foo{.public.api.txt}", ":foo{.public.removed-api.txt}", + ":foo{.public.annotations.zip}", ], } `) @@ -598,6 +806,7 @@ func TestJavaSdkLibraryImport(t *testing.T) { } CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{ + `dex2oatd`, `prebuilt_sdklib.stubs`, `prebuilt_sdklib.stubs.source.test`, `prebuilt_sdklib.stubs.system`, @@ -674,7 +883,6 @@ func TestJavaSdkLibraryImport_Preferred(t *testing.T) { `) CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{ - `dex2oatd`, `prebuilt_sdklib`, `sdklib.impl`, `sdklib.stubs`, @@ -683,6 +891,7 @@ func TestJavaSdkLibraryImport_Preferred(t *testing.T) { }) CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{ + `dex2oatd`, `prebuilt_sdklib.stubs`, `sdklib.impl`, `sdklib.xml`, @@ -931,3 +1140,87 @@ func TestJavaSdkLibraryDist(t *testing.T) { }) } } + +func TestSdkLibrary_CheckMinSdkVersion(t *testing.T) { + preparer := android.GroupFixturePreparers( + PrepareForTestWithJavaBuildComponents, + PrepareForTestWithJavaDefaultModules, + PrepareForTestWithJavaSdkLibraryFiles, + ) + + preparer.RunTestWithBp(t, ` + java_sdk_library { + name: "sdklib", + srcs: ["a.java"], + static_libs: ["util"], + min_sdk_version: "30", + unsafe_ignore_missing_latest_api: true, + } + + java_library { + name: "util", + srcs: ["a.java"], + min_sdk_version: "30", + } + `) + + preparer. + RunTestWithBp(t, ` + java_sdk_library { + name: "sdklib", + srcs: ["a.java"], + libs: ["util"], + impl_only_libs: ["util"], + stub_only_libs: ["util"], + stub_only_static_libs: ["util"], + min_sdk_version: "30", + unsafe_ignore_missing_latest_api: true, + } + + java_library { + name: "util", + srcs: ["a.java"], + } + `) + + preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "util".*should support min_sdk_version\(30\)`)). + RunTestWithBp(t, ` + java_sdk_library { + name: "sdklib", + srcs: ["a.java"], + static_libs: ["util"], + min_sdk_version: "30", + unsafe_ignore_missing_latest_api: true, + } + + java_library { + name: "util", + srcs: ["a.java"], + min_sdk_version: "31", + } + `) + + preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "another_util".*should support min_sdk_version\(30\)`)). + RunTestWithBp(t, ` + java_sdk_library { + name: "sdklib", + srcs: ["a.java"], + static_libs: ["util"], + min_sdk_version: "30", + unsafe_ignore_missing_latest_api: true, + } + + java_library { + name: "util", + srcs: ["a.java"], + static_libs: ["another_util"], + min_sdk_version: "30", + } + + java_library { + name: "another_util", + srcs: ["a.java"], + min_sdk_version: "31", + } + `) +} diff --git a/java/sdk_test.go b/java/sdk_test.go index 6d6213011..9e8ba6ed0 100644 --- a/java/sdk_test.go +++ b/java/sdk_test.go @@ -25,27 +25,36 @@ import ( "android/soong/java/config" ) +type classpathTestCase struct { + name string + unbundled bool + moduleType string + host android.OsClass + properties string + + // for java 8 + bootclasspath []string + java8classpath []string + + // for java 9 + system string + java9classpath []string + + forces8 bool // if set, javac will always be called with java 8 arguments + + aidl string + + // Indicates how this test case is affected by the setting of Always_use_prebuilt_sdks. + // + // If this is nil then the test case is unaffected by the setting of Always_use_prebuilt_sdks. + // Otherwise, the test case can only be used when + // Always_use_prebuilt_sdks=*forAlwaysUsePrebuiltSdks. + forAlwaysUsePrebuiltSdks *bool +} + func TestClasspath(t *testing.T) { const frameworkAidl = "-I" + defaultJavaDir + "/framework/aidl" - var classpathTestcases = []struct { - name string - unbundled bool - moduleType string - host android.OsClass - properties string - - // for java 8 - bootclasspath []string - java8classpath []string - - // for java 9 - system string - java9classpath []string - - forces8 bool // if set, javac will always be called with java 8 arguments - - aidl string - }{ + var classpathTestcases = []classpathTestCase{ { name: "default", bootclasspath: config.StableCorePlatformBootclasspathLibraries, @@ -91,25 +100,52 @@ func TestClasspath(t *testing.T) { aidl: "-pprebuilts/sdk/30/public/framework.aidl", }, { + // Test case only applies when Always_use_prebuilt_sdks=false (the default). + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false), name: "current", properties: `sdk_version: "current",`, bootclasspath: []string{"android_stubs_current", "core-lambda-stubs"}, - system: "core-current-stubs-system-modules", + system: "core-public-stubs-system-modules", java9classpath: []string{"android_stubs_current"}, aidl: "-pout/soong/framework.aidl", }, { + // Test case only applies when Always_use_prebuilt_sdks=true. + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true), + + name: "current", + properties: `sdk_version: "current",`, + bootclasspath: []string{`""`}, + system: "sdk_public_current_system_modules", + java8classpath: []string{"prebuilts/sdk/current/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/current/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/current/public/framework.aidl", + }, + { + // Test case only applies when Always_use_prebuilt_sdks=false (the default). + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false), name: "system_current", properties: `sdk_version: "system_current",`, bootclasspath: []string{"android_system_stubs_current", "core-lambda-stubs"}, - system: "core-current-stubs-system-modules", + system: "core-public-stubs-system-modules", java9classpath: []string{"android_system_stubs_current"}, aidl: "-pout/soong/framework.aidl", }, { + // Test case only applies when Always_use_prebuilt_sdks=true. + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true), + name: "system_current", + properties: `sdk_version: "system_current",`, + bootclasspath: []string{`""`}, + system: "sdk_public_current_system_modules", + java8classpath: []string{"prebuilts/sdk/current/system/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/current/system/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/current/public/framework.aidl", + }, + { name: "system_29", properties: `sdk_version: "system_29",`, bootclasspath: []string{`""`}, @@ -118,7 +154,6 @@ func TestClasspath(t *testing.T) { aidl: "-pprebuilts/sdk/29/public/framework.aidl", }, { - name: "system_30", properties: `sdk_version: "system_30",`, bootclasspath: []string{`""`}, @@ -128,20 +163,57 @@ func TestClasspath(t *testing.T) { aidl: "-pprebuilts/sdk/30/public/framework.aidl", }, { + // Test case only applies when Always_use_prebuilt_sdks=false (the default). + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false), name: "test_current", properties: `sdk_version: "test_current",`, bootclasspath: []string{"android_test_stubs_current", "core-lambda-stubs"}, - system: "core-current-stubs-system-modules", + system: "core-public-stubs-system-modules", java9classpath: []string{"android_test_stubs_current"}, aidl: "-pout/soong/framework.aidl", }, { + // Test case only applies when Always_use_prebuilt_sdks=true. + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true), + + name: "test_current", + properties: `sdk_version: "test_current",`, + bootclasspath: []string{`""`}, + system: "sdk_public_current_system_modules", + java8classpath: []string{"prebuilts/sdk/current/test/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/current/test/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/current/public/framework.aidl", + }, + { + name: "test_30", + properties: `sdk_version: "test_30",`, + bootclasspath: []string{`""`}, + system: "sdk_public_30_system_modules", + java8classpath: []string{"prebuilts/sdk/30/test/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/30/test/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/30/public/framework.aidl", + }, + { + // Test case only applies when Always_use_prebuilt_sdks=false (the default). + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false), name: "core_current", properties: `sdk_version: "core_current",`, bootclasspath: []string{"core.current.stubs", "core-lambda-stubs"}, - system: "core-current-stubs-system-modules", + system: "core-public-stubs-system-modules", + }, + { + // Test case only applies when Always_use_prebuilt_sdks=true. + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true), + + name: "core_current", + properties: `sdk_version: "core_current",`, + bootclasspath: []string{`""`}, + system: "sdk_public_current_system_modules", + java8classpath: []string{"prebuilts/sdk/current/core/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/current/core/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/current/public/framework.aidl", }, { @@ -214,8 +286,10 @@ func TestClasspath(t *testing.T) { java9classpath: []string{"prebuilts/sdk/current/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, aidl: "-pprebuilts/sdk/current/public/framework.aidl", }, - { + // Test case only applies when Always_use_prebuilt_sdks=false (the default). + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false), + name: "module_current", properties: `sdk_version: "module_current",`, bootclasspath: []string{"android_module_lib_stubs_current", "core-lambda-stubs"}, @@ -224,6 +298,48 @@ func TestClasspath(t *testing.T) { aidl: "-pout/soong/framework_non_updatable.aidl", }, { + // Test case only applies when Always_use_prebuilt_sdks=true. + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true), + + name: "module_current", + properties: `sdk_version: "module_current",`, + bootclasspath: []string{`""`}, + system: "sdk_module-lib_current_system_modules", + java8classpath: []string{"prebuilts/sdk/current/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/current/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/current/public/framework.aidl", + }, + { + name: "module_30", + properties: `sdk_version: "module_30",`, + bootclasspath: []string{`""`}, + system: "sdk_public_30_system_modules", + java8classpath: []string{"prebuilts/sdk/30/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/30/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/30/public/framework.aidl", + }, + { + name: "module_31", + properties: `sdk_version: "module_31",`, + bootclasspath: []string{`""`}, + system: "sdk_public_31_system_modules", + java8classpath: []string{"prebuilts/sdk/31/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/31/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/31/public/framework.aidl", + }, + { + name: "module_32", + properties: `sdk_version: "module_32",`, + bootclasspath: []string{`""`}, + system: "sdk_module-lib_32_system_modules", + java8classpath: []string{"prebuilts/sdk/32/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/32/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/32/public/framework.aidl", + }, + { + // Test case only applies when Always_use_prebuilt_sdks=false (the default). + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false), + name: "system_server_current", properties: `sdk_version: "system_server_current",`, bootclasspath: []string{"android_system_server_stubs_current", "core-lambda-stubs"}, @@ -231,9 +347,62 @@ func TestClasspath(t *testing.T) { java9classpath: []string{"android_system_server_stubs_current"}, aidl: "-pout/soong/framework.aidl", }, + { + // Test case only applies when Always_use_prebuilt_sdks=true. + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true), + + name: "system_server_current", + properties: `sdk_version: "system_server_current",`, + bootclasspath: []string{`""`}, + system: "sdk_module-lib_current_system_modules", + java8classpath: []string{"prebuilts/sdk/current/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/current/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/current/public/framework.aidl", + }, + { + name: "system_server_30", + properties: `sdk_version: "system_server_30",`, + bootclasspath: []string{`""`}, + system: "sdk_public_30_system_modules", + java8classpath: []string{"prebuilts/sdk/30/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/30/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/30/public/framework.aidl", + }, + { + name: "system_server_31", + properties: `sdk_version: "system_server_31",`, + bootclasspath: []string{`""`}, + system: "sdk_public_31_system_modules", + java8classpath: []string{"prebuilts/sdk/31/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/31/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/31/public/framework.aidl", + }, + { + name: "system_server_32", + properties: `sdk_version: "system_server_32",`, + bootclasspath: []string{`""`}, + system: "sdk_module-lib_32_system_modules", + java8classpath: []string{"prebuilts/sdk/32/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/32/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/32/public/framework.aidl", + }, } + t.Run("basic", func(t *testing.T) { + testClasspathTestCases(t, classpathTestcases, false) + }) + + t.Run("Always_use_prebuilt_sdks=true", func(t *testing.T) { + testClasspathTestCases(t, classpathTestcases, true) + }) +} + +func testClasspathTestCases(t *testing.T, classpathTestcases []classpathTestCase, alwaysUsePrebuiltSdks bool) { for _, testcase := range classpathTestcases { + if testcase.forAlwaysUsePrebuiltSdks != nil && *testcase.forAlwaysUsePrebuiltSdks != alwaysUsePrebuiltSdks { + continue + } + t.Run(testcase.name, func(t *testing.T) { moduleType := "java_library" if testcase.moduleType != "" { @@ -299,7 +468,9 @@ func TestClasspath(t *testing.T) { system = "--system=none" } else if testcase.system != "" { dir := "" - if strings.HasPrefix(testcase.system, "sdk_public_") { + // If the system modules name starts with sdk_ then it is a prebuilt module and so comes + // from the prebuilt directory. + if strings.HasPrefix(testcase.system, "sdk_") { dir = "prebuilts/sdk" } else { dir = defaultJavaDir @@ -351,11 +522,20 @@ func TestClasspath(t *testing.T) { android.AssertPathsRelativeToTopEquals(t, "implicits", deps, javac.Implicits) } + preparer := android.NullFixturePreparer + if alwaysUsePrebuiltSdks { + preparer = android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true) + }) + } + fixtureFactory := android.GroupFixturePreparers( prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{ "29": {}, "30": {}, + "31": {}, + "32": {}, "current": {}, }), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { @@ -369,6 +549,7 @@ func TestClasspath(t *testing.T) { env["ANDROID_JAVA8_HOME"] = "jdk8" } }), + preparer, ) // Test with legacy javac -source 1.8 -target 1.8 diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go index 5311f62c1..fa61ea68c 100644 --- a/java/systemserver_classpath_fragment.go +++ b/java/systemserver_classpath_fragment.go @@ -23,11 +23,19 @@ import ( func init() { registerSystemserverClasspathBuildComponents(android.InitRegistrationContext) + + android.RegisterSdkMemberType(&systemServerClasspathFragmentMemberType{ + SdkMemberTypeBase: android.SdkMemberTypeBase{ + PropertyName: "systemserverclasspath_fragments", + SupportsSdk: true, + }, + }) } func registerSystemserverClasspathBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("platform_systemserverclasspath", platformSystemServerClasspathFactory) ctx.RegisterModuleType("systemserverclasspath_fragment", systemServerClasspathFactory) + ctx.RegisterModuleType("prebuilt_systemserverclasspath_fragment", prebuiltSystemServerClasspathModuleFactory) } type platformSystemServerClasspathModule struct { @@ -50,6 +58,10 @@ func (p *platformSystemServerClasspathModule) AndroidMkEntries() (entries []andr func (p *platformSystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { configuredJars := p.configuredJars(ctx) classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, p.classpathType) + standaloneConfiguredJars := p.standaloneConfiguredJars(ctx) + standaloneClasspathJars := configuredJarListToClasspathJars(ctx, standaloneConfiguredJars, STANDALONE_SYSTEMSERVER_JARS) + configuredJars = configuredJars.AppendList(&standaloneConfiguredJars) + classpathJars = append(classpathJars, standaloneClasspathJars...) p.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars) } @@ -58,9 +70,14 @@ func (p *platformSystemServerClasspathModule) configuredJars(ctx android.ModuleC return dexpreopt.GetGlobalConfig(ctx).SystemServerJars } +func (p *platformSystemServerClasspathModule) standaloneConfiguredJars(ctx android.ModuleContext) android.ConfiguredJarList { + return dexpreopt.GetGlobalConfig(ctx).StandaloneSystemServerJars +} + type SystemServerClasspathModule struct { android.ModuleBase android.ApexModuleBase + android.SdkBase ClasspathFragmentBase @@ -75,28 +92,38 @@ func (s *SystemServerClasspathModule) ShouldSupportSdkVersion(ctx android.BaseMo } type systemServerClasspathFragmentProperties struct { - // The contents of this systemserverclasspath_fragment, could be either java_library, or java_sdk_library. + // List of system_server classpath jars, could be either java_library, or java_sdk_library. // // The order of this list matters as it is the order that is used in the SYSTEMSERVERCLASSPATH. Contents []string + + // List of jars that system_server loads dynamically using separate classloaders. + // + // The order does not matter. + Standalone_contents []string } func systemServerClasspathFactory() android.Module { m := &SystemServerClasspathModule{} m.AddProperties(&m.properties) android.InitApexModule(m) + android.InitSdkAwareModule(m) initClasspathFragment(m, SYSTEMSERVERCLASSPATH) android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) return m } func (s *SystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { - if len(s.properties.Contents) == 0 { - ctx.PropertyErrorf("contents", "empty contents are not allowed") + if len(s.properties.Contents) == 0 && len(s.properties.Standalone_contents) == 0 { + ctx.PropertyErrorf("contents", "Either contents or standalone_contents needs to be non-empty") } configuredJars := s.configuredJars(ctx) classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, s.classpathType) + standaloneConfiguredJars := s.standaloneConfiguredJars(ctx) + standaloneClasspathJars := configuredJarListToClasspathJars(ctx, standaloneConfiguredJars, STANDALONE_SYSTEMSERVER_JARS) + configuredJars = configuredJars.AppendList(&standaloneConfiguredJars) + classpathJars = append(classpathJars, standaloneClasspathJars...) s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars) // Collect the module directory for IDE info in java/jdeps.go. @@ -110,24 +137,78 @@ func (s *SystemServerClasspathModule) configuredJars(ctx android.ModuleContext) jars, unknown := global.ApexSystemServerJars.Filter(possibleUpdatableModules) // TODO(satayev): remove geotz ssc_fragment, since geotz is not part of SSCP anymore. _, unknown = android.RemoveFromList("geotz", unknown) - - // For non test apexes, make sure that all contents are actually declared in make. - if global.ApexSystemServerJars.Len() > 0 && len(unknown) > 0 { - ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_UPDATABLE_SYSTEM_SERVER_JARS", unknown) + // This module only exists in car products. + // So ignore it even if it is not in PRODUCT_APEX_SYSTEM_SERVER_JARS. + // TODO(b/203233647): Add better mechanism to make it optional. + _, unknown = android.RemoveFromList("car-frameworks-service-module", unknown) + + // This module is optional, so it is not present in all products. + // (See PRODUCT_ISOLATED_COMPILATION_ENABLED.) + // So ignore it even if it is not in PRODUCT_APEX_SYSTEM_SERVER_JARS. + // TODO(b/203233647): Add better mechanism to make it optional. + _, unknown = android.RemoveFromList("service-compos", unknown) + + // TODO(satayev): for apex_test we want to include all contents unconditionally to classpaths + // config. However, any test specific jars would not be present in ApexSystemServerJars. Instead, + // we should check if we are creating a config for apex_test via ApexInfo and amend the values. + // This is an exception to support end-to-end test for ApexdUnitTests, until such support exists. + if android.InList("test_service-apexd", possibleUpdatableModules) { + jars = jars.Append("com.android.apex.test_package", "test_service-apexd") + } else if global.ApexSystemServerJars.Len() > 0 && len(unknown) > 0 && !android.IsModuleInVersionedSdk(ctx.Module()) { + // For non test apexes, make sure that all contents are actually declared in make. + ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_SYSTEM_SERVER_JARS", unknown) } return jars } +func (s *SystemServerClasspathModule) standaloneConfiguredJars(ctx android.ModuleContext) android.ConfiguredJarList { + global := dexpreopt.GetGlobalConfig(ctx) + + possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, s.properties.Standalone_contents, systemServerClasspathFragmentContentDepTag) + jars, _ := global.ApexStandaloneSystemServerJars.Filter(possibleUpdatableModules) + + // TODO(jiakaiz): add a check to ensure that the contents are declared in make. + + return jars +} + type systemServerClasspathFragmentContentDependencyTag struct { blueprint.BaseDependencyTag } +// The systemserverclasspath_fragment contents must never depend on prebuilts. +func (systemServerClasspathFragmentContentDependencyTag) ReplaceSourceWithPrebuilt() bool { + return false +} + +// SdkMemberType causes dependencies added with this tag to be automatically added to the sdk as if +// they were specified using java_systemserver_libs or java_sdk_libs. +func (b systemServerClasspathFragmentContentDependencyTag) SdkMemberType(child android.Module) android.SdkMemberType { + // If the module is a java_sdk_library then treat it as if it was specified in the java_sdk_libs + // property, otherwise treat if it was specified in the java_systemserver_libs property. + if javaSdkLibrarySdkMemberType.IsInstance(child) { + return javaSdkLibrarySdkMemberType + } + + return javaSystemserverLibsSdkMemberType +} + +func (b systemServerClasspathFragmentContentDependencyTag) ExportMember() bool { + return true +} + // Contents of system server fragments in an apex are considered to be directly in the apex, as if // they were listed in java_libs. func (systemServerClasspathFragmentContentDependencyTag) CopyDirectlyInAnyApex() {} +// Contents of system server fragments require files from prebuilt apex files. +func (systemServerClasspathFragmentContentDependencyTag) RequiresFilesFromPrebuiltApex() {} + +var _ android.ReplaceSourceWithPrebuilt = systemServerClasspathFragmentContentDepTag +var _ android.SdkMemberDependencyTag = systemServerClasspathFragmentContentDepTag var _ android.CopyDirectlyInAnyApexTag = systemServerClasspathFragmentContentDepTag +var _ android.RequiresFilesFromPrebuiltApexTag = systemServerClasspathFragmentContentDepTag // The tag used for the dependency between the systemserverclasspath_fragment module and its contents. var systemServerClasspathFragmentContentDepTag = systemServerClasspathFragmentContentDependencyTag{} @@ -138,8 +219,17 @@ func IsSystemServerClasspathFragmentContentDepTag(tag blueprint.DependencyTag) b func (s *SystemServerClasspathModule) ComponentDepsMutator(ctx android.BottomUpMutatorContext) { module := ctx.Module() - - for _, name := range s.properties.Contents { + _, isSourceModule := module.(*SystemServerClasspathModule) + var deps []string + deps = append(deps, s.properties.Contents...) + deps = append(deps, s.properties.Standalone_contents...) + + for _, name := range deps { + // A systemserverclasspath_fragment must depend only on other source modules, while the + // prebuilt_systemserverclasspath_fragment_fragment must only depend on other prebuilt modules. + if !isSourceModule { + name = android.PrebuiltNameFromSource(name) + } ctx.AddDependency(module, systemServerClasspathFragmentContentDepTag, name) } } @@ -147,5 +237,95 @@ func (s *SystemServerClasspathModule) ComponentDepsMutator(ctx android.BottomUpM // Collect information for opening IDE project files in java/jdeps.go. func (s *SystemServerClasspathModule) IDEInfo(dpInfo *android.IdeInfo) { dpInfo.Deps = append(dpInfo.Deps, s.properties.Contents...) + dpInfo.Deps = append(dpInfo.Deps, s.properties.Standalone_contents...) dpInfo.Paths = append(dpInfo.Paths, s.modulePaths...) } + +type systemServerClasspathFragmentMemberType struct { + android.SdkMemberTypeBase +} + +func (s *systemServerClasspathFragmentMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) { + ctx.AddVariationDependencies(nil, dependencyTag, names...) +} + +func (s *systemServerClasspathFragmentMemberType) IsInstance(module android.Module) bool { + _, ok := module.(*SystemServerClasspathModule) + return ok +} + +func (s *systemServerClasspathFragmentMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule { + return ctx.SnapshotBuilder().AddPrebuiltModule(member, "prebuilt_systemserverclasspath_fragment") +} + +func (s *systemServerClasspathFragmentMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties { + return &systemServerClasspathFragmentSdkMemberProperties{} +} + +type systemServerClasspathFragmentSdkMemberProperties struct { + android.SdkMemberPropertiesBase + + // List of system_server classpath jars, could be either java_library, or java_sdk_library. + // + // The order of this list matters as it is the order that is used in the SYSTEMSERVERCLASSPATH. + Contents []string + + // List of jars that system_server loads dynamically using separate classloaders. + // + // The order does not matter. + Standalone_contents []string +} + +func (s *systemServerClasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) { + module := variant.(*SystemServerClasspathModule) + + s.Contents = module.properties.Contents + s.Standalone_contents = module.properties.Standalone_contents +} + +func (s *systemServerClasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) { + builder := ctx.SnapshotBuilder() + requiredMemberDependency := builder.SdkMemberReferencePropertyTag(true) + + if len(s.Contents) > 0 { + propertySet.AddPropertyWithTag("contents", s.Contents, requiredMemberDependency) + } + + if len(s.Standalone_contents) > 0 { + propertySet.AddPropertyWithTag("standalone_contents", s.Standalone_contents, requiredMemberDependency) + } +} + +var _ android.SdkMemberType = (*systemServerClasspathFragmentMemberType)(nil) + +// A prebuilt version of the systemserverclasspath_fragment module. +type prebuiltSystemServerClasspathModule struct { + SystemServerClasspathModule + prebuilt android.Prebuilt +} + +func (module *prebuiltSystemServerClasspathModule) Prebuilt() *android.Prebuilt { + return &module.prebuilt +} + +func (module *prebuiltSystemServerClasspathModule) Name() string { + return module.prebuilt.Name(module.ModuleBase.Name()) +} + +func (module *prebuiltSystemServerClasspathModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string { + return nil +} + +var _ android.RequiredFilesFromPrebuiltApex = (*prebuiltSystemServerClasspathModule)(nil) + +func prebuiltSystemServerClasspathModuleFactory() android.Module { + m := &prebuiltSystemServerClasspathModule{} + m.AddProperties(&m.properties) + // This doesn't actually have any prebuilt files of its own so pass a placeholder for the srcs + // array. + android.InitPrebuiltModule(m, &[]string{"placeholder"}) + android.InitApexModule(m) + android.InitSdkAwareModule(m) + android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) + return m +} diff --git a/java/systemserver_classpath_fragment_test.go b/java/systemserver_classpath_fragment_test.go index 9ad50dd4a..ba328e7b1 100644 --- a/java/systemserver_classpath_fragment_test.go +++ b/java/systemserver_classpath_fragment_test.go @@ -99,7 +99,7 @@ func TestPlatformSystemServerClasspathModule_AndroidMkEntries(t *testing.T) { func TestSystemServerClasspathFragmentWithoutContents(t *testing.T) { prepareForTestWithSystemServerClasspath. ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern( - `\Qempty contents are not allowed\E`)). + `\QEither contents or standalone_contents needs to be non-empty\E`)). RunTestWithBp(t, ` systemserverclasspath_fragment { name: "systemserverclasspath-fragment", diff --git a/java/testing.go b/java/testing.go index 8860b45fa..7441e4497 100644 --- a/java/testing.go +++ b/java/testing.go @@ -159,8 +159,7 @@ func FixtureWithPrebuiltApis(release2Modules map[string][]string) android.Fixtur `, strings.Join(android.SortedStringKeys(release2Modules), `", "`)) for release, modules := range release2Modules { - libs := append([]string{"android", "core-for-system-modules"}, modules...) - mockFS.Merge(prebuiltApisFilesForLibs([]string{release}, libs)) + mockFS.Merge(prebuiltApisFilesForModules([]string{release}, modules)) } return android.GroupFixturePreparers( android.FixtureAddTextFile(path, bp), @@ -168,19 +167,32 @@ func FixtureWithPrebuiltApis(release2Modules map[string][]string) android.Fixtur ) } -func prebuiltApisFilesForLibs(apiLevels []string, sdkLibs []string) map[string][]byte { +func prebuiltApisFilesForModules(apiLevels []string, modules []string) map[string][]byte { + libs := append([]string{"android"}, modules...) + fs := make(map[string][]byte) for _, level := range apiLevels { - for _, lib := range sdkLibs { - for _, scope := range []string{"public", "system", "module-lib", "system-server", "test"} { - fs[fmt.Sprintf("prebuilts/sdk/%s/%s/%s.jar", level, scope, lib)] = nil + apiLevel := android.ApiLevelForTest(level) + for _, sdkKind := range []android.SdkKind{android.SdkPublic, android.SdkSystem, android.SdkModule, android.SdkSystemServer, android.SdkTest} { + // A core-for-system-modules file must only be created for the sdk kind that supports it. + if sdkKind == systemModuleKind(sdkKind, apiLevel) { + fs[fmt.Sprintf("prebuilts/sdk/%s/%s/core-for-system-modules.jar", level, sdkKind)] = nil + } + + for _, lib := range libs { + // Create a jar file for every library. + fs[fmt.Sprintf("prebuilts/sdk/%s/%s/%s.jar", level, sdkKind, lib)] = nil + // No finalized API files for "current" if level != "current" { - fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s.txt", level, scope, lib)] = nil - fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s-removed.txt", level, scope, lib)] = nil + fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s.txt", level, sdkKind, lib)] = nil + fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s-removed.txt", level, sdkKind, lib)] = nil } } } + if level == "current" { + fs["prebuilts/sdk/current/core/android.jar"] = nil + } fs[fmt.Sprintf("prebuilts/sdk/%s/public/framework.aidl", level)] = nil } return fs @@ -229,6 +241,26 @@ func FixtureConfigureApexBootJars(bootJars ...string) android.FixturePreparer { ) } +// FixtureUseLegacyCorePlatformApi prepares the fixture by setting the exception list of those +// modules that are allowed to use the legacy core platform API to be the ones supplied. +func FixtureUseLegacyCorePlatformApi(moduleNames ...string) android.FixturePreparer { + lookup := make(map[string]struct{}) + for _, moduleName := range moduleNames { + lookup[moduleName] = struct{}{} + } + return android.FixtureModifyConfig(func(config android.Config) { + // Try and set the legacyCorePlatformApiLookup in the config, the returned value will be the + // actual value that is set. + cached := config.Once(legacyCorePlatformApiLookupKey, func() interface{} { + return lookup + }) + // Make sure that the cached value is the one we need. + if !reflect.DeepEqual(cached, lookup) { + panic(fmt.Errorf("attempting to set legacyCorePlatformApiLookupKey to %q but it has already been set to %q", lookup, cached)) + } + }) +} + // registerRequiredBuildComponentsForTest registers the build components used by // PrepareForTestWithJavaDefaultModules. // @@ -280,6 +312,7 @@ func gatherRequiredDepsForTest() string { "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8", "kotlin-annotations", + "stub-annotations", } for _, extra := range extraModules { @@ -311,7 +344,7 @@ func gatherRequiredDepsForTest() string { }` systemModules := []string{ - "core-current-stubs-system-modules", + "core-public-stubs-system-modules", "core-module-lib-stubs-system-modules", "legacy-core-platform-api-stubs-system-modules", "stable-core-platform-api-stubs-system-modules", @@ -431,3 +464,45 @@ func CheckMergedCompatConfigInputs(t *testing.T, result *android.TestResult, mes output := sourceGlobalCompatConfig.Output(allOutputs[0]) android.AssertPathsRelativeToTopEquals(t, message+": inputs", expectedPaths, output.Implicits) } + +// Register the fake APEX mutator to `android.InitRegistrationContext` as if the real mutator exists +// at runtime. This must be called in `init()` of a test if the test is going to use the fake APEX +// mutator. Otherwise, we will be missing the runtime mutator because "soong-apex" is not a +// dependency, which will cause an inconsistency between testing and runtime mutators. +func RegisterFakeRuntimeApexMutator() { + registerFakeApexMutator(android.InitRegistrationContext) +} + +var PrepareForTestWithFakeApexMutator = android.GroupFixturePreparers( + android.FixtureRegisterWithContext(registerFakeApexMutator), +) + +func registerFakeApexMutator(ctx android.RegistrationContext) { + ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { + ctx.BottomUp("apex", fakeApexMutator).Parallel() + }) +} + +type apexModuleBase interface { + ApexAvailable() []string +} + +var _ apexModuleBase = (*Library)(nil) +var _ apexModuleBase = (*SdkLibrary)(nil) + +// A fake APEX mutator that creates a platform variant and an APEX variant for modules with +// `apex_available`. It helps us avoid a dependency on the real mutator defined in "soong-apex", +// which will cause a cyclic dependency, and it provides an easy way to create an APEX variant for +// testing without dealing with all the complexities in the real mutator. +func fakeApexMutator(mctx android.BottomUpMutatorContext) { + switch mctx.Module().(type) { + case *Library, *SdkLibrary: + if len(mctx.Module().(apexModuleBase).ApexAvailable()) > 0 { + modules := mctx.CreateVariations("", "apex1000") + apexInfo := android.ApexInfo{ + ApexVariationName: "apex1000", + } + mctx.SetVariationProvider(modules[1], android.ApexInfoProvider, apexInfo) + } + } +} |