Merge "Update dex_preopt related OWNERS"
diff --git a/android/arch.go b/android/arch.go
index 1fbba19..340f136 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -1572,20 +1572,20 @@
// with Neon will break those users.
func getNdkAbisConfig() []archConfig {
return []archConfig{
- {"arm", "armv7-a", "", []string{"armeabi-v7a"}},
{"arm64", "armv8-a-branchprot", "", []string{"arm64-v8a"}},
- {"x86", "", "", []string{"x86"}},
+ {"arm", "armv7-a", "", []string{"armeabi-v7a"}},
{"x86_64", "", "", []string{"x86_64"}},
+ {"x86", "", "", []string{"x86"}},
}
}
// getAmlAbisConfig returns a list of archConfigs for the ABIs supported by mainline modules.
func getAmlAbisConfig() []archConfig {
return []archConfig{
- {"arm", "armv7-a-neon", "", []string{"armeabi-v7a"}},
{"arm64", "armv8-a", "", []string{"arm64-v8a"}},
- {"x86", "", "", []string{"x86"}},
+ {"arm", "armv7-a-neon", "", []string{"armeabi-v7a"}},
{"x86_64", "", "", []string{"x86_64"}},
+ {"x86", "", "", []string{"x86"}},
}
}
diff --git a/android/deapexer.go b/android/deapexer.go
index 63508d7..de3f635 100644
--- a/android/deapexer.go
+++ b/android/deapexer.go
@@ -15,49 +15,75 @@
package android
import (
- "fmt"
- "strings"
-
"github.com/google/blueprint"
)
// Provides support for interacting with the `deapexer` module to which a `prebuilt_apex` module
// will delegate the work to export files from a prebuilt '.apex` file.
+//
+// The actual processing that is done is quite convoluted but it is all about combining information
+// from multiple different sources in order to allow a prebuilt module to use a file extracted from
+// an apex file. As follows:
+//
+// 1. A prebuilt module, e.g. prebuilt_bootclasspath_fragment or java_import needs to use a file
+// from a prebuilt_apex/apex_set. It knows the path of the file within the apex but does not know
+// where the apex file is or what apex to use.
+//
+// 2. The connection between the prebuilt module and the prebuilt_apex/apex_set is created through
+// use of an exported_... property on the latter. That causes four things to occur:
+// a. A `deapexer` mopdule is created by the prebuilt_apex/apex_set to extract files from the
+// apex file.
+// b. A dependency is added from the prebuilt_apex/apex_set modules onto the prebuilt modules
+// listed in those properties.
+// c. An APEX variant is created for each of those prebuilt modules.
+// d. A dependency is added from the prebuilt modules to the `deapexer` module.
+//
+// 3. The prebuilt_apex/apex_set modules do not know which files are available in the apex file.
+// That information could be specified on the prebuilt_apex/apex_set modules but without
+// automated generation of those modules it would be expensive to maintain. So, instead they
+// obtain that information from the prebuilt modules. They do not know what files are actually in
+// the apex file either but they know what files they need from it. So, the
+// prebuilt_apex/apex_set modules obtain the files that should be in the apex file from those
+// modules and then pass those onto the `deapexer` module.
+//
+// 4. The `deapexer` module's ninja rule extracts all the files from the apex file into an output
+// directory and checks that all the expected files are there. The expected files are declared as
+// the outputs of the ninja rule so they are available to other modules.
+//
+// 5. The prebuilt modules then retrieve the paths to the files that they needed from the `deapexer`
+// module.
+//
+// The files that are passed to `deapexer` and those that are passed back have a unique identifier
+// that links them together. e.g. If the `deapexer` is passed something like this:
+// javalib/core-libart.jar -> javalib/core-libart.jar
+// it will return something like this:
+// javalib/core-libart.jar -> out/soong/.....deapexer.../javalib/core-libart.jar
+//
+// The reason why the `deapexer` module is separate from the prebuilt_apex/apex_set is to avoid
+// cycles. e.g.
+// prebuilt_apex "com.android.art" depends upon java_import "core-libart":
+// This is so it can create an APEX variant of the latter and obtain information about the
+// files that it needs from the apex file.
+// java_import "core-libart" depends upon `deapexer` module:
+// This is so it can retrieve the paths to the files it needs.
// The information exported by the `deapexer` module, access it using `DeapxerInfoProvider`.
type DeapexerInfo struct {
// map from the name of an exported file from a prebuilt_apex to the path to that file. The
- // exported file name is of the form <module>{<tag>} where <tag> is currently only allowed to be
- // ".dexjar".
+ // exported file name is the apex relative path, e.g. javalib/core-libart.jar.
//
// See Prebuilt.ApexInfoMutator for more information.
exports map[string]Path
}
-// The set of supported prebuilt export tags. Used to verify the tag parameter for
-// `PrebuiltExportPath`.
-var supportedPrebuiltExportTags = map[string]struct{}{
- ".dexjar": {},
-}
-
// PrebuiltExportPath provides the path, or nil if not available, of a file exported from the
// prebuilt_apex that created this ApexInfo.
//
-// The exported file is identified by the module name and the tag:
-// * The module name is the name of the module that contributed the file when the .apex file
-// referenced by the prebuilt_apex was built. It must be specified in one of the exported_...
-// properties on the prebuilt_apex module.
-// * The tag identifies the type of file and is dependent on the module type.
+// The exported file is identified by the apex relative path, e.g. "javalib/core-libart.jar".
//
// See apex/deapexer.go for more information.
-func (i DeapexerInfo) PrebuiltExportPath(name, tag string) Path {
-
- if _, ok := supportedPrebuiltExportTags[tag]; !ok {
- panic(fmt.Errorf("unsupported prebuilt export tag %q, expected one of %s",
- tag, strings.Join(SortedStringKeys(supportedPrebuiltExportTags), ", ")))
- }
-
- path := i.exports[name+"{"+tag+"}"]
+func (i DeapexerInfo) PrebuiltExportPath(apexRelativePath string) Path {
+ path := i.exports[apexRelativePath]
return path
}
@@ -79,5 +105,31 @@
blueprint.BaseDependencyTag
}
+// Mark this tag so dependencies that use it are excluded from APEX contents.
+func (t deapexerTagStruct) ExcludeFromApexContents() {}
+
+var _ ExcludeFromApexContentsTag = DeapexerTag
+
// A tag that is used for dependencies on the `deapexer` module.
var DeapexerTag = deapexerTagStruct{}
+
+// RequiredFilesFromPrebuiltApex must be implemented by modules that require files to be exported
+// from a prebuilt_apex/apex_set.
+type RequiredFilesFromPrebuiltApex interface {
+ // RequiredFilesFromPrebuiltApex returns a list of the file paths (relative to the root of the
+ // APEX's contents) that the implementing module requires from within a prebuilt .apex file.
+ //
+ // For each file path this will cause the file to be extracted out of the prebuilt .apex file, and
+ // the path to the extracted file will be stored in the DeapexerInfo using the APEX relative file
+ // path as the key, The path can then be retrieved using the PrebuiltExportPath(key) method.
+ RequiredFilesFromPrebuiltApex(ctx BaseModuleContext) []string
+}
+
+// Marker interface that identifies dependencies on modules that may require files from a prebuilt
+// apex.
+type RequiresFilesFromPrebuiltApexTag interface {
+ blueprint.DependencyTag
+
+ // Method that differentiates this interface from others.
+ RequiresFilesFromPrebuiltApex()
+}
diff --git a/android/neverallow.go b/android/neverallow.go
index 41b399a..19b58a7 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -55,6 +55,7 @@
AddNeverAllowRules(createCcSdkVariantRules()...)
AddNeverAllowRules(createUncompressDexRules()...)
AddNeverAllowRules(createMakefileGoalRules()...)
+ AddNeverAllowRules(createInitFirstStageRules()...)
}
// Add a NeverAllow rule to the set of rules to apply.
@@ -216,6 +217,15 @@
}
}
+func createInitFirstStageRules() []Rule {
+ return []Rule{
+ NeverAllow().
+ Without("name", "init_first_stage").
+ With("install_in_root", "true").
+ Because("install_in_root is only for init_first_stage."),
+ }
+}
+
func neverallowMutator(ctx BottomUpMutatorContext) {
m, ok := ctx.Module().(Module)
if !ok {
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 43b7cbe..f3493bd 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -166,6 +166,7 @@
func InitPrebuiltModuleWithSrcSupplier(module PrebuiltInterface, srcsSupplier PrebuiltSrcsSupplier, srcsPropertyName string) {
p := module.Prebuilt()
module.AddProperties(&p.properties)
+ module.base().customizableProperties = module.GetProperties()
if srcsSupplier == nil {
panic(fmt.Errorf("srcsSupplier must not be nil"))
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index ced37fe..23524a5 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -259,6 +259,38 @@
}`,
prebuilt: []OsType{Android, BuildOs},
},
+ {
+ name: "prebuilt properties customizable",
+ replaceBp: true,
+ modules: `
+ source {
+ name: "foo",
+ deps: [":bar"],
+ }
+
+ soong_config_module_type {
+ name: "prebuilt_with_config",
+ module_type: "prebuilt",
+ config_namespace: "any_namespace",
+ bool_variables: ["bool_var"],
+ properties: ["prefer"],
+ }
+
+ prebuilt_with_config {
+ name: "bar",
+ prefer: true,
+ srcs: ["prebuilt_file"],
+ soong_config_variables: {
+ bool_var: {
+ prefer: false,
+ conditions_default: {
+ prefer: true,
+ },
+ },
+ },
+ }`,
+ prebuilt: []OsType{Android, BuildOs},
+ },
}
func TestPrebuilts(t *testing.T) {
@@ -394,6 +426,9 @@
ctx.RegisterModuleType("prebuilt", newPrebuiltModule)
ctx.RegisterModuleType("source", newSourceModule)
ctx.RegisterModuleType("override_source", newOverrideSourceModule)
+ ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
+ ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
+ ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
}
type prebuiltModule struct {
diff --git a/android/sdk.go b/android/sdk.go
index 36c576d..5c58612 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -38,6 +38,36 @@
type sdkAwareWithoutModule interface {
RequiredSdks
+ // SdkMemberComponentName will return the name to use for a component of this module based on the
+ // base name of this module.
+ //
+ // The baseName is the name returned by ModuleBase.BaseModuleName(), i.e. the name specified in
+ // the name property in the .bp file so will not include the prebuilt_ prefix.
+ //
+ // The componentNameCreator is a func for creating the name of a component from the base name of
+ // the module, e.g. it could just append ".component" to the name passed in.
+ //
+ // This is intended to be called by prebuilt modules that create component models. It is because
+ // prebuilt module base names come in a variety of different forms:
+ // * unversioned - this is the same as the source module.
+ // * internal to an sdk - this is the unversioned name prefixed by the base name of the sdk
+ // module.
+ // * versioned - this is the same as the internal with the addition of an "@<version>" suffix.
+ //
+ // While this can be called from a source module in that case it will behave the same way as the
+ // unversioned name and return the result of calling the componentNameCreator func on the supplied
+ // base name.
+ //
+ // e.g. Assuming the componentNameCreator func simply appends ".component" to the name passed in
+ // then this will work as follows:
+ // * An unversioned name of "foo" will return "foo.component".
+ // * An internal to the sdk name of "sdk_foo" will return "sdk_foo.component".
+ // * A versioned name of "sdk_foo@current" will return "sdk_foo.component@current".
+ //
+ // Note that in the latter case the ".component" suffix is added before the version. Adding it
+ // after would change the version.
+ SdkMemberComponentName(baseName string, componentNameCreator func(string) string) string
+
sdkBase() *SdkBase
MakeMemberOf(sdk SdkRef)
IsInAnySdk() bool
@@ -135,6 +165,18 @@
return s
}
+func (s *SdkBase) SdkMemberComponentName(baseName string, componentNameCreator func(string) string) string {
+ if s.MemberName() == "" {
+ return componentNameCreator(baseName)
+ } else {
+ index := strings.LastIndex(baseName, "@")
+ unversionedName := baseName[:index]
+ unversionedComponentName := componentNameCreator(unversionedName)
+ versionSuffix := baseName[index:]
+ return unversionedComponentName + versionSuffix
+ }
+}
+
// MakeMemberOf sets this module to be a member of a specific SDK
func (s *SdkBase) MakeMemberOf(sdk SdkRef) {
s.properties.ContainingSdk = &sdk
@@ -300,6 +342,22 @@
Name() string
}
+// BpPrintable is a marker interface that must be implemented by any struct that is added as a
+// property value.
+type BpPrintable interface {
+ bpPrintable()
+}
+
+// BpPrintableBase must be embedded within any struct that is added as a
+// property value.
+type BpPrintableBase struct {
+}
+
+func (b BpPrintableBase) bpPrintable() {
+}
+
+var _ BpPrintable = BpPrintableBase{}
+
// An individual member of the SDK, includes all of the variants that the SDK
// requires.
type SdkMember interface {
@@ -643,3 +701,30 @@
// into which to copy the prebuilt files.
Name() string
}
+
+// ExportedComponentsInfo contains information about the components that this module exports to an
+// sdk snapshot.
+//
+// A component of a module is a child module that the module creates and which forms an integral
+// part of the functionality that the creating module provides. A component module is essentially
+// owned by its creator and is tightly coupled to the creator and other components.
+//
+// e.g. the child modules created by prebuilt_apis are not components because they are not tightly
+// coupled to the prebuilt_apis module. Once they are created the prebuilt_apis ignores them. The
+// child impl and stub library created by java_sdk_library (and corresponding import) are components
+// because the creating module depends upon them in order to provide some of its own functionality.
+//
+// A component is exported if it is part of an sdk snapshot. e.g. The xml and impl child modules are
+// components but they are not exported as they are not part of an sdk snapshot.
+//
+// This information is used by the sdk snapshot generation code to ensure that it does not create
+// an sdk snapshot that contains a declaration of the component module and the module that creates
+// it as that would result in duplicate modules when attempting to use the snapshot. e.g. a snapshot
+// that included the java_sdk_library_import "foo" and also a java_import "foo.stubs" would fail
+// as there would be two modules called "foo.stubs".
+type ExportedComponentsInfo struct {
+ // The names of the exported components.
+ Components []string
+}
+
+var ExportedComponentsInfoProvider = blueprint.NewProvider(ExportedComponentsInfo{})
diff --git a/apex/Android.bp b/apex/Android.bp
index 14c8771..6269757 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -31,6 +31,7 @@
testSrcs: [
"apex_test.go",
"bootclasspath_fragment_test.go",
+ "classpath_element_test.go",
"platform_bootclasspath_test.go",
"systemserver_classpath_fragment_test.go",
"vndk_test.go",
diff --git a/apex/apex.go b/apex/apex.go
index 9e714ab..7ffa6cc 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -171,8 +171,7 @@
Ignore_system_library_special_case *bool
// Whenever apex_payload.img of the APEX should include dm-verity hashtree.
- // Default value is false.
- // TODO(b/190621617): change default value to true.
+ // Default value is true.
Generate_hashtree *bool
// Whenever apex_payload.img of the APEX should not be dm-verity signed. Should be only
@@ -415,6 +414,9 @@
// Path of API coverage generate file
apisUsedByModuleFile android.ModuleOutPath
apisBackedByModuleFile android.ModuleOutPath
+
+ // Collect the module directory for IDE info in java/jdeps.go.
+ modulePaths []string
}
// apexFileClass represents a type of file that can be included in APEX.
@@ -1343,7 +1345,7 @@
// See the generate_hashtree property
func (a *apexBundle) shouldGenerateHashtree() bool {
- return proptools.BoolDefault(a.properties.Generate_hashtree, false)
+ return proptools.BoolDefault(a.properties.Generate_hashtree, true)
}
// See the test_only_unsigned_payload property
@@ -1690,6 +1692,9 @@
handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
+ // Collect the module directory for IDE info in java/jdeps.go.
+ a.modulePaths = append(a.modulePaths, ctx.ModuleDir())
+
// TODO(jiyong): do this using WalkPayloadDeps
// TODO(jiyong): make this clean!!!
ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
@@ -1756,7 +1761,9 @@
ctx.PropertyErrorf("systemserverclasspath_fragments", "%q is not a systemserverclasspath_fragment module", depName)
return false
}
- filesInfo = append(filesInfo, apexClasspathFragmentProtoFile(ctx, child))
+ if af := apexClasspathFragmentProtoFile(ctx, child); af != nil {
+ filesInfo = append(filesInfo, *af)
+ }
return true
}
case javaLibTag:
@@ -2159,17 +2166,23 @@
}
// Add classpaths.proto config.
- filesToAdd = append(filesToAdd, apexClasspathFragmentProtoFile(ctx, module))
+ if af := apexClasspathFragmentProtoFile(ctx, module); af != nil {
+ filesToAdd = append(filesToAdd, *af)
+ }
return filesToAdd
}
-// apexClasspathFragmentProtoFile returns apexFile structure defining the classpath.proto config that
-// the module contributes to the apex.
-func apexClasspathFragmentProtoFile(ctx android.ModuleContext, module blueprint.Module) apexFile {
- fragmentInfo := ctx.OtherModuleProvider(module, java.ClasspathFragmentProtoContentInfoProvider).(java.ClasspathFragmentProtoContentInfo)
- classpathProtoOutput := fragmentInfo.ClasspathFragmentProtoOutput
- return newApexFile(ctx, classpathProtoOutput, classpathProtoOutput.Base(), fragmentInfo.ClasspathFragmentProtoInstallDir.Rel(), etc, nil)
+// apexClasspathFragmentProtoFile returns *apexFile structure defining the classpath.proto config that
+// the module contributes to the apex; or nil if the proto config was not generated.
+func apexClasspathFragmentProtoFile(ctx android.ModuleContext, module blueprint.Module) *apexFile {
+ info := ctx.OtherModuleProvider(module, java.ClasspathFragmentProtoContentInfoProvider).(java.ClasspathFragmentProtoContentInfo)
+ if !info.ClasspathFragmentProtoGenerated {
+ return nil
+ }
+ classpathProtoOutput := info.ClasspathFragmentProtoOutput
+ af := newApexFile(ctx, classpathProtoOutput, classpathProtoOutput.Base(), info.ClasspathFragmentProtoInstallDir.Rel(), etc, nil)
+ return &af
}
// apexFileForBootclasspathFragmentContentModule creates an apexFile for a bootclasspath_fragment
@@ -2355,16 +2368,30 @@
})
}
-// Enforce that Java deps of the apex are using stable SDKs to compile
+// checkUpdatable enforces APEX and its transitive dep properties to have desired values for updatable APEXes.
func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) {
if a.Updatable() {
if String(a.properties.Min_sdk_version) == "" {
ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well")
}
a.checkJavaStableSdkVersion(ctx)
+ a.checkClasspathFragments(ctx)
}
}
+// checkClasspathFragments enforces that all classpath fragments in deps generate classpaths.proto config.
+func (a *apexBundle) checkClasspathFragments(ctx android.ModuleContext) {
+ ctx.VisitDirectDeps(func(module android.Module) {
+ if tag := ctx.OtherModuleDependencyTag(module); tag == bcpfTag || tag == sscpfTag {
+ info := ctx.OtherModuleProvider(module, java.ClasspathFragmentProtoContentInfoProvider).(java.ClasspathFragmentProtoContentInfo)
+ if !info.ClasspathFragmentProtoGenerated {
+ ctx.OtherModuleErrorf(module, "is included in updatable apex %v, it must not set generate_classpaths_proto to false", ctx.ModuleName())
+ }
+ }
+ })
+}
+
+// checkJavaStableSdkVersion enforces that all Java deps are using stable SDKs to compile.
func (a *apexBundle) checkJavaStableSdkVersion(ctx android.ModuleContext) {
// Visit direct deps only. As long as we guarantee top-level deps are using stable SDKs,
// java's checkLinkType guarantees correct usage for transitive deps
@@ -2383,7 +2410,7 @@
})
}
-// Ensures that the all the dependencies are marked as available for this APEX
+// checkApexAvailability ensures that the all the dependencies are marked as available for this APEX.
func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) {
// Let's be practical. Availability for test, host, and the VNDK apex isn't important
if ctx.Host() || a.testApex || a.vndkApex {
@@ -2435,6 +2462,14 @@
})
}
+// Collect information for opening IDE project files in java/jdeps.go.
+func (a *apexBundle) IDEInfo(dpInfo *android.IdeInfo) {
+ dpInfo.Deps = append(dpInfo.Deps, a.properties.Java_libs...)
+ dpInfo.Deps = append(dpInfo.Deps, a.properties.Bootclasspath_fragments...)
+ dpInfo.Deps = append(dpInfo.Deps, a.properties.Systemserverclasspath_fragments...)
+ dpInfo.Paths = append(dpInfo.Paths, a.modulePaths...)
+}
+
var (
apexAvailBaseline = makeApexAvailableBaseline()
inverseApexAvailBaseline = invertApexBaseline(apexAvailBaseline)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 03db524..792f7f3 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -1695,6 +1695,36 @@
expectNoLink("libx", "shared_apex10000", "libz", "shared")
}
+func TestApexMinSdkVersion_SupportsCodeNames_JavaLibs(t *testing.T) {
+ testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ java_libs: ["libx"],
+ min_sdk_version: "S",
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_library {
+ name: "libx",
+ srcs: ["a.java"],
+ apex_available: [ "myapex" ],
+ sdk_version: "current",
+ min_sdk_version: "S", // should be okay
+ }
+ `,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Platform_version_active_codenames = []string{"S"}
+ variables.Platform_sdk_codename = proptools.StringPtr("S")
+ }),
+ )
+}
+
func TestApexMinSdkVersion_DefaultsToLatest(t *testing.T) {
ctx := testApex(t, `
apex {
@@ -3338,60 +3368,109 @@
}
func TestVndkApexCurrent(t *testing.T) {
- ctx := testApex(t, `
- apex_vndk {
- name: "com.android.vndk.current",
- key: "com.android.vndk.current.key",
- updatable: false,
- }
-
- apex_key {
- name: "com.android.vndk.current.key",
- public_key: "testkey.avbpubkey",
- private_key: "testkey.pem",
- }
-
- cc_library {
- name: "libvndk",
- srcs: ["mylib.cpp"],
- vendor_available: true,
- product_available: true,
- vndk: {
- enabled: true,
- },
- system_shared_libs: [],
- stl: "none",
- apex_available: [ "com.android.vndk.current" ],
- }
-
- cc_library {
- name: "libvndksp",
- srcs: ["mylib.cpp"],
- vendor_available: true,
- product_available: true,
- vndk: {
- enabled: true,
- support_system_process: true,
- },
- system_shared_libs: [],
- stl: "none",
- apex_available: [ "com.android.vndk.current" ],
- }
- `+vndkLibrariesTxtFiles("current"))
-
- ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{
- "lib/libvndk.so",
- "lib/libvndksp.so",
+ commonFiles := []string{
"lib/libc++.so",
- "lib64/libvndk.so",
- "lib64/libvndksp.so",
"lib64/libc++.so",
"etc/llndk.libraries.29.txt",
"etc/vndkcore.libraries.29.txt",
"etc/vndksp.libraries.29.txt",
"etc/vndkprivate.libraries.29.txt",
"etc/vndkproduct.libraries.29.txt",
- })
+ }
+ testCases := []struct {
+ vndkVersion string
+ expectedFiles []string
+ }{
+ {
+ vndkVersion: "current",
+ expectedFiles: append(commonFiles,
+ "lib/libvndk.so",
+ "lib/libvndksp.so",
+ "lib64/libvndk.so",
+ "lib64/libvndksp.so"),
+ },
+ {
+ vndkVersion: "",
+ expectedFiles: append(commonFiles,
+ // Legacy VNDK APEX contains only VNDK-SP files (of core variant)
+ "lib/libvndksp.so",
+ "lib64/libvndksp.so"),
+ },
+ }
+ for _, tc := range testCases {
+ t.Run("VNDK.current with DeviceVndkVersion="+tc.vndkVersion, func(t *testing.T) {
+ ctx := testApex(t, `
+ apex_vndk {
+ name: "com.android.vndk.current",
+ key: "com.android.vndk.current.key",
+ updatable: false,
+ }
+
+ apex_key {
+ name: "com.android.vndk.current.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "libvndk",
+ srcs: ["mylib.cpp"],
+ vendor_available: true,
+ product_available: true,
+ vndk: {
+ enabled: true,
+ },
+ system_shared_libs: [],
+ stl: "none",
+ apex_available: [ "com.android.vndk.current" ],
+ }
+
+ cc_library {
+ name: "libvndksp",
+ srcs: ["mylib.cpp"],
+ vendor_available: true,
+ product_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
+ system_shared_libs: [],
+ stl: "none",
+ apex_available: [ "com.android.vndk.current" ],
+ }
+
+ // VNDK-Ext should not cause any problems
+
+ cc_library {
+ name: "libvndk.ext",
+ srcs: ["mylib2.cpp"],
+ vendor: true,
+ vndk: {
+ enabled: true,
+ extends: "libvndk",
+ },
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libvndksp.ext",
+ srcs: ["mylib2.cpp"],
+ vendor: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ extends: "libvndksp",
+ },
+ system_shared_libs: [],
+ stl: "none",
+ }
+ `+vndkLibrariesTxtFiles("current"), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.DeviceVndkVersion = proptools.StringPtr(tc.vndkVersion)
+ }))
+ ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", tc.expectedFiles)
+ })
+ }
}
func TestVndkApexWithPrebuilt(t *testing.T) {
@@ -4639,11 +4718,18 @@
}
}
- checkHiddenAPIIndexInputs := func(t *testing.T, ctx *android.TestContext, expectedInputs string) {
+ checkHiddenAPIIndexInputs := func(t *testing.T, ctx *android.TestContext, expectedIntermediateInputs string) {
t.Helper()
platformBootclasspath := ctx.ModuleForTests("platform-bootclasspath", "android_common")
- indexRule := platformBootclasspath.Rule("monolithic_hidden_API_index")
- java.CheckHiddenAPIRuleInputs(t, expectedInputs, indexRule)
+ var rule android.TestingBuildParams
+
+ rule = platformBootclasspath.Output("hiddenapi-monolithic/index-from-classes.csv")
+ java.CheckHiddenAPIRuleInputs(t, "intermediate index", expectedIntermediateInputs, rule)
+ }
+
+ fragment := java.ApexVariantReference{
+ Apex: proptools.StringPtr("myapex"),
+ Module: proptools.StringPtr("my-bootclasspath-fragment"),
}
t.Run("prebuilt only", func(t *testing.T) {
@@ -4658,7 +4744,13 @@
src: "myapex-arm.apex",
},
},
- exported_java_libs: ["libfoo", "libbar"],
+ exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+ }
+
+ prebuilt_bootclasspath_fragment {
+ name: "my-bootclasspath-fragment",
+ contents: ["libfoo", "libbar"],
+ apex_available: ["myapex"],
}
java_import {
@@ -4673,18 +4765,19 @@
jars: ["libbar.jar"],
},
apex_available: ["myapex"],
+ shared_library: false,
}
`
- ctx := testDexpreoptWithApexes(t, bp, "", preparer)
+ ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
// Verify the correct module jars contribute to the hiddenapi index file.
checkHiddenAPIIndexInputs(t, ctx, `
-.intermediates/libbar.stubs/android_common/combined/libbar.stubs.jar
-.intermediates/libfoo/android_common_myapex/combined/libfoo.jar
-`)
+ out/soong/.intermediates/libbar.stubs/android_common/combined/libbar.stubs.jar
+ out/soong/.intermediates/libfoo/android_common_myapex/combined/libfoo.jar
+ `)
})
t.Run("apex_set only", func(t *testing.T) {
@@ -4692,7 +4785,13 @@
apex_set {
name: "myapex",
set: "myapex.apks",
- exported_java_libs: ["libfoo", "libbar"],
+ exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+ }
+
+ prebuilt_bootclasspath_fragment {
+ name: "my-bootclasspath-fragment",
+ contents: ["libfoo", "libbar"],
+ apex_available: ["myapex"],
}
java_import {
@@ -4707,18 +4806,19 @@
jars: ["libbar.jar"],
},
apex_available: ["myapex"],
+ shared_library: false,
}
`
- ctx := testDexpreoptWithApexes(t, bp, "", preparer)
+ ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
// Verify the correct module jars contribute to the hiddenapi index file.
checkHiddenAPIIndexInputs(t, ctx, `
-.intermediates/libbar.stubs/android_common/combined/libbar.stubs.jar
-.intermediates/libfoo/android_common_myapex/combined/libfoo.jar
-`)
+ out/soong/.intermediates/libbar.stubs/android_common/combined/libbar.stubs.jar
+ out/soong/.intermediates/libfoo/android_common_myapex/combined/libfoo.jar
+ `)
})
t.Run("prebuilt with source library preferred", func(t *testing.T) {
@@ -4733,7 +4833,13 @@
src: "myapex-arm.apex",
},
},
- exported_java_libs: ["libfoo", "libbar"],
+ exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+ }
+
+ prebuilt_bootclasspath_fragment {
+ name: "my-bootclasspath-fragment",
+ contents: ["libfoo", "libbar"],
+ apex_available: ["myapex"],
}
java_import {
@@ -4754,6 +4860,7 @@
jars: ["libbar.jar"],
},
apex_available: ["myapex"],
+ shared_library: false,
}
java_sdk_library {
@@ -4769,7 +4876,7 @@
// prebuilt_apex module always depends on the prebuilt, and so it doesn't
// find the dex boot jar in it. We either need to disable the source libfoo
// or make the prebuilt libfoo preferred.
- testDexpreoptWithApexes(t, bp, "module libfoo does not provide a dex boot jar", preparer)
+ testDexpreoptWithApexes(t, bp, "module libfoo does not provide a dex boot jar", preparer, fragment)
})
t.Run("prebuilt library preferred with source", func(t *testing.T) {
@@ -4784,7 +4891,13 @@
src: "myapex-arm.apex",
},
},
- exported_java_libs: ["libfoo", "libbar"],
+ exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+ }
+
+ prebuilt_bootclasspath_fragment {
+ name: "my-bootclasspath-fragment",
+ contents: ["libfoo", "libbar"],
+ apex_available: ["myapex"],
}
java_import {
@@ -4807,6 +4920,7 @@
jars: ["libbar.jar"],
},
apex_available: ["myapex"],
+ shared_library: false,
}
java_sdk_library {
@@ -4817,15 +4931,15 @@
}
`
- ctx := testDexpreoptWithApexes(t, bp, "", preparer)
+ ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
// Verify the correct module jars contribute to the hiddenapi index file.
checkHiddenAPIIndexInputs(t, ctx, `
-.intermediates/prebuilt_libbar.stubs/android_common/combined/libbar.stubs.jar
-.intermediates/prebuilt_libfoo/android_common_myapex/combined/libfoo.jar
-`)
+ out/soong/.intermediates/prebuilt_libbar.stubs/android_common/combined/libbar.stubs.jar
+ out/soong/.intermediates/prebuilt_libfoo/android_common_myapex/combined/libfoo.jar
+ `)
})
t.Run("prebuilt with source apex preferred", func(t *testing.T) {
@@ -4853,7 +4967,13 @@
src: "myapex-arm.apex",
},
},
- exported_java_libs: ["libfoo", "libbar"],
+ exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+ }
+
+ prebuilt_bootclasspath_fragment {
+ name: "my-bootclasspath-fragment",
+ contents: ["libfoo", "libbar"],
+ apex_available: ["myapex"],
}
java_import {
@@ -4874,6 +4994,7 @@
jars: ["libbar.jar"],
},
apex_available: ["myapex"],
+ shared_library: false,
}
java_sdk_library {
@@ -4884,15 +5005,15 @@
}
`
- ctx := testDexpreoptWithApexes(t, bp, "", preparer)
+ ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/libfoo/android_common_apex10000/hiddenapi/libfoo.jar")
checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/libbar/android_common_myapex/hiddenapi/libbar.jar")
// Verify the correct module jars contribute to the hiddenapi index file.
checkHiddenAPIIndexInputs(t, ctx, `
-.intermediates/libbar/android_common_myapex/javac/libbar.jar
-.intermediates/libfoo/android_common_apex10000/javac/libfoo.jar
-`)
+ out/soong/.intermediates/libbar/android_common_myapex/javac/libbar.jar
+ out/soong/.intermediates/libfoo/android_common_apex10000/javac/libfoo.jar
+ `)
})
t.Run("prebuilt preferred with source apex disabled", func(t *testing.T) {
@@ -4920,7 +5041,13 @@
src: "myapex-arm.apex",
},
},
- exported_java_libs: ["libfoo", "libbar"],
+ exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+ }
+
+ prebuilt_bootclasspath_fragment {
+ name: "my-bootclasspath-fragment",
+ contents: ["libfoo", "libbar"],
+ apex_available: ["myapex"],
}
java_import {
@@ -4943,6 +5070,7 @@
jars: ["libbar.jar"],
},
apex_available: ["myapex"],
+ shared_library: false,
}
java_sdk_library {
@@ -4953,15 +5081,15 @@
}
`
- ctx := testDexpreoptWithApexes(t, bp, "", preparer)
+ ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
// Verify the correct module jars contribute to the hiddenapi index file.
checkHiddenAPIIndexInputs(t, ctx, `
-.intermediates/prebuilt_libbar.stubs/android_common/combined/libbar.stubs.jar
-.intermediates/prebuilt_libfoo/android_common_myapex/combined/libfoo.jar
-`)
+ out/soong/.intermediates/prebuilt_libbar.stubs/android_common/combined/libbar.stubs.jar
+ out/soong/.intermediates/prebuilt_libfoo/android_common_myapex/combined/libfoo.jar
+ `)
})
}
@@ -6528,7 +6656,7 @@
android.AssertStringEquals(t, "myapex input", extractorOutput, copiedApex.Input.String())
}
-func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, preparer android.FixturePreparer) {
+func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, preparer android.FixturePreparer, fragments ...java.ApexVariantReference) {
t.Helper()
bp := `
@@ -6547,6 +6675,15 @@
apex_available: [
"some-non-updatable-apex",
],
+ compile_dex: true,
+ }
+
+ bootclasspath_fragment {
+ name: "some-non-updatable-fragment",
+ contents: ["some-non-updatable-apex-lib"],
+ apex_available: [
+ "some-non-updatable-apex",
+ ],
}
java_library {
@@ -6564,6 +6701,7 @@
"com.android.art.debug",
],
hostdex: true,
+ compile_dex: true,
}
apex {
@@ -6577,7 +6715,7 @@
apex {
name: "some-non-updatable-apex",
key: "some-non-updatable-apex.key",
- java_libs: ["some-non-updatable-apex-lib"],
+ bootclasspath_fragments: ["some-non-updatable-fragment"],
updatable: false,
}
@@ -6592,7 +6730,7 @@
apex {
name: "com.android.art.debug",
key: "com.android.art.debug.key",
- java_libs: ["some-art-lib"],
+ bootclasspath_fragments: ["art-bootclasspath-fragment"],
updatable: true,
min_sdk_version: "current",
}
@@ -6625,10 +6763,10 @@
}
`
- testDexpreoptWithApexes(t, bp, errmsg, preparer)
+ testDexpreoptWithApexes(t, bp, errmsg, preparer, fragments...)
}
-func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, preparer android.FixturePreparer) *android.TestContext {
+func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, preparer android.FixturePreparer, fragments ...java.ApexVariantReference) *android.TestContext {
t.Helper()
fs := android.MockFS{
@@ -6656,11 +6794,22 @@
PrepareForTestWithApexBuildComponents,
preparer,
fs.AddToFixture(),
- android.FixtureAddTextFile("frameworks/base/boot/Android.bp", `
- platform_bootclasspath {
- name: "platform-bootclasspath",
+ android.FixtureModifyMockFS(func(fs android.MockFS) {
+ if _, ok := fs["frameworks/base/boot/Android.bp"]; !ok {
+ insert := ""
+ for _, fragment := range fragments {
+ insert += fmt.Sprintf("{apex: %q, module: %q},\n", *fragment.Apex, *fragment.Module)
+ }
+ fs["frameworks/base/boot/Android.bp"] = []byte(fmt.Sprintf(`
+ platform_bootclasspath {
+ name: "platform-bootclasspath",
+ fragments: [
+ %s
+ ],
+ }
+ `, insert))
}
- `),
+ }),
).
ExtendWithErrorHandler(errorHandler).
RunTestWithBp(t, bp)
@@ -6699,6 +6848,47 @@
`)
}
+func TestUpdatable_should_not_set_generate_classpaths_proto(t *testing.T) {
+ testApexError(t, `"mysystemserverclasspathfragment" .* it must not set generate_classpaths_proto to false`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ systemserverclasspath_fragments: [
+ "mysystemserverclasspathfragment",
+ ],
+ min_sdk_version: "29",
+ updatable: true,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_library {
+ name: "foo",
+ srcs: ["b.java"],
+ min_sdk_version: "29",
+ installable: true,
+ apex_available: [
+ "myapex",
+ ],
+ }
+
+ systemserverclasspath_fragment {
+ name: "mysystemserverclasspathfragment",
+ generate_classpaths_proto: false,
+ contents: [
+ "foo",
+ ],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ `)
+}
+
func TestNoUpdatableJarsInBootImage(t *testing.T) {
// Set the BootJars in dexpreopt.GlobalConfig and productVariables to the same value. This can
// result in an invalid configuration as it does not set the ArtApexJars and allows art apex
@@ -6727,7 +6917,11 @@
t.Run("updatable jar from ART apex in the ART boot image => ok", func(t *testing.T) {
preparer := java.FixtureConfigureBootJars("com.android.art.debug:some-art-lib")
- testNoUpdatableJarsInBootImage(t, "", preparer)
+ fragment := java.ApexVariantReference{
+ Apex: proptools.StringPtr("com.android.art.debug"),
+ Module: proptools.StringPtr("art-bootclasspath-fragment"),
+ }
+ testNoUpdatableJarsInBootImage(t, "", preparer, fragment)
})
t.Run("updatable jar from ART apex in the framework boot image => error", func(t *testing.T) {
@@ -6759,7 +6953,11 @@
t.Run("non-updatable jar from some other apex in the framework boot image => ok", func(t *testing.T) {
preparer := java.FixtureConfigureBootJars("some-non-updatable-apex:some-non-updatable-apex-lib")
- testNoUpdatableJarsInBootImage(t, "", preparer)
+ fragment := java.ApexVariantReference{
+ Apex: proptools.StringPtr("some-non-updatable-apex"),
+ Module: proptools.StringPtr("some-non-updatable-fragment"),
+ }
+ testNoUpdatableJarsInBootImage(t, "", preparer, fragment)
})
t.Run("nonexistent jar in the ART boot image => error", func(t *testing.T) {
@@ -6790,6 +6988,11 @@
func TestDexpreoptAccessDexFilesFromPrebuiltApex(t *testing.T) {
preparer := java.FixtureConfigureBootJars("myapex:libfoo")
t.Run("prebuilt no source", func(t *testing.T) {
+ fragment := java.ApexVariantReference{
+ Apex: proptools.StringPtr("myapex"),
+ Module: proptools.StringPtr("my-bootclasspath-fragment"),
+ }
+
testDexpreoptWithApexes(t, `
prebuilt_apex {
name: "myapex" ,
@@ -6801,36 +7004,21 @@
src: "myapex-arm.apex",
},
},
- exported_java_libs: ["libfoo"],
- }
+ exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+ }
- java_import {
- name: "libfoo",
- jars: ["libfoo.jar"],
- }
-`, "", preparer)
- })
+ prebuilt_bootclasspath_fragment {
+ name: "my-bootclasspath-fragment",
+ contents: ["libfoo"],
+ apex_available: ["myapex"],
+ }
- t.Run("prebuilt no source", func(t *testing.T) {
- testDexpreoptWithApexes(t, `
- prebuilt_apex {
- name: "myapex" ,
- arch: {
- arm64: {
- src: "myapex-arm64.apex",
- },
- arm: {
- src: "myapex-arm.apex",
- },
- },
- exported_java_libs: ["libfoo"],
- }
-
- java_import {
- name: "libfoo",
- jars: ["libfoo.jar"],
- }
-`, "", preparer)
+ java_import {
+ name: "libfoo",
+ jars: ["libfoo.jar"],
+ apex_available: ["myapex"],
+ }
+ `, "", preparer, fragment)
})
}
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index d77c47d..66bc9e0 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "sort"
"strings"
"testing"
@@ -82,6 +83,7 @@
"com.android.art",
],
srcs: ["b.java"],
+ compile_dex: true,
}
java_library {
@@ -90,6 +92,7 @@
"com.android.art",
],
srcs: ["b.java"],
+ compile_dex: true,
}
bootclasspath_fragment {
@@ -318,6 +321,7 @@
apex_available: [
"com.android.art",
],
+ compile_dex: true,
}
java_import {
@@ -326,6 +330,7 @@
apex_available: [
"com.android.art",
],
+ compile_dex: true,
}
`),
)
@@ -355,6 +360,19 @@
addPrebuilt := func(prefer bool, contents ...string) android.FixturePreparer {
text := fmt.Sprintf(`
+ prebuilt_apex {
+ name: "com.android.art",
+ arch: {
+ arm64: {
+ src: "com.android.art-arm64.apex",
+ },
+ arm: {
+ src: "com.android.art-arm.apex",
+ },
+ },
+ exported_bootclasspath_fragments: ["mybootclasspathfragment"],
+ }
+
prebuilt_bootclasspath_fragment {
name: "mybootclasspathfragment",
image_name: "art",
@@ -368,7 +386,47 @@
return android.FixtureAddTextFile("prebuilts/module_sdk/art/Android.bp", text)
}
- t.Run("boot image files", func(t *testing.T) {
+ t.Run("boot image files from source", func(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ commonPreparer,
+
+ // Configure some libraries in the art bootclasspath_fragment that match the source
+ // bootclasspath_fragment's contents property.
+ java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+ addSource("foo", "bar"),
+ ).RunTest(t)
+
+ ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ "etc/classpaths/bootclasspath.pb",
+ "javalib/arm/boot.art",
+ "javalib/arm/boot.oat",
+ "javalib/arm/boot.vdex",
+ "javalib/arm/boot-bar.art",
+ "javalib/arm/boot-bar.oat",
+ "javalib/arm/boot-bar.vdex",
+ "javalib/arm64/boot.art",
+ "javalib/arm64/boot.oat",
+ "javalib/arm64/boot.vdex",
+ "javalib/arm64/boot-bar.art",
+ "javalib/arm64/boot-bar.oat",
+ "javalib/arm64/boot-bar.vdex",
+ "javalib/bar.jar",
+ "javalib/foo.jar",
+ })
+
+ java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ `bar`,
+ `com.android.art.key`,
+ `mybootclasspathfragment`,
+ })
+
+ // Make sure that the source bootclasspath_fragment copies its dex files to the predefined
+ // locations for the art image.
+ module := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
+ checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
+ })
+
+ t.Run("boot image files with preferred prebuilt", func(t *testing.T) {
result := android.GroupFixturePreparers(
commonPreparer,
@@ -403,7 +461,13 @@
`bar`,
`com.android.art.key`,
`mybootclasspathfragment`,
+ `prebuilt_com.android.art`,
})
+
+ // Make sure that the prebuilt bootclasspath_fragment copies its dex files to the predefined
+ // locations for the art image.
+ module := result.ModuleForTests("prebuilt_mybootclasspathfragment", "android_common_com.android.art")
+ checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
})
t.Run("source with inconsistency between config and contents", func(t *testing.T) {
@@ -489,7 +553,7 @@
src: "com.android.art-arm.apex",
},
},
- exported_java_libs: ["foo", "bar"],
+ exported_bootclasspath_fragments: ["mybootclasspathfragment"],
}
java_import {
@@ -521,15 +585,40 @@
java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
`com.android.art.apex.selector`,
- `prebuilt_bar`,
- `prebuilt_foo`,
+ `prebuilt_mybootclasspathfragment`,
})
- java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common", []string{
+ java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_com.android.art", []string{
+ `com.android.art.deapexer`,
`dex2oatd`,
`prebuilt_bar`,
`prebuilt_foo`,
})
+
+ module := result.ModuleForTests("mybootclasspathfragment", "android_common_com.android.art")
+ checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
+}
+
+// checkCopiesToPredefinedLocationForArt checks that the supplied modules are copied to the
+// predefined locations of boot dex jars used as inputs for the ART boot image.
+func checkCopiesToPredefinedLocationForArt(t *testing.T, config android.Config, module android.TestingModule, modules ...string) {
+ t.Helper()
+ bootJarLocations := []string{}
+ for _, output := range module.AllOutputs() {
+ output = android.StringRelativeToTop(config, output)
+ if strings.HasPrefix(output, "out/soong/test_device/dex_artjars_input/") {
+ bootJarLocations = append(bootJarLocations, output)
+ }
+ }
+
+ sort.Strings(bootJarLocations)
+ expected := []string{}
+ for _, m := range modules {
+ expected = append(expected, fmt.Sprintf("out/soong/test_device/dex_artjars_input/%s.jar", m))
+ }
+ sort.Strings(expected)
+
+ android.AssertArrayString(t, "copies to predefined locations for art", expected, bootJarLocations)
}
func TestBootclasspathFragmentContentsNoName(t *testing.T) {
diff --git a/apex/classpath_element_test.go b/apex/classpath_element_test.go
new file mode 100644
index 0000000..0193127
--- /dev/null
+++ b/apex/classpath_element_test.go
@@ -0,0 +1,317 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// 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 apex
+
+import (
+ "reflect"
+ "testing"
+
+ "android/soong/android"
+ "android/soong/java"
+ "github.com/google/blueprint"
+)
+
+// Contains tests for java.CreateClasspathElements logic from java/classpath_element.go that
+// requires apexes.
+
+// testClasspathElementContext is a ClasspathElementContext suitable for use in tests.
+type testClasspathElementContext struct {
+ testContext *android.TestContext
+ module android.Module
+ errs []error
+}
+
+func (t *testClasspathElementContext) OtherModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool {
+ return t.testContext.ModuleHasProvider(module, provider)
+}
+
+func (t *testClasspathElementContext) OtherModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{} {
+ return t.testContext.ModuleProvider(module, provider)
+}
+
+func (t *testClasspathElementContext) ModuleErrorf(fmt string, args ...interface{}) {
+ t.errs = append(t.errs, t.testContext.ModuleErrorf(t.module, fmt, args...))
+}
+
+var _ java.ClasspathElementContext = (*testClasspathElementContext)(nil)
+
+func TestCreateClasspathElements(t *testing.T) {
+ preparer := android.GroupFixturePreparers(
+ prepareForTestWithPlatformBootclasspath,
+ prepareForTestWithArtApex,
+ prepareForTestWithMyapex,
+ // For otherapex.
+ android.FixtureMergeMockFs(android.MockFS{
+ "system/sepolicy/apex/otherapex-file_contexts": nil,
+ }),
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ java.FixtureWithLastReleaseApis("foo", "othersdklibrary"),
+ android.FixtureWithRootAndroidBp(`
+ apex {
+ name: "com.android.art",
+ key: "com.android.art.key",
+ bootclasspath_fragments: [
+ "art-bootclasspath-fragment",
+ ],
+ java_libs: [
+ "othersdklibrary",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "com.android.art.key",
+ public_key: "com.android.art.avbpubkey",
+ private_key: "com.android.art.pem",
+ }
+
+ bootclasspath_fragment {
+ name: "art-bootclasspath-fragment",
+ apex_available: [
+ "com.android.art",
+ ],
+ contents: [
+ "baz",
+ "quuz",
+ ],
+ }
+
+ java_library {
+ name: "baz",
+ apex_available: [
+ "com.android.art",
+ ],
+ srcs: ["b.java"],
+ installable: true,
+ }
+
+ java_library {
+ name: "quuz",
+ apex_available: [
+ "com.android.art",
+ ],
+ srcs: ["b.java"],
+ installable: true,
+ }
+
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ bootclasspath_fragments: [
+ "mybootclasspath-fragment",
+ ],
+ java_libs: [
+ "othersdklibrary",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ bootclasspath_fragment {
+ name: "mybootclasspath-fragment",
+ apex_available: [
+ "myapex",
+ ],
+ contents: [
+ "bar",
+ ],
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["b.java"],
+ installable: true,
+ apex_available: ["myapex"],
+ permitted_packages: ["bar"],
+ }
+
+ java_sdk_library {
+ name: "foo",
+ srcs: ["b.java"],
+ }
+
+ java_sdk_library {
+ name: "othersdklibrary",
+ srcs: ["b.java"],
+ shared_library: false,
+ apex_available: [
+ "com.android.art",
+ "myapex",
+ ],
+ }
+
+ bootclasspath_fragment {
+ name: "non-apex-fragment",
+ contents: ["othersdklibrary"],
+ }
+
+ apex {
+ name: "otherapex",
+ key: "otherapex.key",
+ java_libs: [
+ "otherapexlibrary",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "otherapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_library {
+ name: "otherapexlibrary",
+ srcs: ["b.java"],
+ installable: true,
+ apex_available: ["otherapex"],
+ permitted_packages: ["otherapexlibrary"],
+ }
+
+ platform_bootclasspath {
+ name: "myplatform-bootclasspath",
+
+ fragments: [
+ {
+ apex: "com.android.art",
+ module: "art-bootclasspath-fragment",
+ },
+ ],
+ }
+ `),
+ )
+
+ result := preparer.RunTest(t)
+
+ artFragment := result.Module("art-bootclasspath-fragment", "android_common_apex10000")
+ artBaz := result.Module("baz", "android_common_apex10000")
+ artQuuz := result.Module("quuz", "android_common_apex10000")
+
+ myFragment := result.Module("mybootclasspath-fragment", "android_common_apex10000")
+ myBar := result.Module("bar", "android_common_apex10000")
+
+ nonApexFragment := result.Module("non-apex-fragment", "android_common")
+ other := result.Module("othersdklibrary", "android_common_apex10000")
+
+ otherApexLibrary := result.Module("otherapexlibrary", "android_common_apex10000")
+
+ platformFoo := result.Module("quuz", "android_common")
+
+ bootclasspath := result.Module("myplatform-bootclasspath", "android_common")
+
+ // Use a custom assertion method instead of AssertDeepEquals as the latter formats the output
+ // using %#v which results in meaningless output as ClasspathElements are pointers.
+ assertElementsEquals := func(t *testing.T, message string, expected, actual java.ClasspathElements) {
+ if !reflect.DeepEqual(expected, actual) {
+ t.Errorf("%s: expected:\n %s\n got:\n %s", message, expected, actual)
+ }
+ }
+
+ expectFragmentElement := func(module android.Module, contents ...android.Module) java.ClasspathElement {
+ return &java.ClasspathFragmentElement{module, contents}
+ }
+ expectLibraryElement := func(module android.Module) java.ClasspathElement {
+ return &java.ClasspathLibraryElement{module}
+ }
+
+ newCtx := func() *testClasspathElementContext {
+ return &testClasspathElementContext{testContext: result.TestContext, module: bootclasspath}
+ }
+
+ // Verify that CreateClasspathElements works when given valid input.
+ t.Run("art:baz, art:quuz, my:bar, foo", func(t *testing.T) {
+ ctx := newCtx()
+ elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, artQuuz, myBar, platformFoo}, []android.Module{artFragment, myFragment})
+ expectedElements := java.ClasspathElements{
+ expectFragmentElement(artFragment, artBaz, artQuuz),
+ expectFragmentElement(myFragment, myBar),
+ expectLibraryElement(platformFoo),
+ }
+ assertElementsEquals(t, "elements", expectedElements, elements)
+ })
+
+ // Verify that CreateClasspathElements detects when a fragment does not have an associated apex.
+ t.Run("non apex fragment", func(t *testing.T) {
+ ctx := newCtx()
+ elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{nonApexFragment})
+ android.FailIfNoMatchingErrors(t, "fragment non-apex-fragment{.*} is not part of an apex", ctx.errs)
+ expectedElements := java.ClasspathElements{}
+ assertElementsEquals(t, "elements", expectedElements, elements)
+ })
+
+ // Verify that CreateClasspathElements detects when an apex has multiple fragments.
+ t.Run("multiple fragments for same apex", func(t *testing.T) {
+ ctx := newCtx()
+ elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{artFragment, artFragment})
+ android.FailIfNoMatchingErrors(t, "apex com.android.art has multiple fragments, art-bootclasspath-fragment{.*} and art-bootclasspath-fragment{.*}", ctx.errs)
+ expectedElements := java.ClasspathElements{}
+ assertElementsEquals(t, "elements", expectedElements, elements)
+ })
+
+ // Verify that CreateClasspathElements detects when a library is in multiple fragments.
+ t.Run("library from multiple fragments", func(t *testing.T) {
+ ctx := newCtx()
+ elements := java.CreateClasspathElements(ctx, []android.Module{other}, []android.Module{artFragment, myFragment})
+ android.FailIfNoMatchingErrors(t, "library othersdklibrary{.*} is in two separate fragments, art-bootclasspath-fragment{.*} and mybootclasspath-fragment{.*}", ctx.errs)
+ expectedElements := java.ClasspathElements{}
+ assertElementsEquals(t, "elements", expectedElements, elements)
+ })
+
+ // Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and
+ // are separated by a library from another fragment.
+ t.Run("discontiguous separated by fragment", func(t *testing.T) {
+ ctx := newCtx()
+ elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, myBar, artQuuz, platformFoo}, []android.Module{artFragment, myFragment})
+ expectedElements := java.ClasspathElements{
+ expectFragmentElement(artFragment, artBaz, artQuuz),
+ expectFragmentElement(myFragment, myBar),
+ expectLibraryElement(platformFoo),
+ }
+ assertElementsEquals(t, "elements", expectedElements, elements)
+ android.FailIfNoMatchingErrors(t, "libraries from the same fragment must be contiguous, however baz{.*} and quuz{os:android,arch:common,apex:apex10000} from fragment art-bootclasspath-fragment{.*} are separated by libraries from fragment mybootclasspath-fragment{.*} like bar{.*}", ctx.errs)
+ })
+
+ // Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and
+ // are separated by a standalone library.
+ t.Run("discontiguous separated by library", func(t *testing.T) {
+ ctx := newCtx()
+ elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, platformFoo, artQuuz, myBar}, []android.Module{artFragment, myFragment})
+ expectedElements := java.ClasspathElements{
+ expectFragmentElement(artFragment, artBaz, artQuuz),
+ expectLibraryElement(platformFoo),
+ expectFragmentElement(myFragment, myBar),
+ }
+ assertElementsEquals(t, "elements", expectedElements, elements)
+ android.FailIfNoMatchingErrors(t, "libraries from the same fragment must be contiguous, however baz{.*} and quuz{os:android,arch:common,apex:apex10000} from fragment art-bootclasspath-fragment{.*} are separated by library quuz{.*}", ctx.errs)
+ })
+
+ // Verify that CreateClasspathElements detects when there a library on the classpath that
+ // indicates it is from an apex the supplied fragments list does not contain a fragment for that
+ // apex.
+ t.Run("no fragment for apex", func(t *testing.T) {
+ ctx := newCtx()
+ elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, otherApexLibrary}, []android.Module{artFragment})
+ expectedElements := java.ClasspathElements{
+ expectFragmentElement(artFragment, artBaz),
+ }
+ assertElementsEquals(t, "elements", expectedElements, elements)
+ android.FailIfNoMatchingErrors(t, `library otherapexlibrary{.*} is from apexes \[otherapex\] which have no corresponding fragment in \[art-bootclasspath-fragment{.*}\]`, ctx.errs)
+ })
+}
diff --git a/apex/deapexer.go b/apex/deapexer.go
index c7cdbfa..c70da15 100644
--- a/apex/deapexer.go
+++ b/apex/deapexer.go
@@ -40,16 +40,6 @@
// This is intentionally not registered by name as it is not intended to be used from within an
// `Android.bp` file.
-// DeapexerExportedFile defines the properties needed to expose a file from the deapexer module.
-type DeapexerExportedFile struct {
- // The tag parameter which must be passed to android.OutputFileProducer OutputFiles(tag) method
- // to retrieve the path to the unpacked file.
- Tag string
-
- // The path within the APEX that needs to be exported.
- Path string `android:"path"`
-}
-
// DeapexerProperties specifies the properties supported by the deapexer module.
//
// As these are never intended to be supplied in a .bp file they use a different naming convention
@@ -62,7 +52,9 @@
CommonModules []string
// List of files exported from the .apex file by this module
- ExportedFiles []DeapexerExportedFile
+ //
+ // Each entry is a path from the apex root, e.g. javalib/core-libart.jar.
+ ExportedFiles []string
}
type SelectedApexProperties struct {
@@ -107,27 +99,23 @@
exports := make(map[string]android.Path)
- // Create mappings from name+tag to all the required exported paths.
- for _, e := range p.properties.ExportedFiles {
- tag := e.Tag
- path := e.Path
-
+ // Create mappings from apex relative path to the extracted file's path.
+ exportedPaths := make(android.Paths, 0, len(exports))
+ for _, path := range p.properties.ExportedFiles {
// Populate the exports that this makes available.
- exports[tag] = deapexerOutput.Join(ctx, path)
+ extractedPath := deapexerOutput.Join(ctx, path)
+ exports[path] = extractedPath
+ exportedPaths = append(exportedPaths, extractedPath)
}
// If the prebuilt_apex exports any files then create a build rule that unpacks the apex using
// deapexer and verifies that all the required files were created. Also, make the mapping from
- // name+tag to path available for other modules.
+ // apex relative path to extracted file path available for other modules.
if len(exports) > 0 {
// Make the information available for other modules.
ctx.SetProvider(android.DeapexerProvider, android.NewDeapexerInfo(exports))
// Create a sorted list of the files that this exports.
- exportedPaths := make(android.Paths, 0, len(exports))
- for _, p := range exports {
- exportedPaths = append(exportedPaths, p)
- }
exportedPaths = android.SortedUniquePaths(exportedPaths)
// The apex needs to export some files so create a ninja rule to unpack the apex and check that
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 7297926..279cf54 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -234,12 +234,18 @@
apex {
name: "myapex",
key: "myapex.key",
- java_libs: [
- "bar",
+ bootclasspath_fragments: [
+ "my-bootclasspath-fragment",
],
updatable: false,
}
+ bootclasspath_fragment {
+ name: "my-bootclasspath-fragment",
+ contents: ["bar"],
+ apex_available: ["myapex"],
+ }
+
apex_key {
name: "myapex.key",
public_key: "testkey.avbpubkey",
@@ -267,6 +273,10 @@
apex: "com.android.art",
module: "art-bootclasspath-fragment",
},
+ {
+ apex: "myapex",
+ module: "my-bootclasspath-fragment",
+ },
],
}
`,
@@ -283,7 +293,8 @@
})
java.CheckPlatformBootclasspathFragments(t, result, "myplatform-bootclasspath", []string{
- `com.android.art:art-bootclasspath-fragment`,
+ "com.android.art:art-bootclasspath-fragment",
+ "myapex:my-bootclasspath-fragment",
})
// Make sure that the myplatform-bootclasspath has the correct dependencies.
@@ -307,6 +318,7 @@
// The fragments.
`com.android.art:art-bootclasspath-fragment`,
+ `myapex:my-bootclasspath-fragment`,
})
}
@@ -410,6 +422,12 @@
platform_bootclasspath {
name: "myplatform-bootclasspath",
+ fragments: [
+ {
+ apex: "myapex",
+ module:"mybootclasspath-fragment",
+ },
+ ],
}
`,
)
@@ -431,7 +449,7 @@
"platform:legacy.core.platform.api.stubs",
// Needed for generating the boot image.
- `platform:dex2oatd`,
+ "platform:dex2oatd",
// The platform_bootclasspath intentionally adds dependencies on both source and prebuilt
// modules when available as it does not know which one will be preferred.
@@ -442,6 +460,9 @@
// Only a source module exists.
"myapex:bar",
+
+ // The fragments.
+ "myapex:mybootclasspath-fragment",
})
}
@@ -460,3 +481,62 @@
pairs := java.ApexNamePairsFromModules(ctx, modules)
android.AssertDeepEquals(t, "module dependencies", expected, pairs)
}
+
+// TestPlatformBootclasspath_IncludesRemainingApexJars verifies that any apex boot jar is present in
+// platform_bootclasspath's classpaths.proto config, if the apex does not generate its own config
+// by setting generate_classpaths_proto property to false.
+func TestPlatformBootclasspath_IncludesRemainingApexJars(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithPlatformBootclasspath,
+ prepareForTestWithMyapex,
+ java.FixtureConfigureUpdatableBootJars("myapex:foo"),
+ android.FixtureWithRootAndroidBp(`
+ platform_bootclasspath {
+ name: "platform-bootclasspath",
+ fragments: [
+ {
+ apex: "myapex",
+ module:"foo-fragment",
+ },
+ ],
+ }
+
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ bootclasspath_fragments: ["foo-fragment"],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ bootclasspath_fragment {
+ name: "foo-fragment",
+ generate_classpaths_proto: false,
+ contents: ["foo"],
+ apex_available: ["myapex"],
+ }
+
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ apex_available: ["myapex"],
+ permitted_packages: ["foo"],
+ }
+ `),
+ ).RunTest(t)
+
+ java.CheckClasspathFragmentProtoContentInfoProvider(t, result,
+ true, // proto should be generated
+ "myapex:foo", // apex doesn't generate its own config, so must be in platform_bootclasspath
+ "bootclasspath.pb",
+ "out/soong/target/product/test_device/system/etc/classpaths",
+ )
+}
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 67ee500..ea06d45 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -17,7 +17,6 @@
import (
"fmt"
"io"
- "path/filepath"
"strconv"
"strings"
@@ -217,43 +216,57 @@
// apex specific variants of the exported java modules available for use from within make.
apexName := p.BaseModuleName()
for _, fi := range p.apexFilesForAndroidMk {
- moduleName := fi.androidMkModuleName + "." + apexName
- entries := android.AndroidMkEntries{
- Class: fi.class.nameInMake(),
- OverrideName: moduleName,
- OutputFile: android.OptionalPathForPath(fi.builtFile),
- Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
- ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
-
- // soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar Therefore
- // we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
- // we will have foo.jar.jar
- entries.SetString("LOCAL_MODULE_STEM", strings.TrimSuffix(fi.stem(), ".jar"))
- entries.SetString("LOCAL_SOONG_CLASSES_JAR", fi.builtFile.String())
- entries.SetString("LOCAL_SOONG_HEADER_JAR", fi.builtFile.String())
- entries.SetString("LOCAL_SOONG_DEX_JAR", fi.builtFile.String())
- entries.SetString("LOCAL_DEX_PREOPT", "false")
- },
- },
- ExtraFooters: []android.AndroidMkExtraFootersFunc{
- func(w io.Writer, name, prefix, moduleDir string) {
- // m <module_name> will build <module_name>.<apex_name> as well.
- if fi.androidMkModuleName != moduleName {
- fmt.Fprintf(w, ".PHONY: %s\n", fi.androidMkModuleName)
- fmt.Fprintf(w, "%s: %s\n", fi.androidMkModuleName, moduleName)
- }
- },
- },
- }
-
+ entries := p.createEntriesForApexFile(fi, apexName)
entriesList = append(entriesList, entries)
}
return entriesList
}
+// createEntriesForApexFile creates an AndroidMkEntries for the supplied apexFile
+func (p *prebuiltCommon) createEntriesForApexFile(fi apexFile, apexName string) android.AndroidMkEntries {
+ moduleName := fi.androidMkModuleName + "." + apexName
+ entries := android.AndroidMkEntries{
+ Class: fi.class.nameInMake(),
+ OverrideName: moduleName,
+ OutputFile: android.OptionalPathForPath(fi.builtFile),
+ Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
+
+ // soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar Therefore
+ // we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
+ // we will have foo.jar.jar
+ entries.SetString("LOCAL_MODULE_STEM", strings.TrimSuffix(fi.stem(), ".jar"))
+ var classesJar android.Path
+ var headerJar android.Path
+ if javaModule, ok := fi.module.(java.ApexDependency); ok {
+ classesJar = javaModule.ImplementationAndResourcesJars()[0]
+ headerJar = javaModule.HeaderJars()[0]
+ } else {
+ classesJar = fi.builtFile
+ headerJar = fi.builtFile
+ }
+ entries.SetString("LOCAL_SOONG_CLASSES_JAR", classesJar.String())
+ entries.SetString("LOCAL_SOONG_HEADER_JAR", headerJar.String())
+ entries.SetString("LOCAL_SOONG_DEX_JAR", fi.builtFile.String())
+ entries.SetString("LOCAL_DEX_PREOPT", "false")
+ },
+ },
+ ExtraFooters: []android.AndroidMkExtraFootersFunc{
+ func(w io.Writer, name, prefix, moduleDir string) {
+ // m <module_name> will build <module_name>.<apex_name> as well.
+ if fi.androidMkModuleName != moduleName {
+ fmt.Fprintf(w, ".PHONY: %s\n", fi.androidMkModuleName)
+ fmt.Fprintf(w, "%s: %s\n", fi.androidMkModuleName, moduleName)
+ }
+ },
+ },
+ }
+ return entries
+}
+
// prebuiltApexModuleCreator defines the methods that need to be implemented by prebuilt_apex and
// apex_set in order to create the modules needed to provide access to the prebuilt .apex file.
type prebuiltApexModuleCreator interface {
@@ -540,21 +553,25 @@
}
// Compute the deapexer properties from the transitive dependencies of this module.
- javaModules := []string{}
- exportedFiles := map[string]string{}
+ commonModules := []string{}
+ exportedFiles := []string{}
ctx.WalkDeps(func(child, parent android.Module) bool {
tag := ctx.OtherModuleDependencyTag(child)
+ // If the child is not in the same apex as the parent then ignore it and all its children.
+ if !android.IsDepInSameApex(ctx, parent, child) {
+ return false
+ }
+
name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(child))
- if java.IsBootclasspathFragmentContentDepTag(tag) || tag == exportedJavaLibTag {
- javaModules = append(javaModules, name)
+ if _, ok := tag.(android.RequiresFilesFromPrebuiltApexTag); ok {
+ commonModules = append(commonModules, name)
- // Add the dex implementation jar to the set of exported files. The path here must match the
- // path of the file in the APEX created by apexFileForJavaModule(...).
- exportedFiles[name+"{.dexjar}"] = filepath.Join("javalib", name+".jar")
+ requiredFiles := child.(android.RequiredFilesFromPrebuiltApex).RequiredFilesFromPrebuiltApex(ctx)
+ exportedFiles = append(exportedFiles, requiredFiles...)
- } else if tag == exportedBootclasspathFragmentTag {
- // Only visit the children of the bootclasspath_fragment for now.
+ // Visit the dependencies of this module just in case they also require files from the
+ // prebuilt apex.
return true
}
@@ -563,18 +580,13 @@
// Create properties for deapexer module.
deapexerProperties := &DeapexerProperties{
- // Remove any duplicates from the java modules lists as a module may be included via a direct
+ // Remove any duplicates from the common modules lists as a module may be included via a direct
// dependency as well as transitive ones.
- CommonModules: android.SortedUniqueStrings(javaModules),
+ CommonModules: android.SortedUniqueStrings(commonModules),
}
// Populate the exported files property in a fixed order.
- for _, tag := range android.SortedStringKeys(exportedFiles) {
- deapexerProperties.ExportedFiles = append(deapexerProperties.ExportedFiles, DeapexerExportedFile{
- Tag: tag,
- Path: exportedFiles[tag],
- })
- }
+ deapexerProperties.ExportedFiles = android.SortedUniqueStrings(exportedFiles)
props := struct {
Name *string
@@ -631,6 +643,10 @@
// incorrectly.
func (t exportedDependencyTag) ExcludeFromVisibilityEnforcement() {}
+func (t exportedDependencyTag) RequiresFilesFromPrebuiltApex() {}
+
+var _ android.RequiresFilesFromPrebuiltApexTag = exportedDependencyTag{}
+
var (
exportedJavaLibTag = exportedDependencyTag{name: "exported_java_libs"}
exportedBootclasspathFragmentTag = exportedDependencyTag{name: "exported_bootclasspath_fragments"}
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index 95b6e23..537f51d 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -76,3 +76,54 @@
`mysystemserverclasspathfragment`,
})
}
+
+func TestSystemserverclasspathFragmentNoGeneratedProto(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithSystemserverclasspathFragment,
+ prepareForTestWithMyapex,
+ ).RunTestWithBp(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ systemserverclasspath_fragments: [
+ "mysystemserverclasspathfragment",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_library {
+ name: "foo",
+ srcs: ["b.java"],
+ installable: true,
+ apex_available: [
+ "myapex",
+ ],
+ }
+
+ systemserverclasspath_fragment {
+ name: "mysystemserverclasspathfragment",
+ generate_classpaths_proto: false,
+ contents: [
+ "foo",
+ ],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ `)
+
+ ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+ "javalib/foo.jar",
+ })
+
+ java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+ `myapex.key`,
+ `mysystemserverclasspathfragment`,
+ })
+}
diff --git a/bazel/properties.go b/bazel/properties.go
index 7093d6c..0dd47da 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -19,6 +19,7 @@
"path/filepath"
"regexp"
"sort"
+ "strings"
)
// BazelTargetModuleProperties contain properties and metadata used for
@@ -33,6 +34,10 @@
const BazelTargetModuleNamePrefix = "__bp2build__"
+func StripNamePrefix(moduleName string) string {
+ return strings.TrimPrefix(moduleName, BazelTargetModuleNamePrefix)
+}
+
var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
// Label is used to represent a Bazel compatible Label. Also stores the original
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 3abbc4c..0e6030e 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -11,6 +11,7 @@
"build_conversion.go",
"bzl_conversion.go",
"configurability.go",
+ "compatibility.go",
"constants.go",
"conversion.go",
"metrics.go",
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index ee36982..06a7306 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -29,12 +29,12 @@
bp2buildDir := android.PathForOutput(ctx, "bp2build")
android.RemoveAllOutputDir(bp2buildDir)
- buildToTargets, metrics := GenerateBazelTargets(ctx, true)
+ buildToTargets, metrics, compatLayer := GenerateBazelTargets(ctx, true)
bp2buildFiles := CreateBazelFiles(nil, buildToTargets, ctx.mode)
writeFiles(ctx, bp2buildDir, bp2buildFiles)
soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName)
- writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles())
+ writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(compatLayer))
return metrics
}
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index a1e0424..96a8b09 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -32,6 +32,7 @@
type BazelTarget struct {
name string
+ packageName string
content string
ruleClass string
bzlLoadLocation string
@@ -44,6 +45,16 @@
return t.bzlLoadLocation != ""
}
+// Label is the fully qualified Bazel label constructed from the BazelTarget's
+// package name and target name.
+func (t BazelTarget) Label() string {
+ if t.packageName == "." {
+ return "//:" + t.name
+ } else {
+ return "//" + t.packageName + ":" + t.name
+ }
+}
+
// BazelTargets is a typedef for a slice of BazelTarget objects.
type BazelTargets []BazelTarget
@@ -213,7 +224,7 @@
return attributes
}
-func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (map[string]BazelTargets, CodegenMetrics) {
+func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (map[string]BazelTargets, CodegenMetrics, CodegenCompatLayer) {
buildFileToTargets := make(map[string]BazelTargets)
buildFileToAppend := make(map[string]bool)
@@ -222,6 +233,10 @@
RuleClassCount: make(map[string]int),
}
+ compatLayer := CodegenCompatLayer{
+ NameToLabelMap: make(map[string]string),
+ }
+
dirs := make(map[string]bool)
bpCtx := ctx.Context()
@@ -236,6 +251,7 @@
if b, ok := m.(android.Bazelable); ok && b.HasHandcraftedLabel() {
metrics.handCraftedTargetCount += 1
metrics.TotalModuleCount += 1
+ compatLayer.AddNameToLabelEntry(m.Name(), b.HandcraftedLabel())
pathToBuildFile := getBazelPackagePath(b)
// We are using the entire contents of handcrafted build file, so if multiple targets within
// a package have handcrafted targets, we only want to include the contents one time.
@@ -253,6 +269,7 @@
} else if btm, ok := m.(android.BazelTargetModule); ok {
t = generateBazelTarget(bpCtx, m, btm)
metrics.RuleClassCount[t.ruleClass] += 1
+ compatLayer.AddNameToLabelEntry(m.Name(), t.Label())
} else {
metrics.TotalModuleCount += 1
return
@@ -283,7 +300,7 @@
}
}
- return buildFileToTargets, metrics
+ return buildFileToTargets, metrics, compatLayer
}
func getBazelPackagePath(b android.Bazelable) string {
@@ -324,6 +341,7 @@
targetName := targetNameForBp2Build(ctx, m)
return BazelTarget{
name: targetName,
+ packageName: ctx.ModuleDir(m),
ruleClass: ruleClass,
bzlLoadLocation: bzlLoadLocation,
content: fmt.Sprintf(
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 33609af..0e52f2a 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -1398,14 +1398,14 @@
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
bp: `filegroup {
- name: "fg_foo",
- bazel_module: { label: "//other:fg_foo" },
-}
+ name: "fg_foo",
+ bazel_module: { label: "//other:fg_foo" },
+ }
-filegroup {
- name: "foo",
- bazel_module: { label: "//other:foo" },
-}`,
+ filegroup {
+ name: "foo",
+ bazel_module: { label: "//other:foo" },
+ }`,
expectedBazelTargets: []string{
`// BUILD file`,
},
@@ -1414,25 +1414,31 @@
},
},
{
- description: "filegroup bazel_module.label and bp2build",
+ description: "filegroup bazel_module.label and bp2build in subdir",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
- bp: `filegroup {
- name: "fg_foo",
- bazel_module: {
- label: "//other:fg_foo",
- bp2build_available: true,
- },
-}`,
+ dir: "other",
+ bp: ``,
+ fs: map[string]string{
+ "other/Android.bp": `filegroup {
+ name: "fg_foo",
+ bazel_module: {
+ bp2build_available: true,
+ },
+ }
+ filegroup {
+ name: "fg_bar",
+ bazel_module: {
+ label: "//other:fg_bar"
+ },
+ }`,
+ "other/BUILD.bazel": `// definition for fg_bar`,
+ },
expectedBazelTargets: []string{
`filegroup(
name = "fg_foo",
-)`,
- `// BUILD file`,
- },
- fs: map[string]string{
- "other/BUILD.bazel": `// BUILD file`,
+)`, `// definition for fg_bar`,
},
},
{
@@ -1441,18 +1447,18 @@
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
bp: `filegroup {
- name: "fg_foo",
- bazel_module: {
- label: "//other:fg_foo",
- },
-}
+ name: "fg_foo",
+ bazel_module: {
+ label: "//other:fg_foo",
+ },
+ }
-filegroup {
- name: "fg_bar",
- bazel_module: {
- bp2build_available: true,
- },
-}`,
+ filegroup {
+ name: "fg_bar",
+ bazel_module: {
+ bp2build_available: true,
+ },
+ }`,
expectedBazelTargets: []string{
`filegroup(
name = "fg_bar",
diff --git a/bp2build/compatibility.go b/bp2build/compatibility.go
new file mode 100644
index 0000000..5baa524
--- /dev/null
+++ b/bp2build/compatibility.go
@@ -0,0 +1,31 @@
+package bp2build
+
+import (
+ "android/soong/bazel"
+ "fmt"
+)
+
+// Data from the code generation process that is used to improve compatibility
+// between build systems.
+type CodegenCompatLayer struct {
+ // A map from the original module name to the generated/handcrafted Bazel
+ // label for legacy build systems to be able to build a fully-qualified
+ // Bazel target from an unique module name.
+ NameToLabelMap map[string]string
+}
+
+// Log an entry of module name -> Bazel target label.
+func (compatLayer CodegenCompatLayer) AddNameToLabelEntry(name, label string) {
+ // The module name may be prefixed with bazel.BazelTargetModuleNamePrefix if
+ // generated from bp2build.
+ name = bazel.StripNamePrefix(name)
+ if existingLabel, ok := compatLayer.NameToLabelMap[name]; ok {
+ panic(fmt.Errorf(
+ "Module '%s' maps to more than one Bazel target label: %s, %s. "+
+ "This shouldn't happen. It probably indicates a bug with the bp2build internals.",
+ name,
+ existingLabel,
+ label))
+ }
+ compatLayer.NameToLabelMap[name] = label
+}
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index bced4c1..75bc2b4 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -16,15 +16,31 @@
Contents string
}
-func CreateSoongInjectionFiles() []BazelFile {
+func CreateSoongInjectionFiles(compatLayer CodegenCompatLayer) []BazelFile {
var files []BazelFile
- files = append(files, newFile("cc_toolchain", "BUILD", "")) // Creates a //cc_toolchain package.
+ files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
files = append(files, newFile("cc_toolchain", "constants.bzl", config.BazelCcToolchainVars()))
+ files = append(files, newFile("module_name_to_label", GeneratedBuildFileName, nameToLabelAliases(compatLayer.NameToLabelMap)))
+
return files
}
+func nameToLabelAliases(nameToLabelMap map[string]string) string {
+ ret := make([]string, len(nameToLabelMap))
+
+ for k, v := range nameToLabelMap {
+ // v is the fully qualified label rooted at '//'
+ ret = append(ret, fmt.Sprintf(
+ `alias(
+ name = "%s",
+ actual = "@%s",
+)`, k, v))
+ }
+ return strings.Join(ret, "\n\n")
+}
+
func CreateBazelFiles(
ruleShims map[string]RuleShim,
buildToTargets map[string]BazelTargets,
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index 0931ff7..56ea589 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -80,17 +80,21 @@
}
func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) {
- files := CreateSoongInjectionFiles()
+ files := CreateSoongInjectionFiles(CodegenCompatLayer{})
expectedFilePaths := []bazelFilepath{
{
dir: "cc_toolchain",
- basename: "BUILD",
+ basename: GeneratedBuildFileName,
},
{
dir: "cc_toolchain",
basename: "constants.bzl",
},
+ {
+ dir: "module_name_to_label",
+ basename: GeneratedBuildFileName,
+ },
}
if len(files) != len(expectedFilePaths) {
@@ -104,7 +108,7 @@
t.Errorf("Did not find expected file %s/%s", actualFile.Dir, actualFile.Basename)
}
- if expectedFile.basename != "BUILD" && actualFile.Contents == "" {
+ if expectedFile.basename != GeneratedBuildFileName && actualFile.Contents == "" {
t.Errorf("Contents of %s unexpected empty.", actualFile)
}
}
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 861f7d2..f3cd7f0 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -213,6 +213,6 @@
// Helper method for tests to easily access the targets in a dir.
func generateBazelTargetsForDir(codegenCtx *CodegenContext, dir string) BazelTargets {
// TODO: Set generateFilegroups to true and/or remove the generateFilegroups argument completely
- buildFileToTargets, _ := GenerateBazelTargets(codegenCtx, false)
+ buildFileToTargets, _, _ := GenerateBazelTargets(codegenCtx, false)
return buildFileToTargets[dir]
}
diff --git a/cc/cc.go b/cc/cc.go
index 49e218e..0c9f945 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -585,6 +585,7 @@
hostToolPath() android.OptionalPath
relativeInstallPath() string
makeUninstallable(mod *Module)
+ installInRoot() bool
}
// bazelHandler is the interface for a helper object related to deferring to Bazel for
@@ -1309,6 +1310,10 @@
Bool(c.sanitize.Properties.Sanitize.Config.Cfi_assembly_support)
}
+func (c *Module) InstallInRoot() bool {
+ return c.installer != nil && c.installer.installInRoot()
+}
+
type baseModuleContext struct {
android.BaseModuleContext
moduleContextImpl
diff --git a/cc/config/global.go b/cc/config/global.go
index 24e10a4..4957767 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -46,6 +46,7 @@
"-O2",
"-g",
+ "-fdebug-default-version=5",
"-fdebug-info-for-profiling",
"-fno-strict-aliasing",
@@ -145,8 +146,8 @@
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-r416183b"
- ClangDefaultShortVersion = "12.0.5"
+ ClangDefaultVersion = "clang-r416183b1"
+ ClangDefaultShortVersion = "12.0.7"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
diff --git a/cc/installer.go b/cc/installer.go
index e551c63..f95b493 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -25,6 +25,10 @@
type InstallerProperties struct {
// install to a subdirectory of the default install path for the module
Relative_install_path *string `android:"arch_variant"`
+
+ // Install output directly in {partition}/, not in any subdir. This is only intended for use by
+ // init_first_stage.
+ Install_in_root *bool `android:"arch_variant"`
}
type installLocation int
@@ -66,6 +70,11 @@
if ctx.toolchain().Is64Bit() && installer.dir64 != "" {
dir = installer.dir64
}
+
+ if installer.installInRoot() {
+ dir = ""
+ }
+
if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
dir = filepath.Join(dir, ctx.Target().NativeBridgeRelativePath)
} else if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
@@ -110,3 +119,7 @@
func (installer *baseInstaller) makeUninstallable(mod *Module) {
mod.ModuleBase.MakeUninstallable()
}
+
+func (installer *baseInstaller) installInRoot() bool {
+ return Bool(installer.Properties.Install_in_root)
+}
diff --git a/cc/pgo.go b/cc/pgo.go
index 95c9c2e..e78549e 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -56,7 +56,7 @@
type PgoProperties struct {
Pgo struct {
Instrumentation *bool
- Sampling *bool
+ Sampling *bool `android:"arch_variant"`
Profile_file *string `android:"arch_variant"`
Benchmarks []string
Enable_profile_use *bool `android:"arch_variant"`
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 513730a..b0eb0c6 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -485,6 +485,11 @@
if Bool(s.Hwaddress) {
s.Address = nil
s.Thread = nil
+ // Disable ubsan diagnosic as a workaround for a compiler bug.
+ // TODO(b/191808836): re-enable.
+ s.Diag.Undefined = nil
+ s.Diag.Integer_overflow = nil
+ s.Diag.Misc_undefined = nil
}
// TODO(b/131771163): CFI transiently depends on LTO, and thus Fuzzer is
diff --git a/cc/vndk.go b/cc/vndk.go
index dd1c3e1..0b40076 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -371,7 +371,7 @@
if mctx.ModuleName() == "libz" {
return false
}
- return m.ImageVariation().Variation == android.CoreVariation && lib.shared() && m.IsVndkSp()
+ return m.ImageVariation().Variation == android.CoreVariation && lib.shared() && m.IsVndkSp() && !m.IsVndkExt()
}
useCoreVariant := m.VndkVersion() == mctx.DeviceConfig().PlatformVndkVersion() &&
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index e2ce772..a8602de 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -25,9 +25,10 @@
func createBazelQueryView(ctx *bp2build.CodegenContext, bazelQueryViewDir string) error {
ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
- // Ignore metrics reporting for queryview, since queryview is already a full-repo
- // conversion and can use data from bazel query directly.
- buildToTargets, _ := bp2build.GenerateBazelTargets(ctx, true)
+ // Ignore metrics reporting and compat layers for queryview, since queryview
+ // is already a full-repo conversion and can use data from bazel query
+ // directly.
+ buildToTargets, _, _ := bp2build.GenerateBazelTargets(ctx, true)
filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets, bp2build.QueryView)
for _, f := range filesToWrite {
diff --git a/java/Android.bp b/java/Android.bp
index 680f3a1..5952602 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -33,6 +33,7 @@
"bootclasspath.go",
"bootclasspath_fragment.go",
"builder.go",
+ "classpath_element.go",
"classpath_fragment.go",
"device_host_converter.go",
"dex.go",
diff --git a/java/base.go b/java/base.go
index 1daa108..a251c3f 100644
--- a/java/base.go
+++ b/java/base.go
@@ -156,9 +156,11 @@
// List of java_plugin modules that provide extra errorprone checks.
Extra_check_modules []string
- // Whether to run errorprone on a normal build. If this is false, errorprone
- // will still be run if the RUN_ERROR_PRONE environment variable is true.
- // Default false.
+ // This property can be in 3 states. When set to true, errorprone will
+ // be run during the regular build. When set to false, errorprone will
+ // never be run. When unset, errorprone will be run when the RUN_ERROR_PRONE
+ // environment variable is true. Setting this to false will improve build
+ // performance more than adding -XepDisableAllChecks in javacflags.
Enabled *bool
}
@@ -258,8 +260,8 @@
EmbeddableSdkLibraryComponent
}
-func (e *embeddableInModuleAndImport) initModuleAndImport(moduleBase *android.ModuleBase) {
- e.initSdkLibraryComponent(moduleBase)
+func (e *embeddableInModuleAndImport) initModuleAndImport(module android.Module) {
+ e.initSdkLibraryComponent(module)
}
// Module/Import's DepIsInSameApex(...) delegates to this method.
@@ -706,7 +708,8 @@
// javaVersion flag.
flags.javaVersion = getJavaVersion(ctx, String(j.properties.Java_version), android.SdkContext(j))
- if ctx.Config().RunErrorProne() || Bool(j.properties.Errorprone.Enabled) {
+ epEnabled := j.properties.Errorprone.Enabled
+ if (ctx.Config().RunErrorProne() && epEnabled == nil) || Bool(epEnabled) {
if config.ErrorProneClasspath == nil && ctx.Config().TestProductVariables == nil {
ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?")
}
@@ -981,7 +984,7 @@
// If error-prone is enabled, enable errorprone flags on the regular
// build.
flags = enableErrorproneFlags(flags)
- } else if ctx.Config().RunErrorProne() {
+ } else if ctx.Config().RunErrorProne() && j.properties.Errorprone.Enabled == nil {
// Otherwise, if the RUN_ERROR_PRONE environment variable is set, create
// a new jar file just for compiling with the errorprone compiler to.
// This is because we don't want to cause the java files to get completely
@@ -1510,12 +1513,8 @@
if sdkSpec.Kind == android.SdkCore {
return nil
}
- ver, err := sdkSpec.EffectiveVersion(ctx)
- if err != nil {
- return err
- }
- if ver.GreaterThan(sdkVersion) {
- return fmt.Errorf("newer SDK(%v)", ver)
+ if sdkSpec.ApiLevel.GreaterThan(sdkVersion) {
+ return fmt.Errorf("newer SDK(%v)", sdkSpec.ApiLevel)
}
return nil
}
diff --git a/java/boot_jars.go b/java/boot_jars.go
index 7abda80..5d40ec3 100644
--- a/java/boot_jars.go
+++ b/java/boot_jars.go
@@ -18,37 +18,6 @@
"android/soong/android"
)
-func init() {
- android.RegisterSingletonType("boot_jars", bootJarsSingletonFactory)
-}
-
-func bootJarsSingletonFactory() android.Singleton {
- return &bootJarsSingleton{}
-}
-
-type bootJarsSingleton struct{}
-
-func populateMapFromConfiguredJarList(ctx android.SingletonContext, moduleToApex map[string]string, list android.ConfiguredJarList, name string) bool {
- for i := 0; i < list.Len(); i++ {
- module := list.Jar(i)
- // Ignore jacocoagent it is only added when instrumenting and so has no impact on
- // app compatibility.
- if module == "jacocoagent" {
- continue
- }
- apex := list.Apex(i)
- if existing, ok := moduleToApex[module]; ok {
- ctx.Errorf("Configuration property %q is invalid as it contains multiple references to module (%s) in APEXes (%s and %s)",
- module, existing, apex)
- return false
- }
-
- moduleToApex[module] = apex
- }
-
- return true
-}
-
// isActiveModule returns true if the given module should be considered for boot
// jars, i.e. if it's enabled and the preferred one in case of source and
// prebuilt alternatives.
@@ -59,73 +28,22 @@
return android.IsModulePreferred(module)
}
-func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
- config := ctx.Config()
- if config.SkipBootJarsCheck() {
+// buildRuleForBootJarsPackageCheck generates the build rule to perform the boot jars package
+// check.
+func buildRuleForBootJarsPackageCheck(ctx android.ModuleContext, bootDexJarByModule bootDexJarByModule) {
+ bootDexJars := bootDexJarByModule.bootDexJarsWithoutCoverage()
+ if len(bootDexJars) == 0 {
return
}
- // Populate a map from module name to APEX from the boot jars. If there is a
- // problem such as duplicate modules then fail and return immediately. Note
- // that both module and APEX names are tracked by base names here, so we need
- // to be careful to remove "prebuilt_" prefixes when comparing them with
- // actual modules and APEX bundles.
- moduleToApex := make(map[string]string)
- if !populateMapFromConfiguredJarList(ctx, moduleToApex, config.NonUpdatableBootJars(), "BootJars") ||
- !populateMapFromConfiguredJarList(ctx, moduleToApex, config.UpdatableBootJars(), "UpdatableBootJars") {
- return
- }
-
- // Map from module name to the correct apex variant.
- nameToApexVariant := make(map[string]android.Module)
-
- // Scan all the modules looking for the module/apex variants corresponding to the
- // boot jars.
- ctx.VisitAllModules(func(module android.Module) {
- if !isActiveModule(module) {
- return
- }
-
- name := android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName(module))
- if apex, ok := moduleToApex[name]; ok {
- apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
- if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApexModule(apex) {
- // The module name/apex variant should be unique in the system but double check
- // just in case something has gone wrong.
- if existing, ok := nameToApexVariant[name]; ok {
- ctx.Errorf("found multiple variants matching %s:%s: %q and %q", apex, name, existing, module)
- }
- nameToApexVariant[name] = module
- }
- }
- })
-
timestamp := android.PathForOutput(ctx, "boot-jars-package-check/stamp")
rule := android.NewRuleBuilder(pctx, ctx)
- checkBootJars := rule.Command().BuiltTool("check_boot_jars").
+ rule.Command().BuiltTool("check_boot_jars").
Input(ctx.Config().HostToolPath(ctx, "dexdump")).
- Input(android.PathForSource(ctx, "build/soong/scripts/check_boot_jars/package_allowed_list.txt"))
-
- // If this is not an unbundled build and missing dependencies are not allowed
- // then all the boot jars listed must have been found.
- strict := !config.UnbundledBuild() && !config.AllowMissingDependencies()
-
- // Iterate over the module names on the boot classpath in order
- for _, name := range android.SortedStringKeys(moduleToApex) {
- if apexVariant, ok := nameToApexVariant[name]; ok {
- if dep, ok := apexVariant.(interface{ DexJarBuildPath() android.Path }); ok {
- // Add the dex implementation jar for the module to be checked.
- checkBootJars.Input(dep.DexJarBuildPath())
- } else {
- ctx.Errorf("module %q is of type %q which is not supported as a boot jar", name, ctx.ModuleType(apexVariant))
- }
- } else if strict {
- ctx.Errorf("boot jars package check failed as it could not find module %q for apex %q", name, moduleToApex[name])
- }
- }
-
- checkBootJars.Text("&& touch").Output(timestamp)
+ Input(android.PathForSource(ctx, "build/soong/scripts/check_boot_jars/package_allowed_list.txt")).
+ Inputs(bootDexJars).
+ Text("&& touch").Output(timestamp)
rule.Build("boot_jars_package_check", "check boot jar packages")
// The check-boot-jars phony target depends on the timestamp created if the check succeeds.
diff --git a/java/bootclasspath.go b/java/bootclasspath.go
index eddcc83..ccb69a0 100644
--- a/java/bootclasspath.go
+++ b/java/bootclasspath.go
@@ -144,6 +144,8 @@
// ApexVariantReference specifies a particular apex variant of a module.
type ApexVariantReference struct {
+ android.BpPrintableBase
+
// The name of the module apex variant, i.e. the apex containing the module variant.
//
// If this is not specified then it defaults to "platform" which will cause a dependency to be
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index a6f6505..c7249b0 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -81,6 +81,9 @@
// they were listed in java_libs.
func (b bootclasspathFragmentContentDependencyTag) CopyDirectlyInAnyApex() {}
+// Contents of bootclasspath fragments require files from prebuilt apex files.
+func (b bootclasspathFragmentContentDependencyTag) RequiresFilesFromPrebuiltApex() {}
+
// The tag used for the dependency between the bootclasspath_fragment module and its contents.
var bootclasspathFragmentContentDepTag = bootclasspathFragmentContentDependencyTag{}
@@ -88,6 +91,7 @@
var _ android.ReplaceSourceWithPrebuilt = bootclasspathFragmentContentDepTag
var _ android.SdkMemberTypeDependencyTag = bootclasspathFragmentContentDepTag
var _ android.CopyDirectlyInAnyApexTag = bootclasspathFragmentContentDepTag
+var _ android.RequiresFilesFromPrebuiltApexTag = bootclasspathFragmentContentDepTag
func IsBootclasspathFragmentContentDepTag(tag blueprint.DependencyTag) bool {
return tag == bootclasspathFragmentContentDepTag
@@ -132,19 +136,37 @@
ClasspathFragmentBase
properties bootclasspathFragmentProperties
+
+ // Collect the module directory for IDE info in java/jdeps.go.
+ modulePaths []string
}
// commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt
// bootclasspath fragment modules.
type commonBootclasspathFragment interface {
- // produceHiddenAPIAllFlagsFile produces the all-flags.csv and intermediate files.
+ // produceHiddenAPIOutput produces the all-flags.csv and intermediate files and encodes the flags
+ // into dex files.
//
- // Updates the supplied hiddenAPIInfo with the paths to the generated files set.
- produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIFlagOutput
+ // Returns a *HiddenAPIOutput containing the paths for the generated files. Returns nil if the
+ // module cannot contribute to hidden API processing, e.g. because it is a prebuilt module in a
+ // versioned sdk.
+ produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput
+
+ // produceBootImageFiles produces the boot image (i.e. .art, .oat and .vdex) files for each of the
+ // required android.ArchType values in the returned map.
+ //
+ // It must return nil if the boot image files cannot be produced for whatever reason.
+ produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch
}
var _ commonBootclasspathFragment = (*BootclasspathFragmentModule)(nil)
+// bootImageFilesByArch is a map from android.ArchType to the paths to the boot image files.
+//
+// The paths include the .art, .oat and .vdex files, one for each of the modules from which the boot
+// image is created.
+type bootImageFilesByArch map[android.ArchType]android.Paths
+
func bootclasspathFragmentFactory() android.Module {
m := &BootclasspathFragmentModule{}
m.AddProperties(&m.properties)
@@ -230,14 +252,6 @@
commonApex, apex)
}
}
-
- if len(contents) != 0 {
- // Nothing to do.
- return
- }
-
- // Store the jars in the Contents property so that they can be used to add dependencies.
- m.properties.Contents = modules.CopyOfJars()
}
// bootclasspathImageNameContentsConsistencyCheck checks that the configuration that applies to this
@@ -290,11 +304,11 @@
modules android.ConfiguredJarList
// Map from arch type to the boot image files.
- bootImageFilesByArch map[android.ArchType]android.OutputPaths
+ bootImageFilesByArch bootImageFilesByArch
- // Map from the name of the context module (as returned by Name()) to the hidden API encoded dex
- // jar path.
- contentModuleDexJarPaths map[string]android.Path
+ // 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
}
func (i BootclasspathFragmentApexContentInfo) Modules() android.ConfiguredJarList {
@@ -304,7 +318,7 @@
// Get a map from ArchType to the associated boot image's contents for Android.
//
// Extension boot images only return their own files, not the files of the boot images they extend.
-func (i BootclasspathFragmentApexContentInfo) AndroidBootImageFilesByArchType() map[android.ArchType]android.OutputPaths {
+func (i BootclasspathFragmentApexContentInfo) AndroidBootImageFilesByArchType() bootImageFilesByArch {
return i.bootImageFilesByArch
}
@@ -312,6 +326,8 @@
//
// The dex boot jar is one which has had hidden API encoding performed on it.
func (i BootclasspathFragmentApexContentInfo) DexBootJarPathForContentModule(module android.Module) (android.Path, error) {
+ // A bootclasspath_fragment cannot use a prebuilt library so Name() will return the base name
+ // without a prebuilt_ prefix so is safe to use as the key for the contentModuleDexJarPaths.
name := module.Name()
if dexJar, ok := i.contentModuleDexJarPaths[name]; ok {
return dexJar, nil
@@ -389,6 +405,9 @@
// Generate classpaths.proto config
b.generateClasspathProtoBuildActions(ctx)
+ // Collect the module directory for IDE info in java/jdeps.go.
+ b.modulePaths = append(b.modulePaths, ctx.ModuleDir())
+
// Gather the bootclasspath fragment's contents.
var contents []android.Module
ctx.VisitDirectDeps(func(module android.Module) {
@@ -400,99 +419,91 @@
fragments := gatherApexModulePairDepsWithTag(ctx, bootclasspathFragmentDepTag)
- // Perform hidden API processing.
- hiddenAPIFlagOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments)
-
// Verify that the image_name specified on a bootclasspath_fragment is valid even if this is a
// prebuilt which will not use the image config.
imageConfig := b.getImageConfig(ctx)
- // A prebuilt fragment cannot contribute to the apex.
- if !android.IsModulePrebuilt(ctx.Module()) {
- // Provide the apex content info.
- b.provideApexContentInfo(ctx, imageConfig, contents, hiddenAPIFlagOutput)
+ // A versioned prebuilt_bootclasspath_fragment cannot and does not need to perform hidden API
+ // processing. It cannot do it because it is not part of a prebuilt_apex and so has no access to
+ // the correct dex implementation jar. It does not need to because the platform-bootclasspath
+ // always references the latest bootclasspath_fragments.
+ if !android.IsModuleInVersionedSdk(ctx.Module()) {
+ // Perform hidden API processing.
+ hiddenAPIOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments)
+
+ var bootImageFilesByArch bootImageFilesByArch
+ if imageConfig != nil {
+ // Delegate the production of the boot image files to a module type specific method.
+ common := ctx.Module().(commonBootclasspathFragment)
+ bootImageFilesByArch = common.produceBootImageFiles(ctx, imageConfig, contents)
+
+ if shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
+ // Copy the dex jars of this fragment's content modules to their predefined locations.
+ copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule)
+ }
+ }
+
+ // A prebuilt fragment cannot contribute to an apex.
+ if !android.IsModulePrebuilt(ctx.Module()) {
+ // Provide the apex content info.
+ b.provideApexContentInfo(ctx, imageConfig, hiddenAPIOutput, bootImageFilesByArch)
+ }
}
}
+// shouldCopyBootFilesToPredefinedLocations determines whether the current module should copy boot
+// files, e.g. boot dex jars or boot image files, to the predefined location expected by the rest
+// of the build.
+//
+// This ensures that only a single module will copy its files to the image configuration.
+func shouldCopyBootFilesToPredefinedLocations(ctx android.ModuleContext, imageConfig *bootImageConfig) bool {
+ // Bootclasspath fragment modules that are for the platform do not produce boot related files.
+ apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ if apexInfo.IsForPlatform() {
+ return false
+ }
+
+ // If the image configuration has no modules specified then it means that the build has been
+ // configured to build something other than a boot image, e.g. an sdk, so do not try and copy the
+ // files.
+ if imageConfig.modules.Len() == 0 {
+ return false
+ }
+
+ // Only copy files from the module that is preferred.
+ return isActiveModule(ctx.Module())
+}
+
// provideApexContentInfo creates, initializes and stores the apex content info for use by other
// modules.
-func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module, hiddenAPIFlagOutput *HiddenAPIFlagOutput) {
+func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, hiddenAPIOutput *HiddenAPIOutput, bootImageFilesByArch bootImageFilesByArch) {
// Construct the apex content info from the config.
- info := BootclasspathFragmentApexContentInfo{}
-
- // Populate the apex content info with paths to the dex jars.
- b.populateApexContentInfoDexJars(ctx, &info, contents, hiddenAPIFlagOutput)
+ info := BootclasspathFragmentApexContentInfo{
+ // Populate the apex content info with paths to the dex jars.
+ contentModuleDexJarPaths: hiddenAPIOutput.EncodedBootDexFilesByModule,
+ }
if imageConfig != nil {
info.modules = imageConfig.modules
-
- if !SkipDexpreoptBootJars(ctx) {
- // Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
- // GenerateSingletonBuildActions method as it cannot create it for itself.
- dexpreopt.GetGlobalSoongConfig(ctx)
-
- // Only generate the boot image if the configuration does not skip it.
- if b.generateBootImageBuildActions(ctx, contents, imageConfig) {
- // Allow the apex to access the boot image files.
- files := map[android.ArchType]android.OutputPaths{}
- for _, variant := range imageConfig.variants {
- // We also generate boot images for host (for testing), but we don't need those in the apex.
- // TODO(b/177892522) - consider changing this to check Os.OsClass = android.Device
- if variant.target.Os == android.Android {
- files[variant.target.Arch.ArchType] = variant.imagesDeps
- }
- }
- info.bootImageFilesByArch = files
- }
- }
}
+ info.bootImageFilesByArch = bootImageFilesByArch
+
// Make the apex content info available for other modules.
ctx.SetProvider(BootclasspathFragmentApexContentInfoProvider, info)
}
-// populateApexContentInfoDexJars adds paths to the dex jars provided by this fragment to the
-// apex content info.
-func (b *BootclasspathFragmentModule) populateApexContentInfoDexJars(ctx android.ModuleContext, info *BootclasspathFragmentApexContentInfo, contents []android.Module, hiddenAPIFlagOutput *HiddenAPIFlagOutput) {
-
- info.contentModuleDexJarPaths = map[string]android.Path{}
- if hiddenAPIFlagOutput != nil {
- // Hidden API encoding has been performed.
- flags := hiddenAPIFlagOutput.AllFlagsPath
- for _, m := range contents {
- h := m.(hiddenAPIModule)
- unencodedDex := h.bootDexJar()
- if unencodedDex == nil {
- // This is an error. Sometimes Soong will report the error directly, other times it will
- // defer the error reporting to happen only when trying to use the missing file in ninja.
- // Either way it is handled by extractBootDexJarsFromModules which must have been
- // called before this as it generates the flags that are used to encode these files.
- continue
- }
-
- outputDir := android.PathForModuleOut(ctx, "hiddenapi-modular/encoded").OutputPath
- encodedDex := hiddenAPIEncodeDex(ctx, unencodedDex, flags, *h.uncompressDex(), outputDir)
- info.contentModuleDexJarPaths[m.Name()] = encodedDex
- }
- } else {
- for _, m := range contents {
- j := m.(UsesLibraryDependency)
- dexJar := j.DexJarBuildPath()
- info.contentModuleDexJarPaths[m.Name()] = dexJar
- }
- }
-}
-
// generateClasspathProtoBuildActions generates all required build actions for classpath.proto config
func (b *BootclasspathFragmentModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) {
var classpathJars []classpathJar
+ configuredJars := b.configuredJars(ctx)
if "art" == proptools.String(b.properties.Image_name) {
// ART and platform boot jars must have a corresponding entry in DEX2OATBOOTCLASSPATH
- classpathJars = configuredJarListToClasspathJars(ctx, b.configuredJars(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
+ classpathJars = configuredJarListToClasspathJars(ctx, configuredJars, BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
} else {
- classpathJars = configuredJarListToClasspathJars(ctx, b.configuredJars(ctx), b.classpathType)
+ classpathJars = configuredJarListToClasspathJars(ctx, configuredJars, b.classpathType)
}
- b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars)
+ b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
}
func (b *BootclasspathFragmentModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
@@ -548,12 +559,12 @@
}
// generateHiddenAPIBuildActions generates all the hidden API related build rules.
-func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) *HiddenAPIFlagOutput {
+func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) *HiddenAPIOutput {
// Create hidden API input structure.
input := b.createHiddenAPIFlagInput(ctx, contents, fragments)
- var output *HiddenAPIFlagOutput
+ var output *HiddenAPIOutput
// Hidden API processing is conditional as a temporary workaround as not all
// bootclasspath_fragments provide the appropriate information needed for hidden API processing
@@ -563,7 +574,14 @@
if input.canPerformHiddenAPIProcessing(ctx, b.properties) {
// Delegate the production of the hidden API all-flags.csv file to a module type specific method.
common := ctx.Module().(commonBootclasspathFragment)
- output = common.produceHiddenAPIAllFlagsFile(ctx, contents, input)
+ output = common.produceHiddenAPIOutput(ctx, contents, input)
+ } else {
+ // As hidden API processing cannot be performed fall back to trying to retrieve the legacy
+ // encoded boot dex files, i.e. those files encoded by the individual libraries and returned
+ // from the DexJarBuildPath() method.
+ output = &HiddenAPIOutput{
+ EncodedBootDexFilesByModule: retrieveLegacyEncodedBootDexFiles(ctx, contents),
+ }
}
// Initialize a HiddenAPIInfo structure.
@@ -580,11 +598,9 @@
TransitiveStubDexJarsByKind: input.transitiveStubDexJarsByKind(),
}
- if output != nil {
- // The monolithic hidden API processing also needs access to all the output files produced by
- // hidden API processing of this fragment.
- hiddenAPIInfo.HiddenAPIFlagOutput = *output
- }
+ // The monolithic hidden API processing also needs access to all the output files produced by
+ // hidden API processing of this fragment.
+ hiddenAPIInfo.HiddenAPIFlagOutput = (*output).HiddenAPIFlagOutput
// Provide it for use by other modules.
ctx.SetProvider(HiddenAPIInfoProvider, hiddenAPIInfo)
@@ -592,10 +608,24 @@
return output
}
+// retrieveLegacyEncodedBootDexFiles attempts to retrieve the legacy encoded boot dex jar files.
+func retrieveLegacyEncodedBootDexFiles(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule {
+ // If the current bootclasspath_fragment is the active module or a source module then retrieve the
+ // encoded dex files, otherwise return an empty map.
+ //
+ // An inactive (i.e. not preferred) bootclasspath_fragment needs to retrieve the encoded dex jars
+ // as they are still needed by an apex. An inactive prebuilt_bootclasspath_fragment does not need
+ // to do so and may not yet have access to dex boot jars from a prebuilt_apex/apex_set.
+ if isActiveModule(ctx.Module()) || !android.IsModulePrebuilt(ctx.Module()) {
+ return extractEncodedDexJarsFromModules(ctx, contents)
+ } else {
+ return nil
+ }
+}
+
// createHiddenAPIFlagInput creates a HiddenAPIFlagInput struct and initializes it with information derived
// from the properties on this module and its dependencies.
func (b *BootclasspathFragmentModule) createHiddenAPIFlagInput(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) HiddenAPIFlagInput {
-
// Merge the HiddenAPIInfo from all the fragment dependencies.
dependencyHiddenApiInfo := newHiddenAPIInfo()
dependencyHiddenApiInfo.mergeFromFragmentDeps(ctx, fragments)
@@ -615,12 +645,36 @@
return input
}
-// produceHiddenAPIAllFlagsFile produces the hidden API all-flags.csv file (and supporting files)
-// for the fragment.
-func (b *BootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIFlagOutput {
+// produceHiddenAPIOutput produces the hidden API all-flags.csv file (and supporting files)
+// for the fragment as well as encoding the flags in the boot dex jars.
+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 hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx, contents, input)
+ return hiddenAPIRulesForBootclasspathFragment(ctx, contents, input)
+}
+
+// produceBootImageFiles builds the boot image files from the source if it is required.
+func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch {
+ if SkipDexpreoptBootJars(ctx) {
+ return nil
+ }
+
+ // Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
+ // GenerateSingletonBuildActions method as it cannot create it for itself.
+ dexpreopt.GetGlobalSoongConfig(ctx)
+
+ // Only generate the boot image if the configuration does not skip it.
+ if !b.generateBootImageBuildActions(ctx, contents, imageConfig) {
+ return nil
+ }
+
+ // Only make the files available to an apex if they were actually generated.
+ files := bootImageFilesByArch{}
+ for _, variant := range imageConfig.apexVariants() {
+ files[variant.target.Arch.ArchType] = variant.imagesDeps.Paths()
+ }
+
+ return files
}
// generateBootImageBuildActions generates ninja rules to create the boot image if required for this
@@ -644,10 +698,6 @@
return false
}
- // Copy the dex jars of this fragment's content modules to their predefined locations.
- bootDexJarByModule := extractEncodedDexJarsFromModules(ctx, contents)
- copyBootJarsToPredefinedLocations(ctx, bootDexJarByModule, imageConfig.dexPathsByModule)
-
// Build a profile for the image config and then use that to build the boot image.
profile := bootImageProfileRule(ctx, imageConfig)
buildBootImage(ctx, imageConfig, profile)
@@ -655,6 +705,12 @@
return true
}
+// Collect information for opening IDE project files in java/jdeps.go.
+func (b *BootclasspathFragmentModule) IDEInfo(dpInfo *android.IdeInfo) {
+ dpInfo.Deps = append(dpInfo.Deps, b.properties.Contents...)
+ dpInfo.Paths = append(dpInfo.Paths, b.modulePaths...)
+}
+
type bootclasspathFragmentMemberType struct {
android.SdkMemberTypeBase
}
@@ -693,6 +749,9 @@
Stub_libs []string
Core_platform_stub_libs []string
+ // Fragment properties
+ Fragments []ApexVariantReference
+
// Flag files by *hiddenAPIFlagFileCategory
Flag_files_by_category FlagFilesByCategory
@@ -733,6 +792,9 @@
// Copy stub_libs properties.
b.Stub_libs = module.properties.Api.Stub_libs
b.Core_platform_stub_libs = module.properties.Core_platform_api.Stub_libs
+
+ // Copy fragment properties.
+ b.Fragments = module.properties.Fragments
}
func (b *bootclasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
@@ -755,6 +817,9 @@
corePlatformApiPropertySet := propertySet.AddPropertySet("core_platform_api")
corePlatformApiPropertySet.AddPropertyWithTag("stub_libs", b.Core_platform_stub_libs, requiredMemberDependency)
}
+ if len(b.Fragments) > 0 {
+ propertySet.AddProperty("fragments", b.Fragments)
+ }
hiddenAPISet := propertySet.AddPropertySet("hidden_api")
hiddenAPIDir := "hiddenapi"
@@ -836,9 +901,8 @@
return module.prebuilt.Name(module.ModuleBase.Name())
}
-// produceHiddenAPIAllFlagsFile returns a path to the prebuilt all-flags.csv or nil if none is
-// specified.
-func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []android.Module, _ HiddenAPIFlagInput) *HiddenAPIFlagOutput {
+// 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 {
if src == nil {
// TODO(b/179354495): Fail if this is not provided once prebuilts have been updated.
@@ -847,19 +911,106 @@
return android.PathForModuleSrc(ctx, *src)
}
- output := HiddenAPIFlagOutput{
- StubFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Stub_flags),
- AnnotationFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Annotation_flags),
- MetadataPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Metadata),
- IndexPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Index),
- AllFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.All_flags),
+ // Retrieve the dex files directly from the content modules. They in turn should retrieve the
+ // encoded dex jars from the prebuilt .apex files.
+ encodedBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, contents)
+
+ output := HiddenAPIOutput{
+ HiddenAPIFlagOutput: HiddenAPIFlagOutput{
+ StubFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Stub_flags),
+ AnnotationFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Annotation_flags),
+ MetadataPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Metadata),
+ IndexPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Index),
+ AllFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.All_flags),
+ },
+ EncodedBootDexFilesByModule: encodedBootDexJarsByModule,
}
return &output
}
+// produceBootImageFiles extracts the boot image files from the APEX if available.
+func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch {
+ if !shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
+ 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 := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
+ for _, variant := range imageConfig.apexVariants() {
+ arch := variant.target.Arch.ArchType
+ for _, toPath := range variant.imagesDeps {
+ apexRelativePath := apexRootRelativePathToBootImageFile(arch, toPath.Base())
+ // Get the path to the file that the deapexer extracted from the prebuilt apex file.
+ fromPath := di.PrebuiltExportPath(apexRelativePath)
+
+ // Copy the file to the predefined location.
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: fromPath,
+ Output: toPath,
+ })
+ }
+ }
+
+ // The returned files will be made available to APEXes that include a bootclasspath_fragment.
+ // However, as a prebuilt_bootclasspath_fragment can never contribute to an APEX there is no point
+ // in returning any files.
+ return nil
+}
+
var _ commonBootclasspathFragment = (*prebuiltBootclasspathFragmentModule)(nil)
+// createBootImageTag creates the tag to uniquely identify the boot image file among all of the
+// files that a module requires from the prebuilt .apex file.
+func createBootImageTag(arch android.ArchType, baseName string) string {
+ tag := fmt.Sprintf(".bootimage-%s-%s", arch, baseName)
+ return tag
+}
+
+// RequiredFilesFromPrebuiltApex returns the list of all files the prebuilt_bootclasspath_fragment
+// requires from a prebuilt .apex file.
+//
+// If there is no image config associated with this fragment then it returns nil. Otherwise, it
+// returns the files that are listed in the image config.
+func (module *prebuiltBootclasspathFragmentModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
+ imageConfig := module.getImageConfig(ctx)
+ if imageConfig != nil {
+ // Add the boot image files, e.g. .art, .oat and .vdex files.
+ files := []string{}
+ for _, variant := range imageConfig.apexVariants() {
+ arch := variant.target.Arch.ArchType
+ for _, path := range variant.imagesDeps.Paths() {
+ base := path.Base()
+ files = append(files, apexRootRelativePathToBootImageFile(arch, base))
+ }
+ }
+ return files
+ }
+ return nil
+}
+
+func apexRootRelativePathToBootImageFile(arch android.ArchType, base string) string {
+ return filepath.Join("javalib", arch.String(), base)
+}
+
+var _ android.RequiredFilesFromPrebuiltApex = (*prebuiltBootclasspathFragmentModule)(nil)
+
func prebuiltBootclasspathFragmentFactory() android.Module {
m := &prebuiltBootclasspathFragmentModule{}
m.AddProperties(&m.properties, &m.prebuiltProperties)
diff --git a/java/classpath_element.go b/java/classpath_element.go
new file mode 100644
index 0000000..753e7f8
--- /dev/null
+++ b/java/classpath_element.go
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * 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 (
+ "fmt"
+ "strings"
+
+ "android/soong/android"
+ "github.com/google/blueprint"
+)
+
+// Supports constructing a list of ClasspathElement from a set of fragments and modules.
+
+// ClasspathElement represents a component that contributes to a classpath. That can be
+// either a java module or a classpath fragment module.
+type ClasspathElement interface {
+ Module() android.Module
+ String() string
+}
+
+type ClasspathElements []ClasspathElement
+
+// ClasspathFragmentElement is a ClasspathElement that encapsulates a classpath fragment module.
+type ClasspathFragmentElement struct {
+ Fragment android.Module
+ Contents []android.Module
+}
+
+func (b *ClasspathFragmentElement) Module() android.Module {
+ return b.Fragment
+}
+
+func (b *ClasspathFragmentElement) String() string {
+ contents := []string{}
+ for _, module := range b.Contents {
+ contents = append(contents, module.String())
+ }
+ return fmt.Sprintf("fragment(%s, %s)", b.Fragment, strings.Join(contents, ", "))
+}
+
+var _ ClasspathElement = (*ClasspathFragmentElement)(nil)
+
+// ClasspathLibraryElement is a ClasspathElement that encapsulates a java library.
+type ClasspathLibraryElement struct {
+ Library android.Module
+}
+
+func (b *ClasspathLibraryElement) Module() android.Module {
+ return b.Library
+}
+
+func (b *ClasspathLibraryElement) String() string {
+ return fmt.Sprintf("library{%s}", b.Library)
+}
+
+var _ ClasspathElement = (*ClasspathLibraryElement)(nil)
+
+// ClasspathElementContext defines the context methods needed by CreateClasspathElements
+type ClasspathElementContext interface {
+ OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool
+ OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{}
+ ModuleErrorf(fmt string, args ...interface{})
+}
+
+// CreateClasspathElements creates a list of ClasspathElement objects from a list of libraries and
+// a list of fragments.
+//
+// The libraries parameter contains the set of libraries from which the classpath is constructed.
+// The fragments parameter contains the classpath fragment modules whose contents are libraries that
+// are part of the classpath. Each library in the libraries parameter may be part of a fragment. The
+// determination as to which libraries belong to fragments and which do not is based on the apex to
+// which they belong, if any.
+//
+// Every fragment in the fragments list must be part of one or more apexes and each apex is assumed
+// to contain only a single fragment from the fragments list. A library in the libraries parameter
+// that is part of an apex must be provided by a classpath fragment in the corresponding apex.
+//
+// This will return a ClasspathElements list that contains a ClasspathElement for each standalone
+// library and each fragment. The order of the elements in the list is such that if the list was
+// flattened into a list of library modules that it would result in the same list or modules as the
+// input libraries. Flattening the list can be done by replacing each ClasspathFragmentElement in
+// the list with its Contents field.
+//
+// Requirements/Assumptions:
+// * A fragment can be associated with more than one apex but each apex must only be associated with
+// a single fragment from the fragments list.
+// * All of a fragment's contents must appear as a contiguous block in the same order in the
+// libraries list.
+// * Each library must only appear in a single fragment.
+//
+// The apex is used to identify which libraries belong to which fragment. First a mapping is created
+// from apex to fragment. Then the libraries are iterated over and any library in an apex is
+// associated with an element for the fragment to which it belongs. Otherwise, the libraries are
+// standalone and have their own element.
+//
+// e.g. Given the following input:
+// libraries: com.android.art:core-oj, com.android.art:core-libart, framework, ext
+// fragments: com.android.art:art-bootclasspath-fragment
+//
+// Then this will return:
+// ClasspathFragmentElement(art-bootclasspath-fragment, [core-oj, core-libart]),
+// ClasspathLibraryElement(framework),
+// ClasspathLibraryElement(ext),
+func CreateClasspathElements(ctx ClasspathElementContext, libraries []android.Module, fragments []android.Module) ClasspathElements {
+ // Create a map from apex name to the fragment module. This makes it easy to find the fragment
+ // associated with a particular apex.
+ apexToFragment := map[string]android.Module{}
+ for _, fragment := range fragments {
+ if !ctx.OtherModuleHasProvider(fragment, android.ApexInfoProvider) {
+ ctx.ModuleErrorf("fragment %s is not part of an apex", fragment)
+ continue
+ }
+
+ apexInfo := ctx.OtherModuleProvider(fragment, android.ApexInfoProvider).(android.ApexInfo)
+ for _, apex := range apexInfo.InApexVariants {
+ if existing, ok := apexToFragment[apex]; ok {
+ ctx.ModuleErrorf("apex %s has multiple fragments, %s and %s", apex, fragment, existing)
+ continue
+ }
+ apexToFragment[apex] = fragment
+ }
+ }
+
+ fragmentToElement := map[android.Module]*ClasspathFragmentElement{}
+ elements := []ClasspathElement{}
+ var currentElement ClasspathElement
+
+skipLibrary:
+ // Iterate over the libraries to construct the ClasspathElements list.
+ for _, library := range libraries {
+ var element ClasspathElement
+ if ctx.OtherModuleHasProvider(library, android.ApexInfoProvider) {
+ apexInfo := ctx.OtherModuleProvider(library, android.ApexInfoProvider).(android.ApexInfo)
+
+ var fragment android.Module
+
+ // Make sure that the library is in only one fragment of the classpath.
+ for _, apex := range apexInfo.InApexVariants {
+ if f, ok := apexToFragment[apex]; ok {
+ if fragment == nil {
+ // This is the first fragment so just save it away.
+ fragment = f
+ } else if f != fragment {
+ // This apex variant of the library is in a different fragment.
+ ctx.ModuleErrorf("library %s is in two separate fragments, %s and %s", library, fragment, f)
+ // Skip over this library entirely as otherwise the resulting classpath elements would
+ // be invalid.
+ continue skipLibrary
+ }
+ } else {
+ // There is no fragment associated with the library's apex.
+ }
+ }
+
+ if fragment == nil {
+ ctx.ModuleErrorf("library %s is from apexes %s which have no corresponding fragment in %s",
+ library, apexInfo.InApexVariants, fragments)
+ // Skip over this library entirely as otherwise the resulting classpath elements would
+ // be invalid.
+ continue skipLibrary
+ } else if existingFragmentElement, ok := fragmentToElement[fragment]; ok {
+ // This library is in a fragment element that has already been added.
+
+ // If the existing fragment element is still the current element then this library is
+ // contiguous with other libraries in that fragment so there is nothing more to do.
+ // Otherwise this library is not contiguous with other libraries in the same fragment which
+ // is an error.
+ if existingFragmentElement != currentElement {
+ separator := ""
+ if fragmentElement, ok := currentElement.(*ClasspathFragmentElement); ok {
+ separator = fmt.Sprintf("libraries from fragment %s like %s", fragmentElement.Fragment, fragmentElement.Contents[0])
+ } else {
+ libraryElement := currentElement.(*ClasspathLibraryElement)
+ separator = fmt.Sprintf("library %s", libraryElement.Library)
+ }
+
+ // Get the library that precedes this library in the fragment. That is the last library as
+ // this library has not yet been added.
+ precedingLibraryInFragment := existingFragmentElement.Contents[len(existingFragmentElement.Contents)-1]
+ ctx.ModuleErrorf("libraries from the same fragment must be contiguous, however %s and %s from fragment %s are separated by %s",
+ precedingLibraryInFragment, library, fragment, separator)
+ }
+
+ // Add this library to the fragment element's contents.
+ existingFragmentElement.Contents = append(existingFragmentElement.Contents, library)
+ } else {
+ // This is the first library in this fragment so add a new element for the fragment,
+ // including the library.
+ fragmentElement := &ClasspathFragmentElement{
+ Fragment: fragment,
+ Contents: []android.Module{library},
+ }
+
+ // Store it away so we can detect when attempting to create another element for the same
+ // fragment.
+ fragmentToElement[fragment] = fragmentElement
+ element = fragmentElement
+ }
+ } else {
+ // The library is from the platform so just add an element for it.
+ element = &ClasspathLibraryElement{Library: library}
+ }
+
+ // If no element was created then it means that the library has been added to an existing
+ // fragment element so the list of elements and current element are unaffected.
+ if element != nil {
+ // Add the element to the list and make it the current element for the next iteration.
+ elements = append(elements, element)
+ currentElement = element
+ }
+ }
+
+ return elements
+}
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
index 4c1e749..f7a200a 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -19,6 +19,7 @@
import (
"fmt"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
"strings"
"android/soong/android"
@@ -44,6 +45,11 @@
}
type classpathFragmentProperties struct {
+ // Whether to generated classpaths.proto config instance for the fragment. If the config is not
+ // generated, then relevant boot jars are added to platform classpath, i.e. platform_bootclasspath
+ // or platform_systemserverclasspath. This is useful for non-updatable APEX boot jars, to keep
+ // them as part of dexopt on device. Defaults to true.
+ Generate_classpaths_proto *bool
}
// classpathFragment interface is implemented by a module that contributes jars to a *CLASSPATH
@@ -100,25 +106,30 @@
return jars
}
-func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, jars []classpathJar) {
- outputFilename := strings.ToLower(c.classpathType.String()) + ".pb"
- c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
- c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
+func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, configuredJars android.ConfiguredJarList, jars []classpathJar) {
+ generateProto := proptools.BoolDefault(c.properties.Generate_classpaths_proto, true)
+ if generateProto {
+ outputFilename := strings.ToLower(c.classpathType.String()) + ".pb"
+ c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
+ c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
- generatedJson := android.PathForModuleOut(ctx, outputFilename+".json")
- writeClasspathsJson(ctx, generatedJson, jars)
+ generatedJson := android.PathForModuleOut(ctx, outputFilename+".json")
+ writeClasspathsJson(ctx, generatedJson, jars)
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().
- BuiltTool("conv_classpaths_proto").
- Flag("encode").
- Flag("--format=json").
- FlagWithInput("--input=", generatedJson).
- FlagWithOutput("--output=", c.outputFilepath)
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ BuiltTool("conv_classpaths_proto").
+ Flag("encode").
+ Flag("--format=json").
+ FlagWithInput("--input=", generatedJson).
+ FlagWithOutput("--output=", c.outputFilepath)
- rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String())
+ rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String())
+ }
classpathProtoInfo := ClasspathFragmentProtoContentInfo{
+ ClasspathFragmentProtoGenerated: generateProto,
+ ClasspathFragmentProtoContents: configuredJars,
ClasspathFragmentProtoInstallDir: c.installDirPath,
ClasspathFragmentProtoOutput: c.outputFilepath,
}
@@ -164,6 +175,12 @@
var ClasspathFragmentProtoContentInfoProvider = blueprint.NewProvider(ClasspathFragmentProtoContentInfo{})
type ClasspathFragmentProtoContentInfo struct {
+ // Whether the classpaths.proto config is generated for the fragment.
+ ClasspathFragmentProtoGenerated bool
+
+ // ClasspathFragmentProtoContents contains a list of jars that are part of this classpath fragment.
+ ClasspathFragmentProtoContents android.ConfiguredJarList
+
// ClasspathFragmentProtoOutput is an output path for the generated classpaths.proto config of this module.
//
// The file should be copied to a relevant place on device, see ClasspathFragmentProtoInstallDir
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index dc8df5e..03769fa 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -358,6 +358,19 @@
return ret
}
+// apexVariants returns a list of all *bootImageVariant that could be included in an apex.
+func (image *bootImageConfig) apexVariants() []*bootImageVariant {
+ variants := []*bootImageVariant{}
+ for _, variant := range image.variants {
+ // We also generate boot images for host (for testing), but we don't need those in the apex.
+ // TODO(b/177892522) - consider changing this to check Os.OsClass = android.Device
+ if variant.target.Os == android.Android {
+ variants = append(variants, variant)
+ }
+ }
+ return variants
+}
+
// Return boot image locations (as a list of symbolic paths).
//
// The image "location" is a symbolic path that, with multiarchitecture support, doesn't really
@@ -489,7 +502,7 @@
}
}
-// buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image.
+// buildBootImage takes a bootImageConfig, and creates rules to build it.
func buildBootImage(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) {
var zipFiles android.Paths
for _, variant := range image.variants {
@@ -871,8 +884,9 @@
ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+sfx, variant.installs.String())
ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+sfx, variant.unstrippedInstalls.String())
}
- imageLocationsOnHost, _ := current.getAnyAndroidVariant().imageLocations()
- ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_"+current.name, strings.Join(imageLocationsOnHost, ":"))
+ imageLocationsOnHost, imageLocationsOnDevice := current.getAnyAndroidVariant().imageLocations()
+ ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_HOST"+current.name, strings.Join(imageLocationsOnHost, ":"))
+ ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICE"+current.name, strings.Join(imageLocationsOnDevice, ":"))
ctx.Strict("DEXPREOPT_IMAGE_ZIP_"+current.name, current.zip.String())
}
ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(imageNames, " "))
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 643c5cb..6e22614 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -524,7 +524,7 @@
}
}
- ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) {
+ ctx.VisitDirectDeps(func(module android.Module) {
tag := ctx.OtherModuleDependencyTag(module)
if hiddenAPIStubsTag, ok := tag.(hiddenAPIStubsDependencyTag); ok {
kind := hiddenAPIStubsTag.sdkKind
@@ -580,6 +580,36 @@
b[android.RemoveOptionalPrebuiltPrefix(module.Name())] = path
}
+// bootDexJars returns the boot dex jar paths sorted by their keys.
+func (b bootDexJarByModule) bootDexJars() android.Paths {
+ paths := android.Paths{}
+ for _, k := range android.SortedStringKeys(b) {
+ paths = append(paths, b[k])
+ }
+ return paths
+}
+
+// bootDexJarsWithoutCoverage returns the boot dex jar paths sorted by their keys without coverage
+// libraries if present.
+func (b bootDexJarByModule) bootDexJarsWithoutCoverage() android.Paths {
+ paths := android.Paths{}
+ for _, k := range android.SortedStringKeys(b) {
+ if k == "jacocoagent" {
+ continue
+ }
+ paths = append(paths, b[k])
+ }
+ return paths
+}
+
+// HiddenAPIOutput encapsulates the output from the hidden API processing.
+type HiddenAPIOutput struct {
+ HiddenAPIFlagOutput
+
+ // The map from base module name to the path to the encoded boot dex file.
+ EncodedBootDexFilesByModule bootDexJarByModule
+}
+
// pathForValidation creates a path of the same type as the supplied type but with a name of
// <path>.valid.
//
@@ -604,7 +634,7 @@
// hiddenAPIInfo is a struct containing paths to files that augment the information provided by
// the annotationFlags.
func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc string,
- outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlags android.Path,
+ outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlagPaths android.Paths,
flagFilesByCategory FlagFilesByCategory, allFlagsPaths android.Paths, generatedRemovedDexSignatures android.OptionalPath) {
// The file which is used to record that the flags file is valid.
@@ -635,7 +665,7 @@
command := rule.Command().
BuiltTool("generate_hiddenapi_lists").
FlagWithInput("--csv ", baseFlagsPath).
- Input(annotationFlags).
+ Inputs(annotationFlagPaths).
FlagWithOutput("--output ", tempPath)
// Add the options for the different categories of flag files.
@@ -665,8 +695,8 @@
rule.Build(name, desc)
}
-// hiddenAPIGenerateAllFlagsForBootclasspathFragment will generate all the flags for a fragment
-// of the bootclasspath.
+// hiddenAPIRulesForBootclasspathFragment will generate all the flags for a fragment of the
+// bootclasspath and then encode the flags into the boot dex files.
//
// It takes:
// * Map from android.SdkKind to stub dex jar paths defining the API for that sdk kind.
@@ -679,15 +709,16 @@
// * metadata.csv
// * index.csv
// * all-flags.csv
-func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIFlagOutput {
+// * encoded boot dex files
+func hiddenAPIRulesForBootclasspathFragment(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
hiddenApiSubDir := "modular-hiddenapi"
- // Gather the dex files for the boot libraries provided by this fragment.
- bootDexJars := extractBootDexJarsFromModules(ctx, contents)
+ // Gather information about the boot dex files for the boot libraries provided by this fragment.
+ bootDexInfoByModule := extractBootDexInfoFromModules(ctx, contents)
// Generate the stub-flags.csv.
stubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "stub-flags.csv")
- rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlagsCSV, bootDexJars, input)
+ rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlagsCSV, bootDexInfoByModule.bootDexJars(), input)
rule.Build("modularHiddenAPIStubFlagsFile", "modular hiddenapi stub flags")
// Extract the classes jars from the contents.
@@ -715,16 +746,29 @@
// Generate the all-flags.csv which are the flags that will, in future, be encoded into the dex
// files.
- outputPath := android.PathForModuleOut(ctx, hiddenApiSubDir, "all-flags.csv")
- buildRuleToGenerateHiddenApiFlags(ctx, "modularHiddenApiAllFlags", "modular hiddenapi all flags", outputPath, stubFlagsCSV, annotationFlagsCSV, input.FlagFilesByCategory, nil, removedDexSignatures)
+ allFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "all-flags.csv")
+ buildRuleToGenerateHiddenApiFlags(ctx, "modularHiddenApiAllFlags", "modular hiddenapi all flags", allFlagsCSV, stubFlagsCSV, android.Paths{annotationFlagsCSV}, input.FlagFilesByCategory, nil, removedDexSignatures)
+
+ // Encode the flags into the boot dex files.
+ encodedBootDexJarsByModule := map[string]android.Path{}
+ outputDir := android.PathForModuleOut(ctx, "hiddenapi-modular/encoded").OutputPath
+ for _, name := range android.SortedStringKeys(bootDexInfoByModule) {
+ bootDexInfo := bootDexInfoByModule[name]
+ unencodedDex := bootDexInfo.path
+ encodedDex := hiddenAPIEncodeDex(ctx, unencodedDex, allFlagsCSV, bootDexInfo.uncompressDex, outputDir)
+ encodedBootDexJarsByModule[name] = encodedDex
+ }
// Store the paths in the info for use by other modules and sdk snapshot generation.
- output := HiddenAPIFlagOutput{
- StubFlagsPath: stubFlagsCSV,
- AnnotationFlagsPath: annotationFlagsCSV,
- MetadataPath: metadataCSV,
- IndexPath: indexCSV,
- AllFlagsPath: outputPath,
+ output := HiddenAPIOutput{
+ HiddenAPIFlagOutput: HiddenAPIFlagOutput{
+ StubFlagsPath: stubFlagsCSV,
+ AnnotationFlagsPath: annotationFlagsCSV,
+ MetadataPath: metadataCSV,
+ IndexPath: indexCSV,
+ AllFlagsPath: allFlagsCSV,
+ },
+ EncodedBootDexFilesByModule: encodedBootDexJarsByModule,
}
return &output
}
@@ -747,37 +791,15 @@
}
// extractBootDexJarsFromModules extracts the boot dex jars from the supplied modules.
-func extractBootDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) android.Paths {
- bootDexJars := android.Paths{}
+func extractBootDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule {
+ bootDexJars := bootDexJarByModule{}
for _, module := range contents {
hiddenAPIModule := hiddenAPIModuleFromModule(ctx, module)
if hiddenAPIModule == nil {
continue
}
- bootDexJar := hiddenAPIModule.bootDexJar()
- if bootDexJar == nil {
- if ctx.Config().AlwaysUsePrebuiltSdks() {
- // 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.
- fake := android.PathForModuleOut(ctx, "fake/boot-dex/%s.jar", module.Name())
-
- // Create an error rule that pretends to create the output file but will actually fail if it
- // is run.
- ctx.Build(pctx, android.BuildParams{
- Rule: android.ErrorRule,
- Output: fake,
- Args: map[string]string{
- "error": fmt.Sprintf("missing dependencies: boot dex jar for %s", module),
- },
- })
- bootDexJars = append(bootDexJars, fake)
- } else {
- ctx.ModuleErrorf("module %s does not provide a dex jar", module)
- }
- } else {
- bootDexJars = append(bootDexJars, bootDexJar)
- }
+ bootDexJar := retrieveBootDexJarFromHiddenAPIModule(ctx, hiddenAPIModule)
+ bootDexJars.addPath(module, bootDexJar)
}
return bootDexJars
}
@@ -794,6 +816,60 @@
return nil
}
+// bootDexInfo encapsulates both the path and uncompressDex status retrieved from a hiddenAPIModule.
+type bootDexInfo struct {
+ // The path to the dex jar that has not had hidden API flags encoded into it.
+ path android.Path
+
+ // Indicates whether the dex jar needs uncompressing before encoding.
+ uncompressDex bool
+}
+
+// bootDexInfoByModule is a map from module name (as returned by module.Name()) to the boot dex
+// path (as returned by hiddenAPIModule.bootDexJar()) and the uncompressDex flag.
+type bootDexInfoByModule map[string]bootDexInfo
+
+// bootDexJars returns the boot dex jar paths sorted by their keys.
+func (b bootDexInfoByModule) bootDexJars() android.Paths {
+ paths := android.Paths{}
+ for _, m := range android.SortedStringKeys(b) {
+ paths = append(paths, b[m].path)
+ }
+ return paths
+}
+
+// extractBootDexInfoFromModules extracts the boot dex jar and uncompress dex state from
+// each of the supplied modules which must implement hiddenAPIModule.
+func extractBootDexInfoFromModules(ctx android.ModuleContext, contents []android.Module) bootDexInfoByModule {
+ bootDexJarsByModule := bootDexInfoByModule{}
+ for _, module := range contents {
+ hiddenAPIModule := module.(hiddenAPIModule)
+ bootDexJar := retrieveBootDexJarFromHiddenAPIModule(ctx, hiddenAPIModule)
+ bootDexJarsByModule[module.Name()] = bootDexInfo{
+ path: bootDexJar,
+ uncompressDex: *hiddenAPIModule.uncompressDex(),
+ }
+ }
+
+ return bootDexJarsByModule
+}
+
+// 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.
+func retrieveBootDexJarFromHiddenAPIModule(ctx android.ModuleContext, module hiddenAPIModule) android.Path {
+ bootDexJar := module.bootDexJar()
+ if bootDexJar == nil {
+ fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/boot-dex/%s.jar", module.Name()))
+ bootDexJar = fake
+
+ handleMissingDexBootFile(ctx, module, fake)
+ }
+ return bootDexJar
+}
+
// extractClassesJarsFromModules extracts the class jars from the supplied modules.
func extractClassesJarsFromModules(contents []android.Module) android.Paths {
classesJars := android.Paths{}
@@ -822,6 +898,11 @@
return true
}
+ // Any missing dependency should be allowed.
+ if ctx.Config().AllowMissingDependencies() {
+ return true
+ }
+
// This is called for both platform_bootclasspath and bootclasspath_fragment modules.
//
// A bootclasspath_fragment module should only use the APEX variant of source or prebuilt modules.
diff --git a/java/hiddenapi_monolithic.go b/java/hiddenapi_monolithic.go
index edf4235..52f0770 100644
--- a/java/hiddenapi_monolithic.go
+++ b/java/hiddenapi_monolithic.go
@@ -43,22 +43,45 @@
// The paths to the generated all-flags.csv files.
AllFlagsPaths android.Paths
+
+ // The classes jars from the libraries on the platform bootclasspath.
+ ClassesJars android.Paths
}
// newMonolithicHiddenAPIInfo creates a new MonolithicHiddenAPIInfo from the flagFilesByCategory
// plus information provided by each of the fragments.
-func newMonolithicHiddenAPIInfo(ctx android.ModuleContext, flagFilesByCategory FlagFilesByCategory, fragments []android.Module) MonolithicHiddenAPIInfo {
+func newMonolithicHiddenAPIInfo(ctx android.ModuleContext, flagFilesByCategory FlagFilesByCategory, classpathElements ClasspathElements) MonolithicHiddenAPIInfo {
monolithicInfo := MonolithicHiddenAPIInfo{}
monolithicInfo.FlagsFilesByCategory = flagFilesByCategory
- // Merge all the information from the fragments. The fragments form a DAG so it is possible that
- // this will introduce duplicates so they will be resolved after processing all the fragments.
- for _, fragment := range fragments {
- if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) {
- info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo)
- monolithicInfo.append(&info)
+ // Merge all the information from the classpathElements. The fragments form a DAG so it is possible that
+ // this will introduce duplicates so they will be resolved after processing all the classpathElements.
+ for _, element := range classpathElements {
+ var classesJars android.Paths
+ switch e := element.(type) {
+ case *ClasspathLibraryElement:
+ classesJars = retrieveClassesJarsFromModule(e.Module())
+
+ case *ClasspathFragmentElement:
+ fragment := e.Module()
+ if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) {
+ info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo)
+ monolithicInfo.append(&info)
+
+ // If the bootclasspath fragment actually perform hidden API processing itself then use the
+ // CSV files it provides and do not bother processing the classesJars files. This ensures
+ // consistent behavior between source and prebuilt as prebuilt modules do not provide
+ // classesJars.
+ if info.AllFlagsPath != nil {
+ continue
+ }
+ }
+
+ classesJars = extractClassesJarsFromModules(e.Contents)
}
+
+ monolithicInfo.ClassesJars = append(monolithicInfo.ClassesJars, classesJars...)
}
// Dedup paths.
diff --git a/java/java.go b/java/java.go
index 2bbb5b1..be1ad87 100644
--- a/java/java.go
+++ b/java/java.go
@@ -643,7 +643,7 @@
module.addHostAndDeviceProperties()
- module.initModuleAndImport(&module.ModuleBase)
+ module.initModuleAndImport(module)
android.InitApexModule(module)
android.InitSdkAwareModule(module)
@@ -1309,7 +1309,7 @@
// Get the path of the dex implementation jar from the `deapexer` module.
di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
- if dexOutputPath := di.PrebuiltExportPath(j.BaseModuleName(), ".dexjar"); dexOutputPath != nil {
+ if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(j.BaseModuleName())); dexOutputPath != nil {
j.dexJarFile = dexOutputPath
// Initialize the hiddenapi structure.
@@ -1416,16 +1416,35 @@
if sdkSpec.Kind == android.SdkCore {
return nil
}
- ver, err := sdkSpec.EffectiveVersion(ctx)
- if err != nil {
- return err
- }
- if ver.GreaterThan(sdkVersion) {
- return fmt.Errorf("newer SDK(%v)", ver)
+ if sdkSpec.ApiLevel.GreaterThan(sdkVersion) {
+ return fmt.Errorf("newer SDK(%v)", sdkSpec.ApiLevel)
}
return nil
}
+// requiredFilesFromPrebuiltApexForImport returns information about the files that a java_import or
+// java_sdk_library_import with the specified base module name requires to be exported from a
+// prebuilt_apex/apex_set.
+func requiredFilesFromPrebuiltApexForImport(name string) []string {
+ // Add the dex implementation jar to the set of exported files.
+ return []string{
+ apexRootRelativePathToJavaLib(name),
+ }
+}
+
+// apexRootRelativePathToJavaLib returns the path, relative to the root of the apex's contents, for
+// the java library with the specified name.
+func apexRootRelativePathToJavaLib(name string) string {
+ return filepath.Join("javalib", name+".jar")
+}
+
+var _ android.RequiredFilesFromPrebuiltApex = (*Import)(nil)
+
+func (j *Import) RequiredFilesFromPrebuiltApex(_ android.BaseModuleContext) []string {
+ name := j.BaseModuleName()
+ return requiredFilesFromPrebuiltApexForImport(name)
+}
+
// Add compile time check for interface implementation
var _ android.IDEInfo = (*Import)(nil)
var _ android.IDECustomizedModuleName = (*Import)(nil)
@@ -1473,7 +1492,7 @@
&module.dexer.dexProperties,
)
- module.initModuleAndImport(&module.ModuleBase)
+ module.initModuleAndImport(module)
module.dexProperties.Optimize.EnabledByDefault = false
diff --git a/java/java_test.go b/java/java_test.go
index 78d9ab4..0f9965d 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1409,7 +1409,7 @@
// Test that the errorprone plugins are passed to javac
expectedSubstring := "-Xplugin:ErrorProne"
if !strings.Contains(javac.Args["javacFlags"], expectedSubstring) {
- t.Errorf("expected javacFlags to conain %q, got %q", expectedSubstring, javac.Args["javacFlags"])
+ t.Errorf("expected javacFlags to contain %q, got %q", expectedSubstring, javac.Args["javacFlags"])
}
// Modules with errorprone { enabled: true } will include errorprone checks
@@ -1420,3 +1420,67 @@
t.Errorf("expected errorprone build rule to not exist, but it did")
}
}
+
+func TestErrorproneDisabled(t *testing.T) {
+ bp := `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ errorprone: {
+ enabled: false,
+ },
+ }
+ `
+ ctx := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureMergeEnv(map[string]string{
+ "RUN_ERROR_PRONE": "true",
+ }),
+ ).RunTestWithBp(t, bp)
+
+ javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
+
+ // Test that the errorprone plugins are not passed to javac, like they would
+ // be if enabled was true.
+ expectedSubstring := "-Xplugin:ErrorProne"
+ if strings.Contains(javac.Args["javacFlags"], expectedSubstring) {
+ t.Errorf("expected javacFlags to not contain %q, got %q", expectedSubstring, javac.Args["javacFlags"])
+ }
+
+ // Check that no errorprone build rule is created, like there would be
+ // if enabled was unset and RUN_ERROR_PRONE was true.
+ errorprone := ctx.ModuleForTests("foo", "android_common").MaybeDescription("errorprone")
+ if errorprone.RuleParams.Description != "" {
+ t.Errorf("expected errorprone build rule to not exist, but it did")
+ }
+}
+
+func TestErrorproneEnabledOnlyByEnvironmentVariable(t *testing.T) {
+ bp := `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ }
+ `
+ ctx := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureMergeEnv(map[string]string{
+ "RUN_ERROR_PRONE": "true",
+ }),
+ ).RunTestWithBp(t, bp)
+
+ javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
+ errorprone := ctx.ModuleForTests("foo", "android_common").Description("errorprone")
+
+ // Check that the errorprone plugins are not passed to javac, because they
+ // will instead be passed to the separate errorprone compilation
+ expectedSubstring := "-Xplugin:ErrorProne"
+ if strings.Contains(javac.Args["javacFlags"], expectedSubstring) {
+ t.Errorf("expected javacFlags to not contain %q, got %q", expectedSubstring, javac.Args["javacFlags"])
+ }
+
+ // Check that the errorprone plugin is enabled
+ if !strings.Contains(errorprone.Args["javacFlags"], expectedSubstring) {
+ t.Errorf("expected errorprone to contain %q, got %q", expectedSubstring, javac.Args["javacFlags"])
+ }
+}
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
index 5949edd..8c401a7 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -20,6 +20,7 @@
)
var legacyCorePlatformApiModules = []string{
+ "ArcSettings",
"ahat-test-dump",
"android.car",
"android.test.mock",
@@ -42,6 +43,7 @@
"car-service-test-lib",
"car-service-test-static-lib",
"CertInstaller",
+ "com.qti.media.secureprocessor",
"ConnectivityManagerTest",
"ContactsProvider",
"CorePerfTests",
@@ -120,6 +122,7 @@
"services.usage",
"services.usb",
"Settings-core",
+ "SettingsGoogle",
"SettingsLib",
"SettingsProvider",
"SettingsProviderTest",
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 1a4f9b7..a444de0 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -44,13 +44,9 @@
properties platformBootclasspathProperties
// The apex:module pairs obtained from the configured modules.
- //
- // Currently only for testing.
configuredModules []android.Module
// The apex:module pairs obtained from the fragments.
- //
- // Currently only for testing.
fragments []android.Module
// Path to the monolithic hiddenapi-flags.csv file.
@@ -189,7 +185,8 @@
b.generateClasspathProtoBuildActions(ctx)
- b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
+ bootDexJarByModule := b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
+ buildRuleForBootJarsPackageCheck(ctx, bootDexJarByModule)
// Nothing to do if skipping the dexpreopt of boot image jars.
if SkipDexpreoptBootJars(ctx) {
@@ -201,13 +198,29 @@
// Generate classpaths.proto config
func (b *platformBootclasspathModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) {
+ configuredJars := b.configuredJars(ctx)
// ART and platform boot jars must have a corresponding entry in DEX2OATBOOTCLASSPATH
- classpathJars := configuredJarListToClasspathJars(ctx, b.configuredJars(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
- b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars)
+ classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
+ b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
}
func (b *platformBootclasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
- return b.getImageConfig(ctx).modules
+ // Include all non APEX jars
+ jars := b.getImageConfig(ctx).modules
+
+ // Include jars from APEXes that don't populate their classpath proto config.
+ remainingJars := dexpreopt.GetGlobalConfig(ctx).UpdatableBootJars
+ for _, fragment := range b.fragments {
+ info := ctx.OtherModuleProvider(fragment, ClasspathFragmentProtoContentInfoProvider).(ClasspathFragmentProtoContentInfo)
+ if info.ClasspathFragmentProtoGenerated {
+ remainingJars = remainingJars.RemoveList(info.ClasspathFragmentProtoContents)
+ }
+ }
+ for i := 0; i < remainingJars.Len(); i++ {
+ jars = jars.Append(remainingJars.Apex(i), remainingJars.Jar(i))
+ }
+
+ return jars
}
// checkNonUpdatableModules ensures that the non-updatable modules supplied are not part of an
@@ -258,13 +271,15 @@
}
// generateHiddenAPIBuildActions generates all the hidden API related build rules.
-func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) {
+func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) bootDexJarByModule {
// Save the paths to the monolithic files for retrieval via OutputFiles().
b.hiddenAPIFlagsCSV = hiddenAPISingletonPaths(ctx).flags
b.hiddenAPIIndexCSV = hiddenAPISingletonPaths(ctx).index
b.hiddenAPIMetadataCSV = hiddenAPISingletonPaths(ctx).metadata
+ bootDexJarByModule := extractBootDexJarsFromModules(ctx, modules)
+
// Don't run any hiddenapi rules if UNSAFE_DISABLE_HIDDENAPI_FLAGS=true. This is a performance
// optimization that can be used to reduce the incremental build time but as its name suggests it
// can be unsafe to use, e.g. when the changes affect anything that goes on the bootclasspath.
@@ -276,10 +291,18 @@
Output: path,
})
}
- return
+ return bootDexJarByModule
}
- monolithicInfo := b.createAndProvideMonolithicHiddenAPIInfo(ctx, fragments)
+ // Construct a list of ClasspathElement objects from the modules and fragments.
+ classpathElements := CreateClasspathElements(ctx, modules, fragments)
+
+ monolithicInfo := b.createAndProvideMonolithicHiddenAPIInfo(ctx, classpathElements)
+
+ // Extract the classes jars only from those libraries that do not have corresponding fragments as
+ // the fragments will have already provided the flags that are needed.
+ classesJars := monolithicInfo.ClassesJars
+
// Create the input to pass to ruleToGenerateHiddenAPIStubFlagsFile
input := newHiddenAPIFlagInput()
@@ -291,42 +314,60 @@
input.FlagFilesByCategory = monolithicInfo.FlagsFilesByCategory
// Generate the monolithic stub-flags.csv file.
- bootDexJars := extractBootDexJarsFromModules(ctx, modules)
stubFlags := hiddenAPISingletonPaths(ctx).stubFlags
- rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlags, bootDexJars, input)
+ rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlags, bootDexJarByModule.bootDexJars(), input)
rule.Build("platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags")
- // Extract the classes jars from the contents.
- classesJars := extractClassesJarsFromModules(modules)
-
// Generate the annotation-flags.csv file from all the module annotations.
- annotationFlags := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "annotation-flags.csv")
- buildRuleToGenerateAnnotationFlags(ctx, "monolithic hiddenapi flags", classesJars, stubFlags, annotationFlags)
+ annotationFlags := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "annotation-flags-from-classes.csv")
+ buildRuleToGenerateAnnotationFlags(ctx, "intermediate hidden API flags", classesJars, stubFlags, annotationFlags)
- // Generate the monotlithic hiddenapi-flags.csv file.
+ // Generate the monolithic hiddenapi-flags.csv file.
+ //
+ // Use annotation flags generated directly from the classes jars as well as annotation flag files
+ // provided by prebuilts.
+ allAnnotationFlagFiles := android.Paths{annotationFlags}
+ allAnnotationFlagFiles = append(allAnnotationFlagFiles, monolithicInfo.AnnotationFlagsPaths...)
allFlags := hiddenAPISingletonPaths(ctx).flags
- buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "hiddenapi flags", allFlags, stubFlags, annotationFlags, monolithicInfo.FlagsFilesByCategory, monolithicInfo.AllFlagsPaths, android.OptionalPath{})
+ buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "monolithic hidden API flags", allFlags, stubFlags, allAnnotationFlagFiles, monolithicInfo.FlagsFilesByCategory, monolithicInfo.AllFlagsPaths, android.OptionalPath{})
// Generate an intermediate monolithic hiddenapi-metadata.csv file directly from the annotations
// in the source code.
- intermediateMetadataCSV := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "intermediate-metadata.csv")
- buildRuleToGenerateMetadata(ctx, "monolithic hidden API metadata", classesJars, stubFlags, intermediateMetadataCSV)
+ intermediateMetadataCSV := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "metadata-from-classes.csv")
+ buildRuleToGenerateMetadata(ctx, "intermediate hidden API metadata", classesJars, stubFlags, intermediateMetadataCSV)
- // Reformat the intermediate file to add | quotes just in case that is important for the tools
- // that consume the metadata file.
- // TODO(b/179354495): Investigate whether it is possible to remove this reformatting step.
+ // Generate the monolithic hiddenapi-metadata.csv file.
+ //
+ // Use metadata files generated directly from the classes jars as well as metadata files provided
+ // by prebuilts.
+ //
+ // This has the side effect of ensuring that the output file uses | quotes just in case that is
+ // important for the tools that consume the metadata file.
+ allMetadataFlagFiles := android.Paths{intermediateMetadataCSV}
+ allMetadataFlagFiles = append(allMetadataFlagFiles, monolithicInfo.MetadataPaths...)
metadataCSV := hiddenAPISingletonPaths(ctx).metadata
- b.buildRuleMergeCSV(ctx, "reformat monolithic hidden API metadata", android.Paths{intermediateMetadataCSV}, metadataCSV)
+ b.buildRuleMergeCSV(ctx, "monolithic hidden API metadata", allMetadataFlagFiles, metadataCSV)
- // Generate the monolithic hiddenapi-index.csv file directly from the CSV files in the classes
- // jars.
+ // Generate an intermediate monolithic hiddenapi-index.csv file directly from the CSV files in the
+ // classes jars.
+ intermediateIndexCSV := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "index-from-classes.csv")
+ buildRuleToGenerateIndex(ctx, "intermediate hidden API index", classesJars, intermediateIndexCSV)
+
+ // Generate the monolithic hiddenapi-index.csv file.
+ //
+ // Use index files generated directly from the classes jars as well as index files provided
+ // by prebuilts.
+ allIndexFlagFiles := android.Paths{intermediateIndexCSV}
+ allIndexFlagFiles = append(allIndexFlagFiles, monolithicInfo.IndexPaths...)
indexCSV := hiddenAPISingletonPaths(ctx).index
- buildRuleToGenerateIndex(ctx, "monolithic hidden API index", classesJars, indexCSV)
+ b.buildRuleMergeCSV(ctx, "monolithic hidden API index", allIndexFlagFiles, indexCSV)
+
+ return bootDexJarByModule
}
// createAndProvideMonolithicHiddenAPIInfo creates a MonolithicHiddenAPIInfo and provides it for
// testing.
-func (b *platformBootclasspathModule) createAndProvideMonolithicHiddenAPIInfo(ctx android.ModuleContext, fragments []android.Module) MonolithicHiddenAPIInfo {
+func (b *platformBootclasspathModule) createAndProvideMonolithicHiddenAPIInfo(ctx android.ModuleContext, classpathElements ClasspathElements) MonolithicHiddenAPIInfo {
// Create a temporary input structure in which to collate information provided directly by this
// module, either through properties or direct dependencies.
temporaryInput := newHiddenAPIFlagInput()
@@ -336,7 +377,7 @@
// Create the monolithic info, by starting with the flag files specified on this and then merging
// in information from all the fragment dependencies of this.
- monolithicInfo := newMonolithicHiddenAPIInfo(ctx, temporaryInput.FlagFilesByCategory, fragments)
+ monolithicInfo := newMonolithicHiddenAPIInfo(ctx, temporaryInput.FlagFilesByCategory, classpathElements)
// Store the information for testing.
ctx.SetProvider(MonolithicHiddenAPIInfoProvider, monolithicInfo)
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index 0318a07..1c2a3ae 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -318,11 +318,41 @@
// Make sure that the foo-hiddenapi-annotations.jar is included in the inputs to the rules that
// creates the index.csv file.
platformBootclasspath := result.ModuleForTests("myplatform-bootclasspath", "android_common")
- indexRule := platformBootclasspath.Rule("monolithic_hidden_API_index")
- CheckHiddenAPIRuleInputs(t, `
-.intermediates/bar/android_common/javac/bar.jar
-.intermediates/foo-hiddenapi-annotations/android_common/javac/foo-hiddenapi-annotations.jar
-.intermediates/foo/android_common/javac/foo.jar
-`,
- indexRule)
+
+ var rule android.TestingBuildParams
+
+ // All the intermediate rules use the same inputs.
+ expectedIntermediateInputs := `
+ out/soong/.intermediates/bar/android_common/javac/bar.jar
+ out/soong/.intermediates/foo-hiddenapi-annotations/android_common/javac/foo-hiddenapi-annotations.jar
+ out/soong/.intermediates/foo/android_common/javac/foo.jar
+ `
+
+ // Check flags output.
+ rule = platformBootclasspath.Output("hiddenapi-monolithic/annotation-flags-from-classes.csv")
+ CheckHiddenAPIRuleInputs(t, "intermediate flags", expectedIntermediateInputs, rule)
+
+ rule = platformBootclasspath.Output("out/soong/hiddenapi/hiddenapi-flags.csv")
+ CheckHiddenAPIRuleInputs(t, "monolithic flags", `
+ out/soong/.intermediates/myplatform-bootclasspath/android_common/hiddenapi-monolithic/annotation-flags-from-classes.csv
+ out/soong/hiddenapi/hiddenapi-stub-flags.txt
+ `, rule)
+
+ // Check metadata output.
+ rule = platformBootclasspath.Output("hiddenapi-monolithic/metadata-from-classes.csv")
+ CheckHiddenAPIRuleInputs(t, "intermediate metadata", expectedIntermediateInputs, rule)
+
+ rule = platformBootclasspath.Output("out/soong/hiddenapi/hiddenapi-unsupported.csv")
+ CheckHiddenAPIRuleInputs(t, "monolithic metadata", `
+ out/soong/.intermediates/myplatform-bootclasspath/android_common/hiddenapi-monolithic/metadata-from-classes.csv
+ `, rule)
+
+ // Check index output.
+ rule = platformBootclasspath.Output("hiddenapi-monolithic/index-from-classes.csv")
+ CheckHiddenAPIRuleInputs(t, "intermediate index", expectedIntermediateInputs, rule)
+
+ rule = platformBootclasspath.Output("out/soong/hiddenapi/hiddenapi-index.csv")
+ CheckHiddenAPIRuleInputs(t, "monolithic index", `
+ out/soong/.intermediates/myplatform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
+ `, rule)
}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 9492729..567e292 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -631,9 +631,17 @@
Doctag_files []string `android:"path"`
}
+// commonSdkLibraryAndImportModule defines the interface that must be provided by a module that
+// embeds the commonToSdkLibraryAndImport struct.
+type commonSdkLibraryAndImportModule interface {
+ android.SdkAware
+
+ BaseModuleName() string
+}
+
// Common code between sdk library and sdk library import
type commonToSdkLibraryAndImport struct {
- moduleBase *android.ModuleBase
+ module commonSdkLibraryAndImportModule
scopePaths map[*apiScope]*scopePaths
@@ -648,13 +656,13 @@
EmbeddableSdkLibraryComponent
}
-func (c *commonToSdkLibraryAndImport) initCommon(moduleBase *android.ModuleBase) {
- c.moduleBase = moduleBase
+func (c *commonToSdkLibraryAndImport) initCommon(module commonSdkLibraryAndImportModule) {
+ c.module = module
- moduleBase.AddProperties(&c.commonSdkLibraryProperties)
+ module.AddProperties(&c.commonSdkLibraryProperties)
// Initialize this as an sdk library component.
- c.initSdkLibraryComponent(moduleBase)
+ c.initSdkLibraryComponent(module)
}
func (c *commonToSdkLibraryAndImport) initCommonAfterDefaultsApplied(ctx android.DefaultableHookContext) bool {
@@ -670,41 +678,50 @@
// Only track this sdk library if this can be used as a shared library.
if c.sharedLibrary() {
// Use the name specified in the module definition as the owner.
- c.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.moduleBase.BaseModuleName())
+ c.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.module.BaseModuleName())
}
return true
}
+// uniqueApexVariations provides common implementation of the ApexModule.UniqueApexVariations
+// method.
+func (c *commonToSdkLibraryAndImport) uniqueApexVariations() bool {
+ // A java_sdk_library that is a shared library produces an XML file that makes the shared library
+ // usable from an AndroidManifest.xml's <uses-library> entry. That XML file contains the name of
+ // the APEX and so it needs a unique variation per APEX.
+ return c.sharedLibrary()
+}
+
func (c *commonToSdkLibraryAndImport) generateCommonBuildActions(ctx android.ModuleContext) {
c.doctagPaths = android.PathsForModuleSrc(ctx, c.commonSdkLibraryProperties.Doctag_files)
}
// Module name of the runtime implementation library
func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string {
- return c.moduleBase.BaseModuleName() + ".impl"
+ return c.module.BaseModuleName() + ".impl"
}
// Module name of the XML file for the lib
func (c *commonToSdkLibraryAndImport) xmlPermissionsModuleName() string {
- return c.moduleBase.BaseModuleName() + sdkXmlFileSuffix
+ return c.module.BaseModuleName() + sdkXmlFileSuffix
}
// Name of the java_library module that compiles the stubs source.
func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string {
- return c.namingScheme.stubsLibraryModuleName(apiScope, c.moduleBase.BaseModuleName())
+ baseName := c.module.BaseModuleName()
+ return c.module.SdkMemberComponentName(baseName, func(name string) string {
+ return c.namingScheme.stubsLibraryModuleName(apiScope, name)
+ })
}
// Name of the droidstubs module that generates the stubs source and may also
// generate/check the API.
func (c *commonToSdkLibraryAndImport) stubsSourceModuleName(apiScope *apiScope) string {
- return c.namingScheme.stubsSourceModuleName(apiScope, c.moduleBase.BaseModuleName())
-}
-
-// Name of the droidstubs module that generates/checks the API. Only used if it
-// requires different arts to the stubs source generating module.
-func (c *commonToSdkLibraryAndImport) apiModuleName(apiScope *apiScope) string {
- return c.namingScheme.apiModuleName(apiScope, c.moduleBase.BaseModuleName())
+ baseName := c.module.BaseModuleName()
+ return c.module.SdkMemberComponentName(baseName, func(name string) string {
+ return c.namingScheme.stubsSourceModuleName(apiScope, name)
+ })
}
// The component names for different outputs of the java_sdk_library.
@@ -753,7 +770,7 @@
if scope, ok := scopeByName[scopeName]; ok {
paths := c.findScopePaths(scope)
if paths == nil {
- return nil, fmt.Errorf("%q does not provide api scope %s", c.moduleBase.BaseModuleName(), scopeName)
+ return nil, fmt.Errorf("%q does not provide api scope %s", c.module.BaseModuleName(), scopeName)
}
switch component {
@@ -784,7 +801,7 @@
if c.doctagPaths != nil {
return c.doctagPaths, nil
} else {
- return nil, fmt.Errorf("no doctag_files specified on %s", c.moduleBase.BaseModuleName())
+ return nil, fmt.Errorf("no doctag_files specified on %s", c.module.BaseModuleName())
}
}
return nil, nil
@@ -830,7 +847,7 @@
// If a specific numeric version has been requested then use prebuilt versions of the sdk.
if !sdkVersion.ApiLevel.IsPreview() {
- return PrebuiltJars(ctx, c.moduleBase.BaseModuleName(), sdkVersion)
+ return PrebuiltJars(ctx, c.module.BaseModuleName(), sdkVersion)
}
paths := c.selectScopePaths(ctx, sdkVersion.Kind)
@@ -857,7 +874,7 @@
scopes = append(scopes, s.name)
}
}
- ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.moduleBase.BaseModuleName(), scopes)
+ ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.module.BaseModuleName(), scopes)
return nil
}
@@ -913,7 +930,7 @@
// any app that includes code which depends (directly or indirectly) on the stubs
// library will have the appropriate <uses-library> invocation inserted into its
// manifest if necessary.
- componentProps.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.moduleBase.BaseModuleName())
+ componentProps.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.module.BaseModuleName())
}
return componentProps
@@ -945,8 +962,8 @@
sdkLibraryComponentProperties SdkLibraryComponentProperties
}
-func (e *EmbeddableSdkLibraryComponent) initSdkLibraryComponent(moduleBase *android.ModuleBase) {
- moduleBase.AddProperties(&e.sdkLibraryComponentProperties)
+func (e *EmbeddableSdkLibraryComponent) initSdkLibraryComponent(module android.Module) {
+ module.AddProperties(&e.sdkLibraryComponentProperties)
}
// to satisfy SdkLibraryComponentDependency
@@ -1168,6 +1185,10 @@
module.Library.GenerateAndroidBuildActions(ctx)
}
+ // Collate the components exported by this module. All scope specific modules are exported but
+ // the impl and xml component modules are not.
+ exportedComponents := map[string]struct{}{}
+
// Record the paths to the header jars of the library (stubs and impl).
// When this java_sdk_library is depended upon from others via "libs" property,
// the recorded paths will be returned depending on the link type of the caller.
@@ -1182,8 +1203,14 @@
// Extract information from the dependency. The exact information extracted
// is determined by the nature of the dependency which is determined by the tag.
scopeTag.extractDepInfo(ctx, to, scopePaths)
+
+ exportedComponents[ctx.OtherModuleName(to)] = struct{}{}
}
})
+
+ // Make the set of components exported by this module available for use elsewhere.
+ exportedComponentInfo := android.ExportedComponentsInfo{Components: android.SortedStringKeys(exportedComponents)}
+ ctx.SetProvider(android.ExportedComponentsInfoProvider, exportedComponentInfo)
}
func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {
@@ -1510,6 +1537,7 @@
mctx.CreateModule(DroidstubsFactory, &props)
}
+// Implements android.ApexModule
func (module *SdkLibrary) DepIsInSameApex(mctx android.BaseModuleContext, dep android.Module) bool {
depTag := mctx.OtherModuleDependencyTag(dep)
if depTag == xmlPermissionsFileTag {
@@ -1518,6 +1546,11 @@
return module.Library.DepIsInSameApex(mctx, dep)
}
+// Implements android.ApexModule
+func (module *SdkLibrary) UniqueApexVariations() bool {
+ return module.uniqueApexVariations()
+}
+
// Creates the xml file that publicizes the runtime library
func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) {
props := struct {
@@ -1713,7 +1746,7 @@
module.addHostAndDeviceProperties()
module.AddProperties(&module.sdkLibraryProperties)
- module.initSdkLibraryComponent(&module.ModuleBase)
+ module.initSdkLibraryComponent(module)
module.properties.Installable = proptools.BoolPtr(true)
module.deviceProperties.IsSDKLibrary = true
@@ -1732,8 +1765,6 @@
stubsLibraryModuleName(scope *apiScope, baseName string) string
stubsSourceModuleName(scope *apiScope, baseName string) string
-
- apiModuleName(scope *apiScope, baseName string) string
}
type defaultNamingScheme struct {
@@ -1747,10 +1778,6 @@
return scope.stubsSourceModuleName(baseName)
}
-func (s *defaultNamingScheme) apiModuleName(scope *apiScope, baseName string) string {
- return scope.apiModuleName(baseName)
-}
-
var _ sdkLibraryComponentNamingScheme = (*defaultNamingScheme)(nil)
func moduleStubLinkType(name string) (stub bool, ret sdkLinkType) {
@@ -1784,7 +1811,7 @@
module := &SdkLibrary{}
// Initialize information common between source and prebuilt.
- module.initCommon(&module.ModuleBase)
+ module.initCommon(module)
module.InitSdkLibraryProperties()
android.InitApexModule(module)
@@ -1932,7 +1959,7 @@
module.AddProperties(&module.properties, allScopeProperties)
// Initialize information common between source and prebuilt.
- module.initCommon(&module.ModuleBase)
+ module.initCommon(module)
android.InitPrebuiltModule(module, &[]string{""})
android.InitApexModule(module)
@@ -2078,6 +2105,11 @@
return nil
}
+// Implements android.ApexModule
+func (module *SdkLibraryImport) UniqueApexVariations() bool {
+ return module.uniqueApexVariations()
+}
+
func (module *SdkLibraryImport) OutputFiles(tag string) (android.Paths, error) {
return module.commonOutputFiles(tag)
}
@@ -2144,7 +2176,7 @@
// Get the path of the dex implementation jar from the `deapexer` module.
di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
- if dexOutputPath := di.PrebuiltExportPath(module.BaseModuleName(), ".dexjar"); dexOutputPath != nil {
+ if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(module.BaseModuleName())); dexOutputPath != nil {
module.dexJarFile = dexOutputPath
module.initHiddenAPI(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil)
} else {
@@ -2269,6 +2301,13 @@
}
}
+var _ android.RequiredFilesFromPrebuiltApex = (*SdkLibraryImport)(nil)
+
+func (module *SdkLibraryImport) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
+ name := module.BaseModuleName()
+ return requiredFilesFromPrebuiltApexForImport(name)
+}
+
//
// java_sdk_library_xml
//
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 2520dde..65af953 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -110,7 +110,7 @@
`)
// check the existence of the internal modules
- result.ModuleForTests("foo", "android_common")
+ foo := result.ModuleForTests("foo", "android_common")
result.ModuleForTests(apiScopePublic.stubsLibraryModuleName("foo"), "android_common")
result.ModuleForTests(apiScopeSystem.stubsLibraryModuleName("foo"), "android_common")
result.ModuleForTests(apiScopeTest.stubsLibraryModuleName("foo"), "android_common")
@@ -122,6 +122,17 @@
result.ModuleForTests("foo.api.system.28", "")
result.ModuleForTests("foo.api.test.28", "")
+ exportedComponentsInfo := result.ModuleProvider(foo.Module(), ExportedComponentsInfoProvider).(ExportedComponentsInfo)
+ expectedFooExportedComponents := []string{
+ "foo.stubs",
+ "foo.stubs.source",
+ "foo.stubs.source.system",
+ "foo.stubs.source.test",
+ "foo.stubs.system",
+ "foo.stubs.test",
+ }
+ android.AssertArrayString(t, "foo exported components", expectedFooExportedComponents, exportedComponentsInfo.Components)
+
bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac")
// tests if baz is actually linked to the stubs lib
android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.system.jar")
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index c63a53f..252c615 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -48,13 +48,14 @@
}
func (p *platformSystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- classpathJars := configuredJarListToClasspathJars(ctx, p.configuredJars(ctx), p.classpathType)
- p.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars)
+ configuredJars := p.configuredJars(ctx)
+ classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, p.classpathType)
+ p.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
}
func (p *platformSystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
- global := dexpreopt.GetGlobalConfig(ctx)
- return global.SystemServerJars
+ // TODO(satayev): include any apex jars that don't populate their classpath proto config.
+ return dexpreopt.GetGlobalConfig(ctx).SystemServerJars
}
type SystemServerClasspathModule struct {
@@ -64,6 +65,9 @@
ClasspathFragmentBase
properties systemServerClasspathFragmentProperties
+
+ // Collect the module directory for IDE info in java/jdeps.go.
+ modulePaths []string
}
func (s *SystemServerClasspathModule) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
@@ -91,8 +95,12 @@
ctx.PropertyErrorf("contents", "empty contents are not allowed")
}
- classpathJars := configuredJarListToClasspathJars(ctx, s.configuredJars(ctx), s.classpathType)
- s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars)
+ configuredJars := s.configuredJars(ctx)
+ classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, s.classpathType)
+ s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
+
+ // Collect the module directory for IDE info in java/jdeps.go.
+ s.modulePaths = append(s.modulePaths, ctx.ModuleDir())
}
func (s *SystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
@@ -139,3 +147,9 @@
ctx.AddDependency(module, systemServerClasspathFragmentContentDepTag, name)
}
}
+
+// 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.Paths = append(dpInfo.Paths, s.modulePaths...)
+}
diff --git a/java/testing.go b/java/testing.go
index 1fef337..c3803c8 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -17,6 +17,7 @@
import (
"fmt"
"reflect"
+ "regexp"
"sort"
"strings"
"testing"
@@ -362,6 +363,17 @@
android.AssertDeepEquals(t, fmt.Sprintf("%s modules", "platform-bootclasspath"), expected, pairs)
}
+func CheckClasspathFragmentProtoContentInfoProvider(t *testing.T, result *android.TestResult, generated bool, contents, outputFilename, installDir string) {
+ t.Helper()
+ p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
+ info := result.ModuleProvider(p, ClasspathFragmentProtoContentInfoProvider).(ClasspathFragmentProtoContentInfo)
+
+ android.AssertBoolEquals(t, "classpath proto generated", generated, info.ClasspathFragmentProtoGenerated)
+ android.AssertStringEquals(t, "classpath proto contents", contents, info.ClasspathFragmentProtoContents.String())
+ android.AssertStringEquals(t, "output filepath", outputFilename, info.ClasspathFragmentProtoOutput.Base())
+ android.AssertPathRelativeToTopEquals(t, "install filepath", installDir, info.ClasspathFragmentProtoInstallDir)
+}
+
// ApexNamePairsFromModules returns the apex:module pair for the supplied modules.
func ApexNamePairsFromModules(ctx *android.TestContext, modules []android.Module) []string {
pairs := []string{}
@@ -393,12 +405,20 @@
android.AssertDeepEquals(t, fmt.Sprintf("%s fragments", "platform-bootclasspath"), expected, pairs)
}
-func CheckHiddenAPIRuleInputs(t *testing.T, expected string, hiddenAPIRule android.TestingBuildParams) {
+func CheckHiddenAPIRuleInputs(t *testing.T, message string, expected string, hiddenAPIRule android.TestingBuildParams) {
t.Helper()
- actual := strings.TrimSpace(strings.Join(android.NormalizePathsForTesting(hiddenAPIRule.Implicits), "\n"))
- expected = strings.TrimSpace(expected)
+ inputs := android.Paths{}
+ if hiddenAPIRule.Input != nil {
+ inputs = append(inputs, hiddenAPIRule.Input)
+ }
+ inputs = append(inputs, hiddenAPIRule.Inputs...)
+ inputs = append(inputs, hiddenAPIRule.Implicits...)
+ inputs = android.SortedUniquePaths(inputs)
+ actual := strings.TrimSpace(strings.Join(inputs.RelativeToTop().Strings(), "\n"))
+ re := regexp.MustCompile(`\n\s+`)
+ expected = strings.TrimSpace(re.ReplaceAllString(expected, "\n"))
if actual != expected {
- t.Errorf("Expected hiddenapi rule inputs:\n%s\nactual inputs:\n%s", expected, actual)
+ t.Errorf("Expected hiddenapi rule inputs - %s:\n%s\nactual inputs:\n%s", message, expected, actual)
}
}
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index f2ab6a1..a458cba 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -40,6 +40,7 @@
}
`, apex, fragment)),
android.FixtureAddFile("frameworks/base/config/boot-profile.txt", nil),
+ android.FixtureAddFile("build/soong/scripts/check_boot_jars/package_allowed_list.txt", nil),
)
}
@@ -173,9 +174,29 @@
.intermediates/mybootlib/android_common/javac/mybootlib.jar -> java/mybootlib.jar
`),
snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
+
+ // Check the behavior of the snapshot without the source.
+ snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
+ // Make sure that the boot jars package check rule includes the dex jar retrieved from the prebuilt apex.
+ checkBootJarsPackageCheckRule(t, result, "out/soong/.intermediates/prebuilts/apex/com.android.art.deapexer/android_common/deapexer/javalib/mybootlib.jar")
+ }),
+
snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
)
+
+ // Make sure that the boot jars package check rule includes the dex jar created from the source.
+ checkBootJarsPackageCheckRule(t, result, "out/soong/.intermediates/mybootlib/android_common_apex10000/aligned/mybootlib.jar")
+}
+
+// checkBootJarsPackageCheckRule checks that the supplied module is an input to the boot jars
+// package check rule.
+func checkBootJarsPackageCheckRule(t *testing.T, result *android.TestResult, expectedModule string) {
+ platformBcp := result.ModuleForTests("platform-bootclasspath", "android_common")
+ bootJarsCheckRule := platformBcp.Rule("boot_jars_package_check")
+ command := bootJarsCheckRule.RuleParams.Command
+ expectedCommandArgs := " out/soong/host/linux-x86/bin/dexdump build/soong/scripts/check_boot_jars/package_allowed_list.txt " + expectedModule + " &&"
+ android.AssertStringDoesContain(t, "boot jars package check", command, expectedCommandArgs)
}
func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) {
@@ -252,7 +273,7 @@
name: "myothersdklibrary",
apex_available: ["myapex"],
srcs: ["Test.java"],
- shared_library: false,
+ compile_dex: true,
public: {enabled: true},
min_sdk_version: "2",
permitted_packages: ["myothersdklibrary"],
@@ -262,7 +283,7 @@
name: "mycoreplatform",
apex_available: ["myapex"],
srcs: ["Test.java"],
- shared_library: false,
+ compile_dex: true,
public: {enabled: true},
min_sdk_version: "2",
}
@@ -313,7 +334,8 @@
prefer: false,
visibility: ["//visibility:public"],
apex_available: ["myapex"],
- shared_library: false,
+ shared_library: true,
+ compile_dex: true,
public: {
jars: ["sdk_library/public/myothersdklibrary-stubs.jar"],
stub_srcs: ["sdk_library/public/myothersdklibrary_stub_sources"],
@@ -343,7 +365,8 @@
prefer: false,
visibility: ["//visibility:public"],
apex_available: ["myapex"],
- shared_library: false,
+ shared_library: true,
+ compile_dex: true,
public: {
jars: ["sdk_library/public/mycoreplatform-stubs.jar"],
stub_srcs: ["sdk_library/public/mycoreplatform_stub_sources"],
@@ -393,7 +416,8 @@
sdk_member_name: "myothersdklibrary",
visibility: ["//visibility:public"],
apex_available: ["myapex"],
- shared_library: false,
+ shared_library: true,
+ compile_dex: true,
public: {
jars: ["sdk_library/public/myothersdklibrary-stubs.jar"],
stub_srcs: ["sdk_library/public/myothersdklibrary_stub_sources"],
@@ -423,7 +447,8 @@
sdk_member_name: "mycoreplatform",
visibility: ["//visibility:public"],
apex_available: ["myapex"],
- shared_library: false,
+ shared_library: true,
+ compile_dex: true,
public: {
jars: ["sdk_library/public/mycoreplatform-stubs.jar"],
stub_srcs: ["sdk_library/public/mycoreplatform_stub_sources"],
@@ -463,6 +488,149 @@
.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
`),
snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
+ snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
+ module := result.ModuleForTests("platform-bootclasspath", "android_common")
+ var rule android.TestingBuildParams
+ rule = module.Output("out/soong/hiddenapi/hiddenapi-flags.csv")
+ java.CheckHiddenAPIRuleInputs(t, "monolithic flags", `
+ out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/annotation-flags-from-classes.csv
+ out/soong/hiddenapi/hiddenapi-stub-flags.txt
+ snapshot/hiddenapi/annotation-flags.csv
+ `, rule)
+
+ rule = module.Output("out/soong/hiddenapi/hiddenapi-unsupported.csv")
+ java.CheckHiddenAPIRuleInputs(t, "monolithic metadata", `
+ out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/metadata-from-classes.csv
+ snapshot/hiddenapi/metadata.csv
+ `, rule)
+
+ rule = module.Output("out/soong/hiddenapi/hiddenapi-index.csv")
+ java.CheckHiddenAPIRuleInputs(t, "monolithic index", `
+ out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
+ snapshot/hiddenapi/index.csv
+ `, rule)
+ }),
+ snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
+ snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
+ )
+}
+
+// TestSnapshotWithBootClasspathFragment_Fragments makes sure that the fragments property of a
+// bootclasspath_fragment is correctly output to the sdk snapshot.
+func TestSnapshotWithBootClasspathFragment_Fragments(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ java.PrepareForTestWithJavaDefaultModules,
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ java.FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary"),
+ prepareForSdkTestWithApex,
+
+ // Some additional files needed for the myotherapex.
+ android.FixtureMergeMockFs(android.MockFS{
+ "system/sepolicy/apex/myotherapex-file_contexts": nil,
+ "myotherapex/apex_manifest.json": nil,
+ "myotherapex/Test.java": nil,
+ }),
+
+ android.FixtureAddTextFile("myotherapex/Android.bp", `
+ apex {
+ name: "myotherapex",
+ key: "myapex.key",
+ min_sdk_version: "2",
+ bootclasspath_fragments: ["myotherbootclasspathfragment"],
+ }
+
+ bootclasspath_fragment {
+ name: "myotherbootclasspathfragment",
+ apex_available: ["myotherapex"],
+ contents: [
+ "myotherlib",
+ ],
+ }
+
+ java_library {
+ name: "myotherlib",
+ apex_available: ["myotherapex"],
+ srcs: ["Test.java"],
+ min_sdk_version: "2",
+ permitted_packages: ["myothersdklibrary"],
+ compile_dex: true,
+ }
+ `),
+
+ android.FixtureWithRootAndroidBp(`
+ sdk {
+ name: "mysdk",
+ bootclasspath_fragments: ["mybootclasspathfragment"],
+ }
+
+ bootclasspath_fragment {
+ name: "mybootclasspathfragment",
+ contents: [
+ "mysdklibrary",
+ ],
+ fragments: [
+ {
+ apex: "myotherapex",
+ module: "myotherbootclasspathfragment"
+ },
+ ],
+ }
+
+ java_sdk_library {
+ name: "mysdklibrary",
+ srcs: ["Test.java"],
+ shared_library: false,
+ public: {enabled: true},
+ min_sdk_version: "2",
+ }
+ `),
+ ).RunTest(t)
+
+ // A preparer to update the test fixture used when processing an unpackage snapshot.
+ preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment")
+
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_bootclasspath_fragment {
+ name: "mybootclasspathfragment",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ contents: ["mysdklibrary"],
+ fragments: [
+ {
+ apex: "myotherapex",
+ module: "myotherbootclasspathfragment",
+ },
+ ],
+ hidden_api: {
+ stub_flags: "hiddenapi/stub-flags.csv",
+ annotation_flags: "hiddenapi/annotation-flags.csv",
+ metadata: "hiddenapi/metadata.csv",
+ index: "hiddenapi/index.csv",
+ all_flags: "hiddenapi/all-flags.csv",
+ },
+}
+
+java_sdk_library_import {
+ name: "mysdklibrary",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ shared_library: false,
+ public: {
+ jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
+ stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
+ current_api: "sdk_library/public/mysdklibrary.txt",
+ removed_api: "sdk_library/public/mysdklibrary-removed.txt",
+ sdk_version: "current",
+ },
+}
+ `),
+ snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
)
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 6f769a3..a2cfe6d 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -663,16 +663,28 @@
}
func TestSnapshotWithJavaSystemModules(t *testing.T) {
- result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_header_libs: ["exported-system-module"],
+ java_sdk_libs: ["myjavalib"],
java_system_modules: ["my-system-modules"],
}
+ java_sdk_library {
+ name: "myjavalib",
+ apex_available: ["//apex_available:anyapex"],
+ srcs: ["Test.java"],
+ sdk_version: "current",
+ shared_library: false,
+ public: {
+ enabled: true,
+ },
+ }
+
java_system_modules {
name: "my-system-modules",
- libs: ["system-module", "exported-system-module"],
+ libs: ["system-module", "exported-system-module", "myjavalib.stubs"],
}
java_library {
@@ -710,6 +722,21 @@
jars: ["java/system-module.jar"],
}
+java_sdk_library_import {
+ name: "myjavalib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:anyapex"],
+ shared_library: false,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "current",
+ },
+}
+
java_system_modules_import {
name: "my-system-modules",
prefer: false,
@@ -717,6 +744,7 @@
libs: [
"mysdk_system-module",
"exported-system-module",
+ "myjavalib.stubs",
],
}
`),
@@ -739,6 +767,21 @@
jars: ["java/system-module.jar"],
}
+java_sdk_library_import {
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:anyapex"],
+ shared_library: false,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "current",
+ },
+}
+
java_system_modules_import {
name: "mysdk_my-system-modules@current",
sdk_member_name: "my-system-modules",
@@ -746,6 +789,7 @@
libs: [
"mysdk_system-module@current",
"mysdk_exported-system-module@current",
+ "mysdk_myjavalib.stubs@current",
],
}
@@ -753,12 +797,16 @@
name: "mysdk@current",
visibility: ["//visibility:public"],
java_header_libs: ["mysdk_exported-system-module@current"],
+ java_sdk_libs: ["mysdk_myjavalib@current"],
java_system_modules: ["mysdk_my-system-modules@current"],
}
`),
checkAllCopyRules(`
.intermediates/exported-system-module/android_common/turbine-combined/exported-system-module.jar -> java/exported-system-module.jar
.intermediates/system-module/android_common/turbine-combined/system-module.jar -> java/system-module.jar
+.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
`),
)
}
@@ -1085,7 +1133,14 @@
checkMergeZips(
".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
".intermediates/mysdk/common_os/tmp/sdk_library/system/myjavalib_stub_sources.zip",
- ".intermediates/mysdk/common_os/tmp/sdk_library/test/myjavalib_stub_sources.zip"),
+ ".intermediates/mysdk/common_os/tmp/sdk_library/test/myjavalib_stub_sources.zip",
+ ),
+ snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
+ // Make sure that the name of the child modules created by a versioned java_sdk_library_import
+ // module is correct, i.e. the suffix is added before the version and not after.
+ result.Module("mysdk_myjavalib.stubs@current", "android_common")
+ result.Module("mysdk_myjavalib.stubs.source@current", "android_common")
+ }),
)
}
diff --git a/sdk/update.go b/sdk/update.go
index 36b564f..3f61339 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -22,7 +22,6 @@
"android/soong/apex"
"android/soong/cc"
-
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -114,8 +113,16 @@
gc.indentLevel--
}
-func (gc *generatedContents) Printfln(format string, args ...interface{}) {
- fmt.Fprintf(&(gc.content), strings.Repeat(" ", gc.indentLevel)+format+"\n", args...)
+// IndentedPrintf will add spaces to indent the line to the appropriate level before printing the
+// arguments.
+func (gc *generatedContents) IndentedPrintf(format string, args ...interface{}) {
+ fmt.Fprintf(&(gc.content), strings.Repeat(" ", gc.indentLevel)+format, args...)
+}
+
+// UnindentedPrintf does not add spaces to indent the line to the appropriate level before printing
+// the arguments.
+func (gc *generatedContents) UnindentedPrintf(format string, args ...interface{}) {
+ fmt.Fprintf(&(gc.content), format, args...)
}
func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderContext, implicits android.Paths) {
@@ -140,8 +147,8 @@
// Collect all the members.
//
-// Updates the sdk module with a list of sdkMemberVariantDeps and details as to which multilibs
-// (32/64/both) are used by this sdk variant.
+// Updates the sdk module with a list of sdkMemberVariantDep instances and details as to which
+// multilibs (32/64/both) are used by this sdk variant.
func (s *sdk) collectMembers(ctx android.ModuleContext) {
s.multilibUsages = multilibNone
ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
@@ -157,8 +164,15 @@
// Keep track of which multilib variants are used by the sdk.
s.multilibUsages = s.multilibUsages.addArchType(child.Target().Arch.ArchType)
+ var exportedComponentsInfo android.ExportedComponentsInfo
+ if ctx.OtherModuleHasProvider(child, android.ExportedComponentsInfoProvider) {
+ exportedComponentsInfo = ctx.OtherModuleProvider(child, android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo)
+ }
+
export := memberTag.ExportMember()
- s.memberVariantDeps = append(s.memberVariantDeps, sdkMemberVariantDep{s, memberType, child.(android.SdkAware), export})
+ s.memberVariantDeps = append(s.memberVariantDeps, sdkMemberVariantDep{
+ s, memberType, child.(android.SdkAware), export, exportedComponentsInfo,
+ })
// Recurse down into the member's dependencies as it may have dependencies that need to be
// automatically added to the sdk.
@@ -245,26 +259,41 @@
// the contents (header files, stub libraries, etc) into the zip file.
func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) android.OutputPath {
- allMembersByName := make(map[string]struct{})
- exportedMembersByName := make(map[string]struct{})
+ // Aggregate all the sdkMemberVariantDep instances from all the sdk variants.
hasLicenses := false
var memberVariantDeps []sdkMemberVariantDep
for _, sdkVariant := range sdkVariants {
memberVariantDeps = append(memberVariantDeps, sdkVariant.memberVariantDeps...)
+ }
- // Record the names of all the members, both explicitly specified and implicitly
- // included.
- for _, memberVariantDep := range sdkVariant.memberVariantDeps {
- name := memberVariantDep.variant.Name()
- allMembersByName[name] = struct{}{}
+ // Filter out any sdkMemberVariantDep that is a component of another.
+ memberVariantDeps = filterOutComponents(ctx, memberVariantDeps)
- if memberVariantDep.export {
- exportedMembersByName[name] = struct{}{}
- }
+ // Record the names of all the members, both explicitly specified and implicitly
+ // included.
+ allMembersByName := make(map[string]struct{})
+ exportedMembersByName := make(map[string]struct{})
- if memberVariantDep.memberType == android.LicenseModuleSdkMemberType {
- hasLicenses = true
- }
+ addMember := func(name string, export bool) {
+ allMembersByName[name] = struct{}{}
+ if export {
+ exportedMembersByName[name] = struct{}{}
+ }
+ }
+
+ for _, memberVariantDep := range memberVariantDeps {
+ name := memberVariantDep.variant.Name()
+ export := memberVariantDep.export
+
+ addMember(name, export)
+
+ // Add any components provided by the module.
+ for _, component := range memberVariantDep.exportedComponentsInfo.Components {
+ addMember(component, export)
+ }
+
+ if memberVariantDep.memberType == android.LicenseModuleSdkMemberType {
+ hasLicenses = true
}
}
@@ -423,6 +452,47 @@
return outputZipFile
}
+// filterOutComponents removes any item from the deps list that is a component of another item in
+// the deps list, e.g. if the deps list contains "foo" and "foo.stubs" which is component of "foo"
+// then it will remove "foo.stubs" from the deps.
+func filterOutComponents(ctx android.ModuleContext, deps []sdkMemberVariantDep) []sdkMemberVariantDep {
+ // Collate the set of components that all the modules added to the sdk provide.
+ components := map[string]*sdkMemberVariantDep{}
+ for i, _ := range deps {
+ dep := &deps[i]
+ for _, c := range dep.exportedComponentsInfo.Components {
+ components[c] = dep
+ }
+ }
+
+ // If no module provides components then return the input deps unfiltered.
+ if len(components) == 0 {
+ return deps
+ }
+
+ filtered := make([]sdkMemberVariantDep, 0, len(deps))
+ for _, dep := range deps {
+ name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(dep.variant))
+ if owner, ok := components[name]; ok {
+ // This is a component of another module that is a member of the sdk.
+
+ // If the component is exported but the owning module is not then the configuration is not
+ // supported.
+ if dep.export && !owner.export {
+ ctx.ModuleErrorf("Module %s is internal to the SDK but provides component %s which is used outside the SDK")
+ continue
+ }
+
+ // This module must not be added to the list of members of the sdk as that would result in a
+ // duplicate module in the sdk snapshot.
+ continue
+ }
+
+ filtered = append(filtered, dep)
+ }
+ return filtered
+}
+
// addSnapshotModule adds the sdk_snapshot/module_exports_snapshot module to the builder.
func (s *sdk) addSnapshotModule(ctx android.ModuleContext, builder *snapshotBuilder, sdkVariants []*sdk, memberVariantDeps []sdkMemberVariantDep) {
bpFile := builder.bpFile
@@ -742,13 +812,13 @@
}
func generateFilteredBpContents(contents *generatedContents, bpFile *bpFile, moduleFilter func(module *bpModule) bool) {
- contents.Printfln("// This is auto-generated. DO NOT EDIT.")
+ contents.IndentedPrintf("// This is auto-generated. DO NOT EDIT.\n")
for _, bpModule := range bpFile.order {
if moduleFilter(bpModule) {
- contents.Printfln("")
- contents.Printfln("%s {", bpModule.moduleType)
+ contents.IndentedPrintf("\n")
+ contents.IndentedPrintf("%s {\n", bpModule.moduleType)
outputPropertySet(contents, bpModule.bpPropertySet)
- contents.Printfln("}")
+ contents.IndentedPrintf("}\n")
}
}
}
@@ -759,7 +829,7 @@
addComment := func(name string) {
if text, ok := set.comments[name]; ok {
for _, line := range strings.Split(text, "\n") {
- contents.Printfln("// %s", line)
+ contents.IndentedPrintf("// %s\n", line)
}
}
}
@@ -776,29 +846,8 @@
}
addComment(name)
- switch v := value.(type) {
- case []string:
- length := len(v)
- if length > 1 {
- contents.Printfln("%s: [", name)
- contents.Indent()
- for i := 0; i < length; i = i + 1 {
- contents.Printfln("%q,", v[i])
- }
- contents.Dedent()
- contents.Printfln("],")
- } else if length == 0 {
- contents.Printfln("%s: [],", name)
- } else {
- contents.Printfln("%s: [%q],", name, v[0])
- }
-
- case bool:
- contents.Printfln("%s: %t,", name, v)
-
- default:
- contents.Printfln("%s: %q,", name, value)
- }
+ reflectValue := reflect.ValueOf(value)
+ outputNamedValue(contents, name, reflectValue)
}
for _, name := range set.order {
@@ -808,15 +857,94 @@
switch v := value.(type) {
case *bpPropertySet:
addComment(name)
- contents.Printfln("%s: {", name)
+ contents.IndentedPrintf("%s: {\n", name)
outputPropertySet(contents, v)
- contents.Printfln("},")
+ contents.IndentedPrintf("},\n")
}
}
contents.Dedent()
}
+// outputNamedValue outputs a value that has an associated name. The name will be indented, followed
+// by the value and then followed by a , and a newline.
+func outputNamedValue(contents *generatedContents, name string, value reflect.Value) {
+ contents.IndentedPrintf("%s: ", name)
+ outputUnnamedValue(contents, value)
+ contents.UnindentedPrintf(",\n")
+}
+
+// outputUnnamedValue outputs a single value. The value is not indented and is not followed by
+// either a , or a newline. With multi-line values, e.g. slices, all but the first line will be
+// indented and all but the last line will end with a newline.
+func outputUnnamedValue(contents *generatedContents, value reflect.Value) {
+ valueType := value.Type()
+ switch valueType.Kind() {
+ case reflect.Bool:
+ contents.UnindentedPrintf("%t", value.Bool())
+
+ case reflect.String:
+ contents.UnindentedPrintf("%q", value)
+
+ case reflect.Ptr:
+ outputUnnamedValue(contents, value.Elem())
+
+ case reflect.Slice:
+ length := value.Len()
+ if length == 0 {
+ contents.UnindentedPrintf("[]")
+ } else {
+ firstValue := value.Index(0)
+ if length == 1 && !multiLineValue(firstValue) {
+ contents.UnindentedPrintf("[")
+ outputUnnamedValue(contents, firstValue)
+ contents.UnindentedPrintf("]")
+ } else {
+ contents.UnindentedPrintf("[\n")
+ contents.Indent()
+ for i := 0; i < length; i++ {
+ itemValue := value.Index(i)
+ contents.IndentedPrintf("")
+ outputUnnamedValue(contents, itemValue)
+ contents.UnindentedPrintf(",\n")
+ }
+ contents.Dedent()
+ contents.IndentedPrintf("]")
+ }
+ }
+
+ case reflect.Struct:
+ // Avoid unlimited recursion by requiring every structure to implement android.BpPrintable.
+ v := value.Interface()
+ if _, ok := v.(android.BpPrintable); !ok {
+ panic(fmt.Errorf("property value %#v of type %T does not implement android.BpPrintable", v, v))
+ }
+ contents.UnindentedPrintf("{\n")
+ contents.Indent()
+ for f := 0; f < valueType.NumField(); f++ {
+ fieldType := valueType.Field(f)
+ if fieldType.Anonymous {
+ continue
+ }
+ fieldValue := value.Field(f)
+ fieldName := fieldType.Name
+ propertyName := proptools.PropertyNameForField(fieldName)
+ outputNamedValue(contents, propertyName, fieldValue)
+ }
+ contents.Dedent()
+ contents.IndentedPrintf("}")
+
+ default:
+ panic(fmt.Errorf("Unknown type: %T of value %#v", value, value))
+ }
+}
+
+// multiLineValue returns true if the supplied value may require multiple lines in the output.
+func multiLineValue(value reflect.Value) bool {
+ kind := value.Kind()
+ return kind == reflect.Slice || kind == reflect.Struct
+}
+
func (s *sdk) GetAndroidBpContentsForTests() string {
contents := &generatedContents{}
generateBpContents(contents, s.builderForTests.bpFile)
@@ -1086,9 +1214,18 @@
type sdkMemberVariantDep struct {
// The sdk variant that depends (possibly indirectly) on the member variant.
sdkVariant *sdk
+
+ // The type of sdk member the variant is to be treated as.
memberType android.SdkMemberType
- variant android.SdkAware
- export bool
+
+ // The variant that is added to the sdk.
+ variant android.SdkAware
+
+ // True if the member should be exported, i.e. accessible, from outside the sdk.
+ export bool
+
+ // The names of additional component modules provided by the variant.
+ exportedComponentsInfo android.ExportedComponentsInfo
}
var _ android.SdkMember = (*sdkMember)(nil)
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index f1c2d0d..a29d4c3 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -145,6 +145,9 @@
// If set to true, allow this module to be dexed and installed on devices.
Installable *bool
+ // Make this module available when building for ramdisk
+ Ramdisk_available *bool
+
// Make this module available when building for recovery
Recovery_available *bool
@@ -396,6 +399,7 @@
Recovery_available *bool
Vendor_available *bool
Product_available *bool
+ Ramdisk_available *bool
Host_supported *bool
Apex_available []string
Min_sdk_version *string
@@ -475,6 +479,7 @@
ccProps.Recovery_available = m.properties.Recovery_available
ccProps.Vendor_available = m.properties.Vendor_available
ccProps.Product_available = m.properties.Product_available
+ ccProps.Ramdisk_available = m.properties.Ramdisk_available
ccProps.Host_supported = m.properties.Host_supported
ccProps.Apex_available = m.ApexProperties.Apex_available
ccProps.Min_sdk_version = m.properties.Cpp.Min_sdk_version
diff --git a/ui/build/build.go b/ui/build/build.go
index 8f050d9..1187aa2 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -274,6 +274,11 @@
// Return early, if we're using Soong as solely the generator of BUILD files.
return
}
+
+ if config.bazelBuildMode() == generateJsonModuleGraph {
+ // Return early, if we're using Soong as solely the generator of the JSON module graph
+ return
+ }
}
if what&RunKati != 0 {
diff --git a/ui/build/config.go b/ui/build/config.go
index 220e734..4806721 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -108,6 +108,9 @@
// Only generate build files (in a subdirectory of the out directory) and exit.
generateBuildFiles
+ // Only generate the Soong json module graph for use with jq, and exit.
+ generateJsonModuleGraph
+
// Generate synthetic build files and incorporate these files into a build which
// partially uses Bazel. Build metadata may come from Android.bp or BUILD files.
mixedBuild
@@ -573,6 +576,9 @@
} else if arg == "--skip-ninja" {
c.skipNinja = true
} else if arg == "--skip-make" {
+ // TODO(ccross): deprecate this, it has confusing behaviors. It doesn't run kati,
+ // but it does run a Kati ninja file if the .kati_enabled marker file was created
+ // by a previous build.
c.skipConfig = true
c.skipKati = true
} else if arg == "--skip-kati" {
@@ -581,6 +587,8 @@
} else if arg == "--soong-only" {
c.skipKati = true
c.skipKatiNinja = true
+ } else if arg == "--skip-config" {
+ c.skipConfig = true
} else if arg == "--skip-soong-tests" {
c.skipSoongTests = true
} else if len(arg) > 0 && arg[0] == '-' {
@@ -931,6 +939,8 @@
return mixedBuild
} else if c.Environment().IsEnvTrue("GENERATE_BAZEL_FILES") {
return generateBuildFiles
+ } else if v, ok := c.Environment().Get("SOONG_DUMP_JSON_MODULE_GRAPH"); ok && v != "" {
+ return generateJsonModuleGraph
} else {
return noBazel
}
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 19a47ae..a40457f 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -333,8 +333,9 @@
}
func shouldCollectBuildSoongMetrics(config Config) bool {
- // Do not collect metrics protobuf if the soong_build binary ran as the bp2build converter.
- return config.bazelBuildMode() != generateBuildFiles
+ // Do not collect metrics protobuf if the soong_build binary ran as the
+ // bp2build converter or the JSON graph dump.
+ return config.bazelBuildMode() != generateBuildFiles && config.bazelBuildMode() != generateJsonModuleGraph
}
func loadSoongBuildMetrics(ctx Context, config Config) *soong_metrics_proto.SoongBuildMetrics {