Merge "Expose imageLocationsOnDevice as well as imageLocationsOnHost"
diff --git a/Android.bp b/Android.bp
index 8f7f3e2..45e661e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -92,7 +92,7 @@
}
cc_genrule {
- name: "host_bionic_linker_flags",
+ name: "host_bionic_linker_script",
host_supported: true,
device_supported: false,
target: {
@@ -107,9 +107,9 @@
},
},
tools: ["extract_linker"],
- cmd: "$(location) -f $(out) $(in)",
+ cmd: "$(location) -T $(out) $(in)",
srcs: [":linker"],
- out: ["linker.flags"],
+ out: ["linker.script"],
}
// Instantiate the dex_bootjars singleton module.
diff --git a/OWNERS b/OWNERS
index e851bf7..bbfd011 100644
--- a/OWNERS
+++ b/OWNERS
@@ -9,7 +9,6 @@
eakammer@google.com
jingwen@google.com
joeo@google.com
-jungjw@google.com
lberki@google.com
ruperts@google.com
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/bazel.go b/android/bazel.go
index b9d7070..992d8aa 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -218,11 +218,7 @@
// Per-module denylist to opt modules out of mixed builds. Such modules will
// still be generated via bp2build.
- mixedBuildsDisabledList = []string{
- "libc", // b/190211183, missing libbionic_Slibdl_Sliblibdl_Ubp2build_Ucc_Ulibrary_Ushared.so
- "libdl", // b/190211183, missing libbionic_Slinker_Slibld-android_Ubp2build_Ucc_Ulibrary_Ushared.so
- "libdl_android", // b/190211183, missing libbionic_Slinker_Slibld-android_Ubp2build_Ucc_Ulibrary_Ushared.so
- }
+ mixedBuildsDisabledList = []string{}
// Used for quicker lookups
bp2buildModuleDoNotConvert = map[string]bool{}
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index e4bbe64..c6364af 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -205,7 +205,7 @@
func NewBazelContext(c *config) (BazelContext, error) {
// TODO(cparsons): Assess USE_BAZEL=1 instead once "mixed Soong/Bazel builds"
// are production ready.
- if c.Getenv("USE_BAZEL_ANALYSIS") != "1" {
+ if !c.IsEnvTrue("USE_BAZEL_ANALYSIS") {
return noopBazelContext{}, nil
}
@@ -542,10 +542,13 @@
platform_name = build_options(target)["//command_line_option:platforms"][0].name
if platform_name == "host":
return "HOST"
- elif not platform_name.startswith("android_"):
- fail("expected platform name of the form 'android_<arch>', but was " + str(platforms))
+ elif platform_name.startswith("android_"):
+ return platform_name[len("android_"):]
+ elif platform_name.startswith("linux_"):
+ return platform_name[len("linux_"):]
+ else:
+ fail("expected platform name of the form 'android_<arch>' or 'linux_<arch>', but was " + str(platforms))
return "UNKNOWN"
- return platform_name[len("android_"):]
def format(target):
id_string = str(target.label) + "|" + get_arch(target)
@@ -742,8 +745,17 @@
}
rule := NewRuleBuilder(pctx, ctx)
cmd := rule.Command()
- cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ && %s",
- ctx.Config().BazelContext.OutputBase(), buildStatement.Command))
+
+ // cd into Bazel's execution root, which is the action cwd.
+ cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ && ", ctx.Config().BazelContext.OutputBase()))
+
+ for _, pair := range buildStatement.Env {
+ // Set per-action env variables, if any.
+ cmd.Flag(pair.Key + "=" + pair.Value)
+ }
+
+ // The actual Bazel action.
+ cmd.Text(" " + buildStatement.Command)
for _, outputPath := range buildStatement.OutputPaths {
cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
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/image.go b/android/image.go
index 66101be..bc6b8cd 100644
--- a/android/image.go
+++ b/android/image.go
@@ -43,10 +43,9 @@
// its variation.
ExtraImageVariations(ctx BaseModuleContext) []string
- // SetImageVariation will be passed a newly created recovery variant of the module. ModuleBase implements
- // SetImageVariation, most module types will not need to override it, and those that do must call the
- // overridden method. Implementors of SetImageVariation must be careful to modify the module argument
- // and not the receiver.
+ // SetImageVariation is called for each newly created image variant. The receiver is the original
+ // module, "variation" is the name of the newly created variant and "module" is the newly created
+ // variant itself.
SetImageVariation(ctx BaseModuleContext, variation string, module Module)
}
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/sdk.go b/android/sdk.go
index 36c576d..93beb6e 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -300,6 +300,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 {
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 33b83c0..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.
@@ -1057,8 +1059,9 @@
// apexBundle itself is mutated so that it and its dependencies have the same apex variant.
// TODO(jiyong): document the reason why the VNDK APEX is an exception here.
- if a, ok := mctx.Module().(*apexBundle); ok && !a.vndkApex {
- apexBundleName := mctx.ModuleName()
+ unprefixedModuleName := android.RemoveOptionalPrebuiltPrefix(mctx.ModuleName())
+ if apexModuleTypeRequiresVariant(mctx.Module()) {
+ apexBundleName := unprefixedModuleName
mctx.CreateVariations(apexBundleName)
if strings.HasPrefix(apexBundleName, "com.android.art") {
// Create an alias from the platform variant. This is done to make
@@ -1076,6 +1079,12 @@
mctx.ModuleErrorf("base property is not set")
return
}
+ // Workaround the issue reported in b/191269918 by using the unprefixed module name of this
+ // module as the default variation to use if dependencies of this module do not have the correct
+ // apex variant name. This name matches the name used to create the variations of modules for
+ // which apexModuleTypeRequiresVariant return true.
+ // TODO(b/191269918): Remove this workaround.
+ mctx.SetDefaultDependencyVariation(&unprefixedModuleName)
mctx.CreateVariations(apexBundleName)
if strings.HasPrefix(apexBundleName, "com.android.art") {
// TODO(b/183882457): See note for CreateAliasVariation above.
@@ -1084,6 +1093,22 @@
}
}
+// apexModuleTypeRequiresVariant determines whether the module supplied requires an apex specific
+// variant.
+func apexModuleTypeRequiresVariant(module android.Module) bool {
+ if a, ok := module.(*apexBundle); ok {
+ return !a.vndkApex
+ }
+
+ // Match apex_set and prebuilt_apex. Would also match apexBundle but that is handled specially
+ // above.
+ if _, ok := module.(ApexInfoMutator); ok {
+ return true
+ }
+
+ return false
+}
+
// See android.UpdateDirectlyInAnyApex
// TODO(jiyong): move this to android/apex.go?
func apexDirectlyInAnyMutator(mctx android.BottomUpMutatorContext) {
@@ -1320,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
@@ -1667,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 {
@@ -1733,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:
@@ -2136,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
@@ -2332,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
@@ -2360,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 {
@@ -2412,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 1bfe7e9..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) {
@@ -4389,7 +4468,7 @@
}
`)
- prebuilt := ctx.ModuleForTests("myapex", "android_common").Module().(*Prebuilt)
+ prebuilt := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*Prebuilt)
expectedInput := "myapex-arm64.apex"
if prebuilt.inputApex.String() != expectedInput {
@@ -4398,7 +4477,7 @@
}
func TestPrebuiltMissingSrc(t *testing.T) {
- testApexError(t, `module "myapex" variant "android_common".*: prebuilt_apex does not support "arm64_armv8-a"`, `
+ testApexError(t, `module "myapex" variant "android_common_myapex".*: prebuilt_apex does not support "arm64_armv8-a"`, `
prebuilt_apex {
name: "myapex",
}
@@ -4414,7 +4493,7 @@
}
`)
- p := ctx.ModuleForTests("myapex", "android_common").Module().(*Prebuilt)
+ p := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*Prebuilt)
expected := "notmyapex.apex"
if p.installFilename != expected {
@@ -4433,7 +4512,7 @@
}
`)
- p := ctx.ModuleForTests("myapex.prebuilt", "android_common").Module().(*Prebuilt)
+ p := ctx.ModuleForTests("myapex.prebuilt", "android_common_myapex.prebuilt").Module().(*Prebuilt)
expected := []string{"myapex"}
actual := android.AndroidMkEntriesForTest(t, ctx, p)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
@@ -4503,7 +4582,7 @@
}
// Make sure that the prebuilt_apex has the correct input APEX.
- prebuiltApex := ctx.ModuleForTests("myapex", "android_common")
+ prebuiltApex := ctx.ModuleForTests("myapex", "android_common_myapex")
rule = prebuiltApex.Rule("android/soong/android.Cp")
if expected, actual := "myapex-arm64.apex", android.NormalizePathForTesting(rule.Input); !reflect.DeepEqual(expected, actual) {
t.Errorf("expected: %q, found: %q", expected, actual)
@@ -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
+ `)
})
}
@@ -6522,13 +6650,13 @@
android.AssertArrayString(t, "extractor input", []string{"myapex.hwasan.apks"}, extractedApex.Inputs.Strings())
// Ditto for the apex.
- m = ctx.ModuleForTests("myapex", "android_common")
- copiedApex := m.Output("out/soong/.intermediates/myapex/android_common/foo_v2.apex")
+ m = ctx.ModuleForTests("myapex", "android_common_myapex")
+ copiedApex := m.Output("out/soong/.intermediates/myapex/android_common_myapex/foo_v2.apex")
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)
})
}
@@ -7183,7 +7371,7 @@
t.Errorf("Unexpected abis parameter - expected %q vs actual %q", expected, actual)
}
- m = ctx.ModuleForTests("myapex", "android_common")
+ m = ctx.ModuleForTests("myapex", "android_common_myapex")
a := m.Module().(*ApexSet)
expectedOverrides := []string{"foo"}
actualOverrides := android.AndroidMkEntriesForTest(t, ctx, a)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 7aecff6..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 {
@@ -519,17 +583,42 @@
}
`)
- java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common", []string{
+ 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 81bfc86..ea06d45 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -16,7 +16,7 @@
import (
"fmt"
- "path/filepath"
+ "io"
"strconv"
"strings"
@@ -47,21 +47,51 @@
}
type prebuiltCommon struct {
+ android.ModuleBase
prebuilt android.Prebuilt
// Properties common to both prebuilt_apex and apex_set.
- prebuiltCommonProperties prebuiltCommonProperties
+ prebuiltCommonProperties *PrebuiltCommonProperties
+
+ installDir android.InstallPath
+ installFilename string
+ outputApex android.WritablePath
+
+ // A list of apexFile objects created in prebuiltCommon.initApexFilesForAndroidMk which are used
+ // to create make modules in prebuiltCommon.AndroidMkEntries.
+ apexFilesForAndroidMk []apexFile
+
+ // list of commands to create symlinks for backward compatibility.
+ // these commands will be attached as LOCAL_POST_INSTALL_CMD
+ compatSymlinks []string
+
+ hostRequired []string
+ postInstallCommands []string
}
type sanitizedPrebuilt interface {
hasSanitizedSource(sanitizer string) bool
}
-type prebuiltCommonProperties struct {
+type PrebuiltCommonProperties struct {
SelectedApexProperties
ForceDisable bool `blueprint:"mutated"`
+ // whether the extracted apex file is installable.
+ Installable *bool
+
+ // optional name for the installed apex. If unspecified, name of the
+ // module is used as the file name
+ Filename *string
+
+ // names of modules to be overridden. Listed modules can only be other binaries
+ // (in Make or Soong).
+ // This does not completely prevent installation of the overridden binaries, but if both
+ // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
+ // from PRODUCT_PACKAGES.
+ Overrides []string
+
// List of java libraries that are embedded inside this prebuilt APEX bundle and for which this
// APEX bundle will create an APEX variant and provide dex implementation jars for use by
// dexpreopt and boot jars package check.
@@ -72,6 +102,14 @@
Exported_bootclasspath_fragments []string
}
+// initPrebuiltCommon initializes the prebuiltCommon structure and performs initialization of the
+// module that is common to Prebuilt and ApexSet.
+func (p *prebuiltCommon) initPrebuiltCommon(module android.Module, properties *PrebuiltCommonProperties) {
+ p.prebuiltCommonProperties = properties
+ android.InitSingleSourcePrebuiltModule(module.(android.PrebuiltInterface), properties, "Selected_apex")
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+}
+
func (p *prebuiltCommon) Prebuilt() *android.Prebuilt {
return &p.prebuilt
}
@@ -105,6 +143,130 @@
return false
}
+func (p *prebuiltCommon) InstallFilename() string {
+ return proptools.StringDefault(p.prebuiltCommonProperties.Filename, p.BaseModuleName()+imageApexSuffix)
+}
+
+func (p *prebuiltCommon) Name() string {
+ return p.prebuilt.Name(p.ModuleBase.Name())
+}
+
+func (p *prebuiltCommon) Overrides() []string {
+ return p.prebuiltCommonProperties.Overrides
+}
+
+func (p *prebuiltCommon) installable() bool {
+ return proptools.BoolDefault(p.prebuiltCommonProperties.Installable, true)
+}
+
+// initApexFilesForAndroidMk initializes the prebuiltCommon.apexFilesForAndroidMk field from the
+// modules that this depends upon.
+func (p *prebuiltCommon) initApexFilesForAndroidMk(ctx android.ModuleContext) {
+ // Walk the dependencies of this module looking for the java modules that it exports.
+ ctx.WalkDeps(func(child, parent android.Module) bool {
+ tag := ctx.OtherModuleDependencyTag(child)
+
+ name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(child))
+ if java.IsBootclasspathFragmentContentDepTag(tag) || tag == exportedJavaLibTag {
+ // If the exported java module provides a dex jar path then add it to the list of apexFiles.
+ path := child.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath()
+ if path != nil {
+ p.apexFilesForAndroidMk = append(p.apexFilesForAndroidMk, apexFile{
+ module: child,
+ moduleDir: ctx.OtherModuleDir(child),
+ androidMkModuleName: name,
+ builtFile: path,
+ class: javaSharedLib,
+ })
+ }
+ } else if tag == exportedBootclasspathFragmentTag {
+ // Visit the children of the bootclasspath_fragment.
+ return true
+ }
+
+ return false
+ })
+}
+
+func (p *prebuiltCommon) AndroidMkEntries() []android.AndroidMkEntries {
+ entriesList := []android.AndroidMkEntries{
+ {
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(p.outputApex),
+ Include: "$(BUILD_PREBUILT)",
+ Host_required: p.hostRequired,
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
+ entries.SetString("LOCAL_MODULE_STEM", p.installFilename)
+ entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
+ entries.AddStrings("LOCAL_OVERRIDES_MODULES", p.prebuiltCommonProperties.Overrides...)
+ postInstallCommands := append([]string{}, p.postInstallCommands...)
+ postInstallCommands = append(postInstallCommands, p.compatSymlinks...)
+ if len(postInstallCommands) > 0 {
+ entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(postInstallCommands, " && "))
+ }
+ },
+ },
+ },
+ }
+
+ // Iterate over the apexFilesForAndroidMk list and create an AndroidMkEntries struct for each
+ // file. This provides similar behavior to that provided in apexBundle.AndroidMk() as it makes the
+ // apex specific variants of the exported java modules available for use from within make.
+ apexName := p.BaseModuleName()
+ for _, fi := range p.apexFilesForAndroidMk {
+ 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 {
@@ -272,19 +434,11 @@
}
type Prebuilt struct {
- android.ModuleBase
prebuiltCommon
properties PrebuiltProperties
- inputApex android.Path
- installDir android.InstallPath
- installFilename string
- outputApex android.WritablePath
-
- // list of commands to create symlinks for backward compatibility.
- // these commands will be attached as LOCAL_POST_INSTALL_CMD
- compatSymlinks []string
+ inputApex android.Path
}
type ApexFileProperties struct {
@@ -349,27 +503,13 @@
type PrebuiltProperties struct {
ApexFileProperties
- Installable *bool
- // Optional name for the installed apex. If unspecified, name of the
- // module is used as the file name
- Filename *string
-
- // Names of modules to be overridden. Listed modules can only be other binaries
- // (in Make or Soong).
- // This does not completely prevent installation of the overridden binaries, but if both
- // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
- // from PRODUCT_PACKAGES.
- Overrides []string
+ PrebuiltCommonProperties
}
func (a *Prebuilt) hasSanitizedSource(sanitizer string) bool {
return false
}
-func (p *Prebuilt) installable() bool {
- return p.properties.Installable == nil || proptools.Bool(p.properties.Installable)
-}
-
func (p *Prebuilt) OutputFiles(tag string) (android.Paths, error) {
switch tag {
case "":
@@ -379,20 +519,11 @@
}
}
-func (p *Prebuilt) InstallFilename() string {
- return proptools.StringDefault(p.properties.Filename, p.BaseModuleName()+imageApexSuffix)
-}
-
-func (p *Prebuilt) Name() string {
- return p.prebuiltCommon.prebuilt.Name(p.ModuleBase.Name())
-}
-
// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
func PrebuiltFactory() android.Module {
module := &Prebuilt{}
- module.AddProperties(&module.properties, &module.prebuiltCommonProperties)
- android.InitSingleSourcePrebuiltModule(module, &module.prebuiltCommonProperties, "Selected_apex")
- android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ module.AddProperties(&module.properties)
+ module.initPrebuiltCommon(module, &module.properties.PrebuiltCommonProperties)
return module
}
@@ -415,28 +546,32 @@
// A deapexer module is only needed when the prebuilt apex specifies one or more modules in either
// the `exported_java_libs` or `exported_bootclasspath_fragments` properties as that indicates that
// the listed modules need access to files from within the prebuilt .apex file.
-func createDeapexerModuleIfNeeded(ctx android.TopDownMutatorContext, deapexerName string, apexFileSource string, properties *prebuiltCommonProperties) {
+func createDeapexerModuleIfNeeded(ctx android.TopDownMutatorContext, deapexerName string, apexFileSource string, properties *PrebuiltCommonProperties) {
// Only create the deapexer module if it is needed.
if len(properties.Exported_java_libs)+len(properties.Exported_bootclasspath_fragments) == 0 {
return
}
// 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
}
@@ -445,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
@@ -513,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"}
@@ -556,7 +690,7 @@
createApexSelectorModule(ctx, apexSelectorModuleName, &p.properties.ApexFileProperties)
apexFileSource := ":" + apexSelectorModuleName
- createDeapexerModuleIfNeeded(ctx, deapexerModuleName(baseModuleName), apexFileSource, &p.prebuiltCommonProperties)
+ createDeapexerModuleIfNeeded(ctx, deapexerModuleName(baseModuleName), apexFileSource, p.prebuiltCommonProperties)
// Add a source reference to retrieve the selected apex from the selector module.
p.prebuiltCommonProperties.Selected_apex = proptools.StringPtr(apexFileSource)
@@ -592,6 +726,9 @@
return
}
+ // Save the files that need to be made available to Make.
+ p.initApexFilesForAndroidMk(ctx)
+
if p.installable() {
ctx.InstallFile(p.installDir, p.installFilename, p.inputApex)
}
@@ -599,30 +736,11 @@
// in case that prebuilt_apex replaces source apex (using prefer: prop)
p.compatSymlinks = makeCompatSymlinks(p.BaseModuleName(), ctx)
// or that prebuilt_apex overrides other apexes (using overrides: prop)
- for _, overridden := range p.properties.Overrides {
+ for _, overridden := range p.prebuiltCommonProperties.Overrides {
p.compatSymlinks = append(p.compatSymlinks, makeCompatSymlinks(overridden, ctx)...)
}
}
-func (p *Prebuilt) AndroidMkEntries() []android.AndroidMkEntries {
- return []android.AndroidMkEntries{android.AndroidMkEntries{
- Class: "ETC",
- OutputFile: android.OptionalPathForPath(p.inputApex),
- Include: "$(BUILD_PREBUILT)",
- ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
- entries.SetString("LOCAL_MODULE_STEM", p.installFilename)
- entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
- entries.AddStrings("LOCAL_OVERRIDES_MODULES", p.properties.Overrides...)
- if len(p.compatSymlinks) > 0 {
- entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(p.compatSymlinks, " && "))
- }
- },
- },
- }}
-}
-
// prebuiltApexExtractorModule is a private module type that is only created by the prebuilt_apex
// module. It extracts the correct apex to use and makes it available for use by apex_set.
type prebuiltApexExtractorModule struct {
@@ -667,21 +785,9 @@
}
type ApexSet struct {
- android.ModuleBase
prebuiltCommon
properties ApexSetProperties
-
- installDir android.InstallPath
- installFilename string
- outputApex android.WritablePath
-
- // list of commands to create symlinks for backward compatibility.
- // these commands will be attached as LOCAL_POST_INSTALL_CMD
- compatSymlinks []string
-
- hostRequired []string
- postInstallCommands []string
}
type ApexExtractorProperties struct {
@@ -731,19 +837,7 @@
type ApexSetProperties struct {
ApexExtractorProperties
- // whether the extracted apex file installable.
- Installable *bool
-
- // optional name for the installed apex. If unspecified, name of the
- // module is used as the file name
- Filename *string
-
- // names of modules to be overridden. Listed modules can only be other binaries
- // (in Make or Soong).
- // This does not completely prevent installation of the overridden binaries, but if both
- // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
- // from PRODUCT_PACKAGES.
- Overrides []string
+ PrebuiltCommonProperties
}
func (a *ApexSet) hasSanitizedSource(sanitizer string) bool {
@@ -757,29 +851,11 @@
return false
}
-func (a *ApexSet) installable() bool {
- return a.properties.Installable == nil || proptools.Bool(a.properties.Installable)
-}
-
-func (a *ApexSet) InstallFilename() string {
- return proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+imageApexSuffix)
-}
-
-func (a *ApexSet) Name() string {
- return a.prebuiltCommon.prebuilt.Name(a.ModuleBase.Name())
-}
-
-func (a *ApexSet) Overrides() []string {
- return a.properties.Overrides
-}
-
// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
func apexSetFactory() android.Module {
module := &ApexSet{}
- module.AddProperties(&module.properties, &module.prebuiltCommonProperties)
-
- android.InitSingleSourcePrebuiltModule(module, &module.prebuiltCommonProperties, "Selected_apex")
- android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ module.AddProperties(&module.properties)
+ module.initPrebuiltCommon(module, &module.properties.PrebuiltCommonProperties)
return module
}
@@ -817,7 +893,7 @@
createApexExtractorModule(ctx, apexExtractorModuleName, &a.properties.ApexExtractorProperties)
apexFileSource := ":" + apexExtractorModuleName
- createDeapexerModuleIfNeeded(ctx, deapexerModuleName(baseModuleName), apexFileSource, &a.prebuiltCommonProperties)
+ createDeapexerModuleIfNeeded(ctx, deapexerModuleName(baseModuleName), apexFileSource, a.prebuiltCommonProperties)
// After passing the arch specific src properties to the creating the apex selector module
a.prebuiltCommonProperties.Selected_apex = proptools.StringPtr(apexFileSource)
@@ -852,6 +928,9 @@
return
}
+ // Save the files that need to be made available to Make.
+ a.initApexFilesForAndroidMk(ctx)
+
a.installDir = android.PathForModuleInstall(ctx, "apex")
if a.installable() {
ctx.InstallFile(a.installDir, a.installFilename, a.outputApex)
@@ -860,7 +939,7 @@
// in case that apex_set replaces source apex (using prefer: prop)
a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx)
// or that apex_set overrides other apexes (using overrides: prop)
- for _, overridden := range a.properties.Overrides {
+ for _, overridden := range a.prebuiltCommonProperties.Overrides {
a.compatSymlinks = append(a.compatSymlinks, makeCompatSymlinks(overridden, ctx)...)
}
@@ -883,25 +962,3 @@
func (*systemExtContext) SystemExtSpecific() bool {
return true
}
-
-func (a *ApexSet) AndroidMkEntries() []android.AndroidMkEntries {
- return []android.AndroidMkEntries{android.AndroidMkEntries{
- Class: "ETC",
- OutputFile: android.OptionalPathForPath(a.outputApex),
- Include: "$(BUILD_PREBUILT)",
- Host_required: a.hostRequired,
- ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", a.installDir.ToMakePath().String())
- entries.SetString("LOCAL_MODULE_STEM", a.installFilename)
- entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !a.installable())
- entries.AddStrings("LOCAL_OVERRIDES_MODULES", a.properties.Overrides...)
- postInstallCommands := append([]string{}, a.postInstallCommands...)
- postInstallCommands = append(postInstallCommands, a.compatSymlinks...)
- if len(postInstallCommands) > 0 {
- entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(postInstallCommands, " && "))
- }
- },
- },
- }}
-}
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/cquery/request_type.go b/bazel/cquery/request_type.go
index 92135c6..52c6c2f 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -20,6 +20,10 @@
// be a subset of OutputFiles. (or static libraries, this will be equal to OutputFiles,
// but general cc_library will also have dynamic libraries in output files).
RootStaticArchives []string
+ // Dynamic libraries (.so files) created by the current target. These will
+ // be a subset of OutputFiles. (or shared libraries, this will be equal to OutputFiles,
+ // but general cc_library will also have dynamic libraries in output files).
+ RootDynamicLibraries []string
}
type getOutputFilesRequestType struct{}
@@ -86,13 +90,21 @@
if linker_input.owner == target.label:
rootStaticArchives.append(library.static_library.path)
+rootDynamicLibraries = []
+
+if "@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo" in providers(target):
+ shared_info = providers(target)["@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo"]
+ for lib in shared_info.linker_input.libraries:
+ rootDynamicLibraries += [lib.dynamic_library.path]
+
returns = [
outputFiles,
staticLibraries,
ccObjectFiles,
includes,
system_includes,
- rootStaticArchives
+ rootStaticArchives,
+ rootDynamicLibraries
]
return "|".join([", ".join(r) for r in returns])`
@@ -106,7 +118,7 @@
var ccObjects []string
splitString := strings.Split(rawString, "|")
- if expectedLen := 6; len(splitString) != expectedLen {
+ if expectedLen := 7; len(splitString) != expectedLen {
return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString)
}
outputFilesString := splitString[0]
@@ -118,6 +130,7 @@
includes := splitOrEmpty(splitString[3], ", ")
systemIncludes := splitOrEmpty(splitString[4], ", ")
rootStaticArchives := splitOrEmpty(splitString[5], ", ")
+ rootDynamicLibraries := splitOrEmpty(splitString[6], ", ")
return CcInfo{
OutputFiles: outputFiles,
CcObjectFiles: ccObjects,
@@ -125,6 +138,7 @@
Includes: includes,
SystemIncludes: systemIncludes,
RootStaticArchives: rootStaticArchives,
+ RootDynamicLibraries: rootDynamicLibraries,
}, nil
}
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 602849e..035544e 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -46,7 +46,7 @@
}{
{
description: "no result",
- input: "|||||",
+ input: "||||||",
expectedOutput: CcInfo{
OutputFiles: []string{},
CcObjectFiles: []string{},
@@ -54,11 +54,12 @@
Includes: []string{},
SystemIncludes: []string{},
RootStaticArchives: []string{},
+ RootDynamicLibraries: []string{},
},
},
{
description: "only output",
- input: "test|||||",
+ input: "test||||||",
expectedOutput: CcInfo{
OutputFiles: []string{"test"},
CcObjectFiles: []string{},
@@ -66,11 +67,12 @@
Includes: []string{},
SystemIncludes: []string{},
RootStaticArchives: []string{},
+ RootDynamicLibraries: []string{},
},
},
{
description: "all items set",
- input: "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|rootstaticarchive1",
+ input: "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|rootstaticarchive1|rootdynamiclibrary1",
expectedOutput: CcInfo{
OutputFiles: []string{"out1", "out2"},
CcObjectFiles: []string{"object1", "object2"},
@@ -78,19 +80,20 @@
Includes: []string{".", "dir/subdir"},
SystemIncludes: []string{"system/dir", "system/other/dir"},
RootStaticArchives: []string{"rootstaticarchive1"},
+ RootDynamicLibraries: []string{"rootdynamiclibrary1"},
},
},
{
description: "too few result splits",
input: "|",
expectedOutput: CcInfo{},
- expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 6, []string{"", ""}),
+ expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 7, []string{"", ""}),
},
{
description: "too many result splits",
input: strings.Repeat("|", 8),
expectedOutput: CcInfo{},
- expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 6, make([]string, 9)),
+ expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 7, make([]string, 9)),
},
}
for _, tc := range testCases {
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/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 7f6982c..285677a 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -385,19 +385,23 @@
"-I$(BINDIR)/foo/bar",
],
dynamic_deps = [":shared_dep_for_both"],
- dynamic_deps_for_shared = [":shared_dep_for_shared"],
- dynamic_deps_for_static = [":shared_dep_for_static"],
implementation_deps = [":static_dep_for_both"],
- shared_copts = ["sharedflag"],
- shared_srcs = ["sharedonly.cpp"],
+ shared = {
+ "copts": ["sharedflag"],
+ "dynamic_deps": [":shared_dep_for_shared"],
+ "srcs": ["sharedonly.cpp"],
+ "static_deps": [":static_dep_for_shared"],
+ "whole_archive_deps": [":whole_static_lib_for_shared"],
+ },
srcs = ["both.cpp"],
- static_copts = ["staticflag"],
- static_deps_for_shared = [":static_dep_for_shared"],
- static_deps_for_static = [":static_dep_for_static"],
- static_srcs = ["staticonly.cpp"],
+ static = {
+ "copts": ["staticflag"],
+ "dynamic_deps": [":shared_dep_for_static"],
+ "srcs": ["staticonly.cpp"],
+ "static_deps": [":static_dep_for_static"],
+ "whole_archive_deps": [":whole_static_lib_for_static"],
+ },
whole_archive_deps = [":whole_static_lib_for_both"],
- whole_archive_deps_for_shared = [":whole_static_lib_for_shared"],
- whole_archive_deps_for_static = [":whole_static_lib_for_static"],
)`},
})
}
@@ -437,9 +441,13 @@
"-Ifoo/bar",
"-I$(BINDIR)/foo/bar",
],
+ shared = {
+ "whole_archive_deps": [":whole_static_lib_for_shared_alwayslink"],
+ },
+ static = {
+ "whole_archive_deps": [":whole_static_lib_for_static_alwayslink"],
+ },
whole_archive_deps = [":whole_static_lib_for_both_alwayslink"],
- whole_archive_deps_for_shared = [":whole_static_lib_for_shared_alwayslink"],
- whole_archive_deps_for_static = [":whole_static_lib_for_static_alwayslink"],
)`},
})
}
@@ -529,52 +537,56 @@
"-Ifoo/bar",
"-I$(BINDIR)/foo/bar",
],
- dynamic_deps_for_shared = select({
- "//build/bazel/platforms/arch:arm": [":arm_shared_dep_for_shared"],
- "//conditions:default": [],
- }),
implementation_deps = [":static_dep_for_both"],
- shared_copts = ["sharedflag"] + select({
- "//build/bazel/platforms/arch:arm": ["-DARM_SHARED"],
- "//conditions:default": [],
- }) + select({
- "//build/bazel/platforms/os:android": ["-DANDROID_SHARED"],
- "//conditions:default": [],
- }) + select({
- "//build/bazel/platforms/os_arch:android_arm": ["-DANDROID_ARM_SHARED"],
- "//conditions:default": [],
- }),
- shared_srcs = ["sharedonly.cpp"] + select({
- "//build/bazel/platforms/arch:arm": ["arm_shared.cpp"],
- "//conditions:default": [],
- }) + select({
- "//build/bazel/platforms/os:android": ["android_shared.cpp"],
- "//conditions:default": [],
- }),
+ shared = {
+ "copts": ["sharedflag"] + select({
+ "//build/bazel/platforms/arch:arm": ["-DARM_SHARED"],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/os:android": ["-DANDROID_SHARED"],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/os_arch:android_arm": ["-DANDROID_ARM_SHARED"],
+ "//conditions:default": [],
+ }),
+ "dynamic_deps": select({
+ "//build/bazel/platforms/arch:arm": [":arm_shared_dep_for_shared"],
+ "//conditions:default": [],
+ }),
+ "srcs": ["sharedonly.cpp"] + select({
+ "//build/bazel/platforms/arch:arm": ["arm_shared.cpp"],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/os:android": ["android_shared.cpp"],
+ "//conditions:default": [],
+ }),
+ "static_deps": [":static_dep_for_shared"] + select({
+ "//build/bazel/platforms/arch:arm": [":arm_static_dep_for_shared"],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/os:android": [":android_dep_for_shared"],
+ "//conditions:default": [],
+ }),
+ "whole_archive_deps": select({
+ "//build/bazel/platforms/arch:arm": [":arm_whole_static_dep_for_shared"],
+ "//conditions:default": [],
+ }),
+ },
srcs = ["both.cpp"],
- static_copts = ["staticflag"] + select({
- "//build/bazel/platforms/arch:x86": ["-DX86_STATIC"],
- "//conditions:default": [],
- }),
- static_deps_for_shared = [":static_dep_for_shared"] + select({
- "//build/bazel/platforms/arch:arm": [":arm_static_dep_for_shared"],
- "//conditions:default": [],
- }) + select({
- "//build/bazel/platforms/os:android": [":android_dep_for_shared"],
- "//conditions:default": [],
- }),
- static_deps_for_static = [":static_dep_for_static"] + select({
- "//build/bazel/platforms/arch:x86": [":x86_dep_for_static"],
- "//conditions:default": [],
- }),
- static_srcs = ["staticonly.cpp"] + select({
- "//build/bazel/platforms/arch:x86": ["x86_static.cpp"],
- "//conditions:default": [],
- }),
- whole_archive_deps_for_shared = select({
- "//build/bazel/platforms/arch:arm": [":arm_whole_static_dep_for_shared"],
- "//conditions:default": [],
- }),
+ static = {
+ "copts": ["staticflag"] + select({
+ "//build/bazel/platforms/arch:x86": ["-DX86_STATIC"],
+ "//conditions:default": [],
+ }),
+ "srcs": ["staticonly.cpp"] + select({
+ "//build/bazel/platforms/arch:x86": ["x86_static.cpp"],
+ "//conditions:default": [],
+ }),
+ "static_deps": [":static_dep_for_static"] + select({
+ "//build/bazel/platforms/arch:x86": [":x86_dep_for_static"],
+ "//conditions:default": [],
+ }),
+ },
)`},
})
}
@@ -666,20 +678,22 @@
"-Ifoo/bar",
"-I$(BINDIR)/foo/bar",
],
- shared_srcs = [
- ":shared_filegroup_cpp_srcs",
- "shared_source.cc",
- "shared_source.cpp",
- ],
- shared_srcs_as = [
- "shared_source.s",
- "shared_source.S",
- ":shared_filegroup_as_srcs",
- ],
- shared_srcs_c = [
- "shared_source.c",
- ":shared_filegroup_c_srcs",
- ],
+ shared = {
+ "srcs": [
+ ":shared_filegroup_cpp_srcs",
+ "shared_source.cc",
+ "shared_source.cpp",
+ ],
+ "srcs_as": [
+ "shared_source.s",
+ "shared_source.S",
+ ":shared_filegroup_as_srcs",
+ ],
+ "srcs_c": [
+ "shared_source.c",
+ ":shared_filegroup_c_srcs",
+ ],
+ },
srcs = [
":both_filegroup_cpp_srcs",
"both_source.cc",
@@ -694,20 +708,22 @@
"both_source.c",
":both_filegroup_c_srcs",
],
- static_srcs = [
- ":static_filegroup_cpp_srcs",
- "static_source.cc",
- "static_source.cpp",
- ],
- static_srcs_as = [
- "static_source.s",
- "static_source.S",
- ":static_filegroup_as_srcs",
- ],
- static_srcs_c = [
- "static_source.c",
- ":static_filegroup_c_srcs",
- ],
+ static = {
+ "srcs": [
+ ":static_filegroup_cpp_srcs",
+ "static_source.cc",
+ "static_source.cpp",
+ ],
+ "srcs_as": [
+ "static_source.s",
+ "static_source.S",
+ ":static_filegroup_as_srcs",
+ ],
+ "srcs_c": [
+ "static_source.c",
+ ":static_filegroup_c_srcs",
+ ],
+ },
)`},
})
}
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/binary.go b/cc/binary.go
index 999b82c..3aa3fdf 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -149,11 +149,11 @@
if ctx.toolchain().Bionic() {
if !Bool(binary.baseLinker.Properties.Nocrt) {
if binary.static() {
- deps.CrtBegin = "crtbegin_static"
+ deps.CrtBegin = []string{"crtbegin_static"}
} else {
- deps.CrtBegin = "crtbegin_dynamic"
+ deps.CrtBegin = []string{"crtbegin_dynamic"}
}
- deps.CrtEnd = "crtend_android"
+ deps.CrtEnd = []string{"crtend_android"}
}
if binary.static() {
@@ -178,7 +178,7 @@
// the kernel before jumping to the embedded linker.
if ctx.Os() == android.LinuxBionic && !binary.static() {
deps.DynamicLinker = "linker"
- deps.LinkerFlagsFile = "host_bionic_linker_flags"
+ deps.CrtBegin = append(deps.CrtBegin, "host_bionic_linker_script")
}
}
@@ -345,12 +345,6 @@
var linkerDeps android.Paths
- // Add flags from linker flags file.
- if deps.LinkerFlagsFile.Valid() {
- flags.Local.LdFlags = append(flags.Local.LdFlags, "$$(cat "+deps.LinkerFlagsFile.String()+")")
- linkerDeps = append(linkerDeps, deps.LinkerFlagsFile.Path())
- }
-
if flags.DynamicLinker != "" {
flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-dynamic-linker,"+flags.DynamicLinker)
} else if ctx.toolchain().Bionic() && !binary.static() {
@@ -401,16 +395,18 @@
}
}
+ var validations android.WritablePaths
+
// Handle host bionic linker symbols.
if ctx.Os() == android.LinuxBionic && !binary.static() {
- injectedOutputFile := outputFile
- outputFile = android.PathForModuleOut(ctx, "prelinker", fileName)
+ verifyFile := android.PathForModuleOut(ctx, "host_bionic_verify.stamp")
if !deps.DynamicLinker.Valid() {
panic("Non-static host bionic modules must have a dynamic linker")
}
- binary.injectHostBionicLinkerSymbols(ctx, outputFile, deps.DynamicLinker.Path(), injectedOutputFile)
+ binary.verifyHostBionicLinker(ctx, outputFile, deps.DynamicLinker.Path(), verifyFile)
+ validations = append(validations, verifyFile)
}
var sharedLibs android.Paths
@@ -430,7 +426,7 @@
// Register link action.
transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs,
deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true,
- builderFlags, outputFile, nil)
+ builderFlags, outputFile, nil, validations)
objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
@@ -532,19 +528,19 @@
}
func init() {
- pctx.HostBinToolVariable("hostBionicSymbolsInjectCmd", "host_bionic_inject")
+ pctx.HostBinToolVariable("verifyHostBionicCmd", "host_bionic_verify")
}
-var injectHostBionicSymbols = pctx.AndroidStaticRule("injectHostBionicSymbols",
+var verifyHostBionic = pctx.AndroidStaticRule("verifyHostBionic",
blueprint.RuleParams{
- Command: "$hostBionicSymbolsInjectCmd -i $in -l $linker -o $out",
- CommandDeps: []string{"$hostBionicSymbolsInjectCmd"},
+ Command: "$verifyHostBionicCmd -i $in -l $linker && touch $out",
+ CommandDeps: []string{"$verifyHostBionicCmd"},
}, "linker")
-func (binary *binaryDecorator) injectHostBionicLinkerSymbols(ctx ModuleContext, in, linker android.Path, out android.WritablePath) {
+func (binary *binaryDecorator) verifyHostBionicLinker(ctx ModuleContext, in, linker android.Path, out android.WritablePath) {
ctx.Build(pctx, android.BuildParams{
- Rule: injectHostBionicSymbols,
- Description: "inject host bionic symbols",
+ Rule: verifyHostBionic,
+ Description: "verify host bionic",
Input: in,
Implicit: linker,
Output: out,
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 7c6ee0a..76c5f3b 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -142,15 +142,14 @@
// staticOrSharedAttributes are the Bazel-ified versions of StaticOrSharedProperties --
// properties which apply to either the shared or static version of a cc_library module.
type staticOrSharedAttributes struct {
- srcs bazel.LabelListAttribute
- srcs_c bazel.LabelListAttribute
- srcs_as bazel.LabelListAttribute
+ Srcs bazel.LabelListAttribute
+ Srcs_c bazel.LabelListAttribute
+ Srcs_as bazel.LabelListAttribute
+ Copts bazel.StringListAttribute
- copts bazel.StringListAttribute
-
- staticDeps bazel.LabelListAttribute
- dynamicDeps bazel.LabelListAttribute
- wholeArchiveDeps bazel.LabelListAttribute
+ Static_deps bazel.LabelListAttribute
+ Dynamic_deps bazel.LabelListAttribute
+ Whole_archive_deps bazel.LabelListAttribute
}
func groupSrcsByExtension(ctx android.TopDownMutatorContext, srcs bazel.LabelListAttribute) (cppSrcs, cSrcs, asSrcs bazel.LabelListAttribute) {
@@ -251,19 +250,19 @@
}
attrs := staticOrSharedAttributes{
- copts: bazel.StringListAttribute{Value: props.Cflags},
- srcs: bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, props.Srcs)),
- staticDeps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Static_libs)),
- dynamicDeps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Shared_libs)),
- wholeArchiveDeps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleWholeDeps(ctx, props.Whole_static_libs)),
+ Copts: bazel.StringListAttribute{Value: props.Cflags},
+ Srcs: bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, props.Srcs)),
+ Static_deps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Static_libs)),
+ Dynamic_deps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Shared_libs)),
+ Whole_archive_deps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleWholeDeps(ctx, props.Whole_static_libs)),
}
setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
- attrs.copts.SetSelectValue(axis, config, props.Cflags)
- attrs.srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
- attrs.staticDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Static_libs))
- attrs.dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Shared_libs))
- attrs.wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleWholeDeps(ctx, props.Whole_static_libs))
+ attrs.Copts.SetSelectValue(axis, config, props.Cflags)
+ attrs.Srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
+ attrs.Static_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Static_libs))
+ attrs.Dynamic_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Shared_libs))
+ attrs.Whole_archive_deps.SetSelectValue(axis, config, android.BazelLabelForModuleWholeDeps(ctx, props.Whole_static_libs))
}
if isStatic {
@@ -284,10 +283,10 @@
}
}
- cppSrcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, attrs.srcs)
- attrs.srcs = cppSrcs
- attrs.srcs_c = cSrcs
- attrs.srcs_as = asSrcs
+ cppSrcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, attrs.Srcs)
+ attrs.Srcs = cppSrcs
+ attrs.Srcs_c = cSrcs
+ attrs.Srcs_as = asSrcs
return attrs
}
diff --git a/cc/builder.go b/cc/builder.go
index fae9522..bde8c96 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -730,8 +730,9 @@
// Generate a rule for compiling multiple .o files, plus static libraries, whole static libraries,
// and shared libraries, to a shared library (.so) or dynamic executable
func transformObjToDynamicBinary(ctx android.ModuleContext,
- objFiles, sharedLibs, staticLibs, lateStaticLibs, wholeStaticLibs, deps android.Paths,
- crtBegin, crtEnd android.OptionalPath, groupLate bool, flags builderFlags, outputFile android.WritablePath, implicitOutputs android.WritablePaths) {
+ objFiles, sharedLibs, staticLibs, lateStaticLibs, wholeStaticLibs, deps, crtBegin, crtEnd android.Paths,
+ groupLate bool, flags builderFlags, outputFile android.WritablePath,
+ implicitOutputs android.WritablePaths, validations android.WritablePaths) {
ldCmd := "${config.ClangBin}/clang++"
@@ -778,18 +779,17 @@
deps = append(deps, staticLibs...)
deps = append(deps, lateStaticLibs...)
deps = append(deps, wholeStaticLibs...)
- if crtBegin.Valid() {
- deps = append(deps, crtBegin.Path(), crtEnd.Path())
- }
+ deps = append(deps, crtBegin...)
+ deps = append(deps, crtEnd...)
rule := ld
args := map[string]string{
"ldCmd": ldCmd,
- "crtBegin": crtBegin.String(),
+ "crtBegin": strings.Join(crtBegin.Strings(), " "),
"libFlags": strings.Join(libFlagsList, " "),
"extraLibFlags": flags.extraLibFlags,
"ldFlags": flags.globalLdFlags + " " + flags.localLdFlags,
- "crtEnd": crtEnd.String(),
+ "crtEnd": strings.Join(crtEnd.Strings(), " "),
}
if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_CXX_LINKS") {
rule = ldRE
@@ -805,6 +805,7 @@
Inputs: objFiles,
Implicits: deps,
OrderOnly: sharedLibs,
+ Validations: validations.Paths(),
Args: args,
})
}
diff --git a/cc/cc.go b/cc/cc.go
index 555cb6c..0c9f945 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -126,11 +126,10 @@
ReexportGeneratedHeaders []string
- CrtBegin, CrtEnd string
+ CrtBegin, CrtEnd []string
// Used for host bionic
- LinkerFlagsFile string
- DynamicLinker string
+ DynamicLinker string
// List of libs that need to be excluded for APEX variant
ExcludeLibsForApex []string
@@ -177,10 +176,7 @@
ReexportedDeps android.Paths
// Paths to crt*.o files
- CrtBegin, CrtEnd android.OptionalPath
-
- // Path to the file container flags to use with the linker
- LinkerFlagsFile android.OptionalPath
+ CrtBegin, CrtEnd android.Paths
// Path to the dynamic linker binary
DynamicLinker android.OptionalPath
@@ -290,9 +286,15 @@
// Set by DepsMutator.
AndroidMkSystemSharedLibs []string `blueprint:"mutated"`
+ // The name of the image this module is built for, suffixed with a '.'
ImageVariationPrefix string `blueprint:"mutated"`
- VndkVersion string `blueprint:"mutated"`
- SubName string `blueprint:"mutated"`
+
+ // The VNDK version this module is built against. If empty, the module is not
+ // build against the VNDK.
+ VndkVersion string `blueprint:"mutated"`
+
+ // Suffix for the name of Android.mk entries generated by this module
+ SubName string `blueprint:"mutated"`
// *.logtags files, to combine together in order to generate the /system/etc/event-log-tags
// file
@@ -315,12 +317,15 @@
// Make this module available when building for recovery
Recovery_available *bool
- // Set by imageMutator
- CoreVariantNeeded bool `blueprint:"mutated"`
- RamdiskVariantNeeded bool `blueprint:"mutated"`
- VendorRamdiskVariantNeeded bool `blueprint:"mutated"`
- RecoveryVariantNeeded bool `blueprint:"mutated"`
- ExtraVariants []string `blueprint:"mutated"`
+ // Used by imageMutator, set by ImageMutatorBegin()
+ CoreVariantNeeded bool `blueprint:"mutated"`
+ RamdiskVariantNeeded bool `blueprint:"mutated"`
+ VendorRamdiskVariantNeeded bool `blueprint:"mutated"`
+ RecoveryVariantNeeded bool `blueprint:"mutated"`
+
+ // A list of variations for the "image" mutator of the form
+ //<image name> '.' <version char>, for example, 'vendor.S'
+ ExtraVersionedImageVariations []string `blueprint:"mutated"`
// Allows this module to use non-APEX version of libraries. Useful
// for building binaries that are started before APEXes are activated.
@@ -580,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
@@ -717,7 +723,6 @@
genHeaderDepTag = dependencyTag{name: "gen header"}
genHeaderExportDepTag = dependencyTag{name: "gen header export"}
objDepTag = dependencyTag{name: "obj"}
- linkerFlagsDepTag = dependencyTag{name: "linker flags file"}
dynamicLinkerDepTag = installDependencyTag{name: "dynamic linker"}
reuseObjTag = dependencyTag{name: "reuse objects"}
staticVariantTag = dependencyTag{name: "static variant"}
@@ -1305,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
@@ -1673,10 +1682,6 @@
c.makeLinkType = GetMakeLinkType(actx, c)
- if c.maybeGenerateBazelActions(actx) {
- return
- }
-
ctx := &moduleContext{
ModuleContext: actx,
moduleContextImpl: moduleContextImpl{
@@ -1685,6 +1690,11 @@
}
ctx.ctx = ctx
+ if c.maybeGenerateBazelActions(actx) {
+ c.maybeInstall(ctx, apexInfo)
+ return
+ }
+
deps := c.depsToPaths(ctx)
if ctx.Failed() {
return
@@ -1772,19 +1782,7 @@
}
c.outputFile = android.OptionalPathForPath(outputFile)
- // If a lib is directly included in any of the APEXes or is not available to the
- // platform (which is often the case when the stub is provided as a prebuilt),
- // unhide the stubs variant having the latest version gets visible to make. In
- // addition, the non-stubs variant is renamed to <libname>.bootstrap. This is to
- // force anything in the make world to link against the stubs library. (unless it
- // is explicitly referenced via .bootstrap suffix or the module is marked with
- // 'bootstrap: true').
- if c.HasStubsVariants() && c.NotInPlatform() && !c.InRamdisk() &&
- !c.InRecovery() && !c.UseVndk() && !c.static() && !c.isCoverageVariant() &&
- c.IsStubs() && !c.InVendorRamdisk() {
- c.Properties.HideFromMake = false // unhide
- // Note: this is still non-installable
- }
+ c.maybeUnhideFromMake()
// glob exported headers for snapshot, if BOARD_VNDK_VERSION is current or
// RECOVERY_SNAPSHOT_VERSION is current.
@@ -1795,6 +1793,26 @@
}
}
+ c.maybeInstall(ctx, apexInfo)
+}
+
+func (c *Module) maybeUnhideFromMake() {
+ // If a lib is directly included in any of the APEXes or is not available to the
+ // platform (which is often the case when the stub is provided as a prebuilt),
+ // unhide the stubs variant having the latest version gets visible to make. In
+ // addition, the non-stubs variant is renamed to <libname>.bootstrap. This is to
+ // force anything in the make world to link against the stubs library. (unless it
+ // is explicitly referenced via .bootstrap suffix or the module is marked with
+ // 'bootstrap: true').
+ if c.HasStubsVariants() && c.NotInPlatform() && !c.InRamdisk() &&
+ !c.InRecovery() && !c.UseVndk() && !c.static() && !c.isCoverageVariant() &&
+ c.IsStubs() && !c.InVendorRamdisk() {
+ c.Properties.HideFromMake = false // unhide
+ // Note: this is still non-installable
+ }
+}
+
+func (c *Module) maybeInstall(ctx ModuleContext, apexInfo android.ApexInfo) {
if !proptools.BoolDefault(c.Properties.Installable, true) {
// If the module has been specifically configure to not be installed then
// hide from make as otherwise it will break when running inside make
@@ -2180,13 +2198,6 @@
}, depTag, RewriteSnapshotLib(staticUnwinder(actx), GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
}
- for _, lib := range deps.LateStaticLibs {
- depTag := libraryDependencyTag{Kind: staticLibraryDependency, Order: lateLibraryDependency}
- actx.AddVariationDependencies([]blueprint.Variation{
- {Mutator: "link", Variation: "static"},
- }, depTag, RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
- }
-
// shared lib names without the #version suffix
var sharedLibNames []string
@@ -2212,6 +2223,13 @@
AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, name, version, false)
}
+ for _, lib := range deps.LateStaticLibs {
+ depTag := libraryDependencyTag{Kind: staticLibraryDependency, Order: lateLibraryDependency}
+ actx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "static"},
+ }, depTag, RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
+ }
+
for _, lib := range deps.LateSharedLibs {
if inList(lib, sharedLibNames) {
// This is to handle the case that some of the late shared libs (libc, libdl, libm, ...)
@@ -2246,16 +2264,13 @@
crtVariations := GetCrtVariations(ctx, c)
actx.AddVariationDependencies(crtVariations, objDepTag, deps.ObjFiles...)
- if deps.CrtBegin != "" {
+ for _, crt := range deps.CrtBegin {
actx.AddVariationDependencies(crtVariations, CrtBeginDepTag,
- RewriteSnapshotLib(deps.CrtBegin, GetSnapshot(c, &snapshotInfo, actx).Objects))
+ RewriteSnapshotLib(crt, GetSnapshot(c, &snapshotInfo, actx).Objects))
}
- if deps.CrtEnd != "" {
+ for _, crt := range deps.CrtEnd {
actx.AddVariationDependencies(crtVariations, CrtEndDepTag,
- RewriteSnapshotLib(deps.CrtEnd, GetSnapshot(c, &snapshotInfo, actx).Objects))
- }
- if deps.LinkerFlagsFile != "" {
- actx.AddDependency(c, linkerFlagsDepTag, deps.LinkerFlagsFile)
+ RewriteSnapshotLib(crt, GetSnapshot(c, &snapshotInfo, actx).Objects))
}
if deps.DynamicLinker != "" {
actx.AddDependency(c, dynamicLinkerDepTag, deps.DynamicLinker)
@@ -2555,17 +2570,10 @@
} else {
ctx.ModuleErrorf("module %q is not a genrule", depName)
}
- case linkerFlagsDepTag:
- if genRule, ok := dep.(genrule.SourceFileGenerator); ok {
- files := genRule.GeneratedSourceFiles()
- if len(files) == 1 {
- depPaths.LinkerFlagsFile = android.OptionalPathForPath(files[0])
- } else if len(files) > 1 {
- ctx.ModuleErrorf("module %q can only generate a single file if used for a linker flag file", depName)
- }
- } else {
- ctx.ModuleErrorf("module %q is not a genrule", depName)
- }
+ case CrtBeginDepTag:
+ depPaths.CrtBegin = append(depPaths.CrtBegin, android.OutputFileForModule(ctx, dep, ""))
+ case CrtEndDepTag:
+ depPaths.CrtEnd = append(depPaths.CrtEnd, android.OutputFileForModule(ctx, dep, ""))
}
return
}
@@ -2878,9 +2886,9 @@
case objDepTag:
depPaths.Objs.objFiles = append(depPaths.Objs.objFiles, linkFile.Path())
case CrtBeginDepTag:
- depPaths.CrtBegin = linkFile
+ depPaths.CrtBegin = append(depPaths.CrtBegin, linkFile.Path())
case CrtEndDepTag:
- depPaths.CrtEnd = linkFile
+ depPaths.CrtEnd = append(depPaths.CrtEnd, linkFile.Path())
case dynamicLinkerDepTag:
depPaths.DynamicLinker = linkFile
}
@@ -3380,7 +3388,7 @@
}
func (c *Module) IsSdkVariant() bool {
- return c.Properties.IsSdkVariant || c.AlwaysSdk()
+ return c.Properties.IsSdkVariant
}
func kytheExtractAllFactory() android.Singleton {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index e0fae5a..2d0d78b 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -4231,3 +4231,152 @@
)
})
}
+
+func TestIncludeDirectoryOrdering(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "libfoo",
+ srcs: ["foo.c"],
+ local_include_dirs: ["local_include_dirs"],
+ export_include_dirs: ["export_include_dirs"],
+ export_system_include_dirs: ["export_system_include_dirs"],
+ static_libs: ["libstatic1", "libstatic2"],
+ whole_static_libs: ["libwhole1", "libwhole2"],
+ shared_libs: ["libshared1", "libshared2"],
+ header_libs: ["libheader1", "libheader2"],
+ target: {
+ android: {
+ shared_libs: ["libandroid"],
+ local_include_dirs: ["android_local_include_dirs"],
+ export_include_dirs: ["android_export_include_dirs"],
+ },
+ android_arm: {
+ shared_libs: ["libandroid_arm"],
+ local_include_dirs: ["android_arm_local_include_dirs"],
+ export_include_dirs: ["android_arm_export_include_dirs"],
+ },
+ linux: {
+ shared_libs: ["liblinux"],
+ local_include_dirs: ["linux_local_include_dirs"],
+ export_include_dirs: ["linux_export_include_dirs"],
+ },
+ },
+ multilib: {
+ lib32: {
+ shared_libs: ["lib32"],
+ local_include_dirs: ["lib32_local_include_dirs"],
+ export_include_dirs: ["lib32_export_include_dirs"],
+ },
+ },
+ arch: {
+ arm: {
+ shared_libs: ["libarm"],
+ local_include_dirs: ["arm_local_include_dirs"],
+ export_include_dirs: ["arm_export_include_dirs"],
+ },
+ },
+ stl: "libc++",
+ sdk_version: "20",
+ }
+
+ cc_library_headers {
+ name: "libheader1",
+ export_include_dirs: ["libheader1"],
+ sdk_version: "20",
+ stl: "none",
+ }
+
+ cc_library_headers {
+ name: "libheader2",
+ export_include_dirs: ["libheader2"],
+ sdk_version: "20",
+ stl: "none",
+ }
+ `
+
+ libs := []string{
+ "libstatic1",
+ "libstatic2",
+ "libwhole1",
+ "libwhole2",
+ "libshared1",
+ "libshared2",
+ "libandroid",
+ "libandroid_arm",
+ "liblinux",
+ "lib32",
+ "libarm",
+ }
+
+ for _, lib := range libs {
+ bp += fmt.Sprintf(`
+ cc_library {
+ name: "%s",
+ export_include_dirs: ["%s"],
+ sdk_version: "20",
+ stl: "none",
+ }
+ `, lib, lib)
+ }
+
+ ctx := PrepareForIntegrationTestWithCc.RunTestWithBp(t, bp)
+ // Use the arm variant instead of the arm64 variant so that it gets headers from
+ // ndk_libandroid_support to test LateStaticLibs.
+ cflags := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_sdk_static").Output("obj/foo.o").Args["cFlags"]
+
+ var includes []string
+ flags := strings.Split(cflags, " ")
+ for i, flag := range flags {
+ if strings.Contains(flag, "Cflags") {
+ includes = append(includes, flag)
+ } else if strings.HasPrefix(flag, "-I") {
+ includes = append(includes, strings.TrimPrefix(flag, "-I"))
+ } else if flag == "-isystem" {
+ includes = append(includes, flags[i+1])
+ }
+ }
+
+ want := []string{
+ "${config.ArmClangThumbCflags}",
+ "${config.ArmClangCflags}",
+ "${config.CommonClangGlobalCflags}",
+ "${config.DeviceClangGlobalCflags}",
+ "${config.ClangExternalCflags}",
+ "${config.ArmToolchainClangCflags}",
+ "${config.ArmClangArmv7ANeonCflags}",
+ "${config.ArmClangGenericCflags}",
+ "export_include_dirs",
+ "linux_export_include_dirs",
+ "android_export_include_dirs",
+ "arm_export_include_dirs",
+ "lib32_export_include_dirs",
+ "android_arm_export_include_dirs",
+ "android_arm_local_include_dirs",
+ "lib32_local_include_dirs",
+ "arm_local_include_dirs",
+ "android_local_include_dirs",
+ "linux_local_include_dirs",
+ "local_include_dirs",
+ ".",
+ "libheader1",
+ "libheader2",
+ "libwhole1",
+ "libwhole2",
+ "libstatic1",
+ "libstatic2",
+ "libshared1",
+ "libshared2",
+ "liblinux",
+ "libandroid",
+ "libarm",
+ "lib32",
+ "libandroid_arm",
+ "defaults/cc/common/ndk_libc++_shared",
+ "defaults/cc/common/ndk_libandroid_support",
+ "out/soong/ndk/sysroot/usr/include",
+ "out/soong/ndk/sysroot/usr/include/arm-linux-androideabi",
+ "${config.NoOverrideClangGlobalCflags}",
+ }
+
+ android.AssertArrayString(t, "includes", want, includes)
+}
diff --git a/cc/image.go b/cc/image.go
index c9c0e63..15ec1c8 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -341,11 +341,11 @@
}
func (m *Module) ExtraVariants() []string {
- return m.Properties.ExtraVariants
+ return m.Properties.ExtraVersionedImageVariations
}
func (m *Module) AppendExtraVariant(extraVariant string) {
- m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, extraVariant)
+ m.Properties.ExtraVersionedImageVariations = append(m.Properties.ExtraVersionedImageVariations, extraVariant)
}
func (m *Module) SetRamdiskVariantNeeded(b bool) {
@@ -629,7 +629,7 @@
}
func (c *Module) ExtraImageVariations(ctx android.BaseModuleContext) []string {
- return c.Properties.ExtraVariants
+ return c.Properties.ExtraVersionedImageVariations
}
func squashVendorSrcs(m *Module) {
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/library.go b/cc/library.go
index 28605f5..5b6c623 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -28,6 +28,7 @@
"android/soong/android"
"android/soong/bazel"
+ "android/soong/bazel/cquery"
"android/soong/cc/config"
)
@@ -236,29 +237,12 @@
Linkopts bazel.StringListAttribute
Use_libcrt bazel.BoolAttribute
- // Attributes pertaining to shared variant.
- Shared_srcs bazel.LabelListAttribute
- Shared_srcs_c bazel.LabelListAttribute
- Shared_srcs_as bazel.LabelListAttribute
- Shared_copts bazel.StringListAttribute
+ // This is shared only.
+ Version_script bazel.LabelAttribute
- Exported_deps_for_shared bazel.LabelListAttribute
- Static_deps_for_shared bazel.LabelListAttribute
- Dynamic_deps_for_shared bazel.LabelListAttribute
- Whole_archive_deps_for_shared bazel.LabelListAttribute
- User_link_flags bazel.StringListAttribute
- Version_script bazel.LabelAttribute
-
- // Attributes pertaining to static variant.
- Static_srcs bazel.LabelListAttribute
- Static_srcs_c bazel.LabelListAttribute
- Static_srcs_as bazel.LabelListAttribute
- Static_copts bazel.StringListAttribute
-
- Exported_deps_for_static bazel.LabelListAttribute
- Static_deps_for_static bazel.LabelListAttribute
- Dynamic_deps_for_static bazel.LabelListAttribute
- Whole_archive_deps_for_static bazel.LabelListAttribute
+ // Common properties shared between both shared and static variants.
+ Shared staticOrSharedAttributes
+ Static staticOrSharedAttributes
Strip stripAttributes
}
@@ -333,6 +317,8 @@
Linkopts: linkerAttrs.linkopts,
Use_libcrt: linkerAttrs.useLibcrt,
+ Version_script: linkerAttrs.versionScript,
+
Strip: stripAttributes{
Keep_symbols: linkerAttrs.stripKeepSymbols,
Keep_symbols_and_debug_frame: linkerAttrs.stripKeepSymbolsAndDebugFrame,
@@ -341,22 +327,9 @@
None: linkerAttrs.stripNone,
},
- Shared_srcs: sharedAttrs.srcs,
- Shared_srcs_c: sharedAttrs.srcs_c,
- Shared_srcs_as: sharedAttrs.srcs_as,
- Shared_copts: sharedAttrs.copts,
- Static_deps_for_shared: sharedAttrs.staticDeps,
- Whole_archive_deps_for_shared: sharedAttrs.wholeArchiveDeps,
- Dynamic_deps_for_shared: sharedAttrs.dynamicDeps,
- Version_script: linkerAttrs.versionScript,
+ Shared: sharedAttrs,
- Static_srcs: staticAttrs.srcs,
- Static_srcs_c: staticAttrs.srcs_c,
- Static_srcs_as: staticAttrs.srcs_as,
- Static_copts: staticAttrs.copts,
- Static_deps_for_static: staticAttrs.staticDeps,
- Whole_archive_deps_for_static: staticAttrs.wholeArchiveDeps,
- Dynamic_deps_for_static: staticAttrs.dynamicDeps,
+ Static: staticAttrs,
}
props := bazel.BazelTargetModuleProperties{
@@ -579,22 +552,10 @@
module *Module
}
-func (handler *ccLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
- if !handler.module.static() {
- // TODO(cparsons): Support shared libraries.
- return false
- }
- bazelCtx := ctx.Config().BazelContext
- ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
- if err != nil {
- ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
- return false
- }
- if !ok {
- return ok
- }
+// generateStaticBazelBuildActions constructs the StaticLibraryInfo Soong
+// provider from a Bazel shared library's CcInfo provider.
+func (handler *ccLibraryBazelHandler) generateStaticBazelBuildActions(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool {
rootStaticArchives := ccInfo.RootStaticArchives
- objPaths := ccInfo.CcObjectFiles
if len(rootStaticArchives) != 1 {
ctx.ModuleErrorf("expected exactly one root archive file for '%s', but got %s", label, rootStaticArchives)
return false
@@ -602,6 +563,7 @@
outputFilePath := android.PathForBazelOut(ctx, rootStaticArchives[0])
handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
+ objPaths := ccInfo.CcObjectFiles
objFiles := make(android.Paths, len(objPaths))
for i, objPath := range objPaths {
objFiles[i] = android.PathForBazelOut(ctx, objPath)
@@ -615,26 +577,109 @@
ReuseObjects: objects,
Objects: objects,
- // TODO(cparsons): Include transitive static libraries in this provider to support
+ // TODO(b/190524881): Include transitive static libraries in this provider to support
// static libraries with deps.
TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL).
Direct(outputFilePath).
Build(),
})
- ctx.SetProvider(FlagExporterInfoProvider, flagExporterInfoFromCcInfo(ctx, ccInfo))
+ return true
+}
+
+// generateSharedBazelBuildActions constructs the SharedLibraryInfo Soong
+// provider from a Bazel shared library's CcInfo provider.
+func (handler *ccLibraryBazelHandler) generateSharedBazelBuildActions(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool {
+ rootDynamicLibraries := ccInfo.RootDynamicLibraries
+
+ if len(rootDynamicLibraries) != 1 {
+ ctx.ModuleErrorf("expected exactly one root dynamic library file for '%s', but got %s", label, rootDynamicLibraries)
+ return false
+ }
+ outputFilePath := android.PathForBazelOut(ctx, rootDynamicLibraries[0])
+ handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
+
+ handler.module.linker.(*libraryDecorator).unstrippedOutputFile = outputFilePath
+
+ tocFile := getTocFile(ctx, label, ccInfo.OutputFiles)
+ handler.module.linker.(*libraryDecorator).tocFile = tocFile
+
+ ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
+ TableOfContents: tocFile,
+ SharedLibrary: outputFilePath,
+ Target: ctx.Target(),
+ // TODO(b/190524881): Include transitive static libraries in this provider to support
+ // static libraries with deps. The provider key for this is TransitiveStaticLibrariesForOrdering.
+ })
+ return true
+}
+
+// getTocFile looks for the .so.toc file in the target's output files, if any. The .so.toc file
+// contains the table of contents of all symbols of a shared object.
+func getTocFile(ctx android.ModuleContext, label string, outputFiles []string) android.OptionalPath {
+ var tocFile string
+ for _, file := range outputFiles {
+ if strings.HasSuffix(file, ".so.toc") {
+ if tocFile != "" {
+ ctx.ModuleErrorf("The %s target cannot produce more than 1 .toc file.", label)
+ }
+ tocFile = file
+ // Don't break to validate that there are no multiple .toc files per .so.
+ }
+ }
+ if tocFile == "" {
+ return android.OptionalPath{}
+ }
+ return android.OptionalPathForPath(android.PathForBazelOut(ctx, tocFile))
+}
+
+func (handler *ccLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+ bazelCtx := ctx.Config().BazelContext
+ ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
+ if err != nil {
+ ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
+ return false
+ }
+ if !ok {
+ return ok
+ }
+
+ if handler.module.static() {
+ if ok := handler.generateStaticBazelBuildActions(ctx, label, ccInfo); !ok {
+ return false
+ }
+ } else if handler.module.Shared() {
+ if ok := handler.generateSharedBazelBuildActions(ctx, label, ccInfo); !ok {
+ return false
+ }
+ } else {
+ return false
+ }
+
+ handler.module.linker.(*libraryDecorator).setFlagExporterInfoFromCcInfo(ctx, ccInfo)
+ handler.module.maybeUnhideFromMake()
+
if i, ok := handler.module.linker.(snapshotLibraryInterface); ok {
// Dependencies on this library will expect collectedSnapshotHeaders to
// be set, otherwise validation will fail. For now, set this to an empty
// list.
- // TODO(cparsons): More closely mirror the collectHeadersForSnapshot
+ // TODO(b/190533363): More closely mirror the collectHeadersForSnapshot
// implementation.
i.(*libraryDecorator).collectedSnapshotHeaders = android.Paths{}
}
-
return ok
}
+func (library *libraryDecorator) setFlagExporterInfoFromCcInfo(ctx android.ModuleContext, ccInfo cquery.CcInfo) {
+ flagExporterInfo := flagExporterInfoFromCcInfo(ctx, ccInfo)
+ // flag exporters consolidates properties like includes, flags, dependencies that should be
+ // exported from this module to other modules
+ ctx.SetProvider(FlagExporterInfoProvider, flagExporterInfo)
+ // Store flag info to be passed along to androidmk
+ // TODO(b/184387147): Androidmk should be done in Bazel, not Soong.
+ library.flagExporterInfo = &flagExporterInfo
+}
+
func GlobHeadersForSnapshot(ctx android.ModuleContext, paths android.Paths) android.Paths {
ret := android.Paths{}
@@ -1130,8 +1175,8 @@
deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.StaticProperties.Static.Export_static_lib_headers...)
} else if library.shared() {
if ctx.toolchain().Bionic() && !Bool(library.baseLinker.Properties.Nocrt) {
- deps.CrtBegin = "crtbegin_so"
- deps.CrtEnd = "crtend_so"
+ deps.CrtBegin = []string{"crtbegin_so"}
+ deps.CrtEnd = []string{"crtend_so"}
}
deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.SharedProperties.Shared.Whole_static_libs...)
deps.StaticLibs = append(deps.StaticLibs, library.SharedProperties.Shared.Static_libs...)
@@ -1361,7 +1406,7 @@
linkerDeps = append(linkerDeps, objs.tidyFiles...)
transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
- linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs)
+ linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, nil)
objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 2065929..d6b4529 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -73,13 +73,7 @@
// HeaderLibraryInfo is an empty struct to indicate to dependencies that this is a header library
ctx.SetProvider(HeaderLibraryInfoProvider, HeaderLibraryInfo{})
- flagExporterInfo := flagExporterInfoFromCcInfo(ctx, ccInfo)
- // Store flag info to be passed along to androimk
- // TODO(b/184387147): Androidmk should be done in Bazel, not Soong.
- h.library.flagExporterInfo = &flagExporterInfo
- // flag exporters consolidates properties like includes, flags, dependencies that should be
- // exported from this module to other modules
- ctx.SetProvider(FlagExporterInfoProvider, flagExporterInfo)
+ h.library.setFlagExporterInfoFromCcInfo(ctx, ccInfo)
// Dependencies on this library will expect collectedSnapshotHeaders to be set, otherwise
// validation will fail. For now, set this to an empty list.
diff --git a/cc/library_test.go b/cc/library_test.go
index 7975275..ba372a8 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -19,6 +19,7 @@
"testing"
"android/soong/android"
+ "android/soong/bazel/cquery"
)
func TestLibraryReuse(t *testing.T) {
@@ -240,3 +241,48 @@
testCcError(t, `"libfoo" .*: versions: "X" could not be parsed as an integer and is not a recognized codename`, bp)
}
+
+func TestCcLibraryWithBazel(t *testing.T) {
+ bp := `
+cc_library {
+ name: "foo",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo/bar:bar" },
+}`
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//foo/bar:bar": cquery.CcInfo{
+ CcObjectFiles: []string{"foo.o"},
+ Includes: []string{"include"},
+ SystemIncludes: []string{"system_include"},
+ RootStaticArchives: []string{"foo.a"},
+ RootDynamicLibraries: []string{"foo.so"},
+ },
+ },
+ }
+ ctx := testCcWithConfig(t, config)
+
+ staticFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_static").Module()
+ outputFiles, err := staticFoo.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+ }
+
+ expectedOutputFiles := []string{"outputbase/execroot/__main__/foo.a"}
+ android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+
+ sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+ outputFiles, err = sharedFoo.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+ }
+ expectedOutputFiles = []string{"outputbase/execroot/__main__/foo.so"}
+ android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+
+ entries := android.AndroidMkEntriesForTest(t, ctx, sharedFoo)[0]
+ expectedFlags := []string{"-Ioutputbase/execroot/__main__/include", "-isystem outputbase/execroot/__main__/system_include"}
+ gotFlags := entries.EntryMap["LOCAL_EXPORT_CFLAGS"]
+ android.AssertDeepEquals(t, "androidmk exported cflags", expectedFlags, gotFlags)
+}
diff --git a/cc/linker.go b/cc/linker.go
index 895931a..d9ee0cf 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -122,7 +122,7 @@
// version script for vendor or product variant
Version_script *string `android:"arch_variant"`
- }
+ } `android:"arch_variant"`
Recovery struct {
// list of shared libs that only should be used to build the recovery
// variant of the C/C++ module.
@@ -182,7 +182,7 @@
// variant of the C/C++ module.
Exclude_static_libs []string
}
- }
+ } `android:"arch_variant"`
// make android::build:GetBuildNumber() available containing the build ID.
Use_version_lib *bool `android:"arch_variant"`
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index 4a9601b..2726b1a 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -144,13 +144,13 @@
Inputs: licensePaths,
})
- baseDepPaths := append(installPaths, combinedLicense,
- getNdkAbiDiffTimestampFile(ctx))
+ baseDepPaths := append(installPaths, combinedLicense)
ctx.Build(pctx, android.BuildParams{
- Rule: android.Touch,
- Output: getNdkBaseTimestampFile(ctx),
- Implicits: baseDepPaths,
+ Rule: android.Touch,
+ Output: getNdkBaseTimestampFile(ctx),
+ Implicits: baseDepPaths,
+ Validation: getNdkAbiDiffTimestampFile(ctx),
})
fullDepPaths := append(staticLibInstallPaths, getNdkBaseTimestampFile(ctx))
diff --git a/cc/ndkstubgen/__init__.py b/cc/ndkstubgen/__init__.py
index 2387e69..5e6b8f5 100755
--- a/cc/ndkstubgen/__init__.py
+++ b/cc/ndkstubgen/__init__.py
@@ -108,7 +108,14 @@
parser.add_argument(
'--llndk', action='store_true', help='Use the LLNDK variant.')
parser.add_argument(
- '--apex', action='store_true', help='Use the APEX variant.')
+ '--apex',
+ action='store_true',
+ help='Use the APEX variant. Note: equivalent to --system-api.')
+ parser.add_argument(
+ '--system-api',
+ action='store_true',
+ dest='apex',
+ help='Use the SystemAPI variant. Note: equivalent to --apex.')
parser.add_argument('--api-map',
type=resolved_path,
diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py
index 3dbab61..09551ea 100755
--- a/cc/ndkstubgen/test_ndkstubgen.py
+++ b/cc/ndkstubgen/test_ndkstubgen.py
@@ -19,9 +19,10 @@
import textwrap
import unittest
-import ndkstubgen
import symbolfile
-from symbolfile import Arch, Tag
+from symbolfile import Arch, Tags
+
+import ndkstubgen
# pylint: disable=missing-docstring
@@ -38,23 +39,25 @@
version_file, symbol_list_file,
Arch('arm'), 9, False, False)
- version = symbolfile.Version('VERSION_PRIVATE', None, [], [
- symbolfile.Symbol('foo', []),
+ version = symbolfile.Version('VERSION_PRIVATE', None, Tags(), [
+ symbolfile.Symbol('foo', Tags()),
])
generator.write_version(version)
self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue())
- version = symbolfile.Version('VERSION', None, [Tag('x86')], [
- symbolfile.Symbol('foo', []),
- ])
+ version = symbolfile.Version('VERSION', None, Tags.from_strs(['x86']),
+ [
+ symbolfile.Symbol('foo', Tags()),
+ ])
generator.write_version(version)
self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue())
- version = symbolfile.Version('VERSION', None, [Tag('introduced=14')], [
- symbolfile.Symbol('foo', []),
- ])
+ version = symbolfile.Version('VERSION', None,
+ Tags.from_strs(['introduced=14']), [
+ symbolfile.Symbol('foo', Tags()),
+ ])
generator.write_version(version)
self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue())
@@ -69,29 +72,29 @@
version_file, symbol_list_file,
Arch('arm'), 9, False, False)
- version = symbolfile.Version('VERSION_1', None, [], [
- symbolfile.Symbol('foo', [Tag('x86')]),
+ version = symbolfile.Version('VERSION_1', None, Tags(), [
+ symbolfile.Symbol('foo', Tags.from_strs(['x86'])),
])
generator.write_version(version)
self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue())
- version = symbolfile.Version('VERSION_1', None, [], [
- symbolfile.Symbol('foo', [Tag('introduced=14')]),
+ version = symbolfile.Version('VERSION_1', None, Tags(), [
+ symbolfile.Symbol('foo', Tags.from_strs(['introduced=14'])),
])
generator.write_version(version)
self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue())
- version = symbolfile.Version('VERSION_1', None, [], [
- symbolfile.Symbol('foo', [Tag('llndk')]),
+ version = symbolfile.Version('VERSION_1', None, Tags(), [
+ symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
])
generator.write_version(version)
self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue())
- version = symbolfile.Version('VERSION_1', None, [], [
- symbolfile.Symbol('foo', [Tag('apex')]),
+ version = symbolfile.Version('VERSION_1', None, Tags(), [
+ symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
])
generator.write_version(version)
self.assertEqual('', src_file.getvalue())
@@ -106,18 +109,17 @@
Arch('arm'), 9, False, False)
versions = [
- symbolfile.Version('VERSION_1', None, [], [
- symbolfile.Symbol('foo', []),
- symbolfile.Symbol('bar', [Tag('var')]),
- symbolfile.Symbol('woodly', [Tag('weak')]),
- symbolfile.Symbol('doodly',
- [Tag('weak'), Tag('var')]),
+ symbolfile.Version('VERSION_1', None, Tags(), [
+ symbolfile.Symbol('foo', Tags()),
+ symbolfile.Symbol('bar', Tags.from_strs(['var'])),
+ symbolfile.Symbol('woodly', Tags.from_strs(['weak'])),
+ symbolfile.Symbol('doodly', Tags.from_strs(['weak', 'var'])),
]),
- symbolfile.Version('VERSION_2', 'VERSION_1', [], [
- symbolfile.Symbol('baz', []),
+ symbolfile.Version('VERSION_2', 'VERSION_1', Tags(), [
+ symbolfile.Symbol('baz', Tags()),
]),
- symbolfile.Version('VERSION_3', 'VERSION_1', [], [
- symbolfile.Symbol('qux', [Tag('versioned=14')]),
+ symbolfile.Version('VERSION_3', 'VERSION_1', Tags(), [
+ symbolfile.Symbol('qux', Tags.from_strs(['versioned=14'])),
]),
]
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/sdk.go b/cc/sdk.go
index aec950b..69ad311 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -35,7 +35,8 @@
if !m.UseSdk() && !m.SplitPerApiLevel() {
ctx.ModuleErrorf("UseSdk() must return true when AlwaysSdk is set, did the factory forget to set Sdk_version?")
}
- ctx.CreateVariations("sdk")
+ modules := ctx.CreateVariations("sdk")
+ modules[0].(*Module).Properties.IsSdkVariant = true
} else if m.UseSdk() || m.SplitPerApiLevel() {
modules := ctx.CreateVariations("", "sdk")
diff --git a/cc/stl.go b/cc/stl.go
index 75921c6..06dc840 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -199,7 +199,9 @@
deps.StaticLibs = append(deps.StaticLibs, stl.Properties.SelectedStl, "ndk_libc++abi")
}
if needsLibAndroidSupport(ctx) {
- deps.StaticLibs = append(deps.StaticLibs, "ndk_libandroid_support")
+ // Use LateStaticLibs for ndk_libandroid_support so that its include directories
+ // come after ndk_libc++_static or ndk_libc++_shared.
+ deps.LateStaticLibs = append(deps.LateStaticLibs, "ndk_libandroid_support")
}
deps.StaticLibs = append(deps.StaticLibs, "ndk_libunwind")
default:
diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py
index 5678e7d..31c4443 100644
--- a/cc/symbolfile/__init__.py
+++ b/cc/symbolfile/__init__.py
@@ -14,18 +14,22 @@
# limitations under the License.
#
"""Parser for Android's version script information."""
-from dataclasses import dataclass
+from __future__ import annotations
+
+from dataclasses import dataclass, field
import logging
import re
from typing import (
Dict,
Iterable,
+ Iterator,
List,
Mapping,
NewType,
Optional,
TextIO,
Tuple,
+ Union,
)
@@ -52,11 +56,52 @@
@dataclass
+class Tags:
+ """Container class for the tags attached to a symbol or version."""
+
+ tags: tuple[Tag, ...] = field(default_factory=tuple)
+
+ @classmethod
+ def from_strs(cls, strs: Iterable[str]) -> Tags:
+ """Constructs tags from a collection of strings.
+
+ Does not decode API levels.
+ """
+ return Tags(tuple(Tag(s) for s in strs))
+
+ def __contains__(self, tag: Union[Tag, str]) -> bool:
+ return tag in self.tags
+
+ def __iter__(self) -> Iterator[Tag]:
+ yield from self.tags
+
+ @property
+ def has_mode_tags(self) -> bool:
+ """Returns True if any mode tags (apex, llndk, etc) are set."""
+ return self.has_apex_tags or self.has_llndk_tags
+
+ @property
+ def has_apex_tags(self) -> bool:
+ """Returns True if any APEX tags are set."""
+ return 'apex' in self.tags or 'systemapi' in self.tags
+
+ @property
+ def has_llndk_tags(self) -> bool:
+ """Returns True if any LL-NDK tags are set."""
+ return 'llndk' in self.tags
+
+ @property
+ def has_platform_only_tags(self) -> bool:
+ """Returns True if any platform-only tags are set."""
+ return 'platform-only' in self.tags
+
+
+@dataclass
class Symbol:
"""A symbol definition from a symbol file."""
name: str
- tags: List[Tag]
+ tags: Tags
@dataclass
@@ -65,14 +110,22 @@
name: str
base: Optional[str]
- tags: List[Tag]
+ tags: Tags
symbols: List[Symbol]
+ @property
+ def is_private(self) -> bool:
+ """Returns True if this version block is private (platform only)."""
+ return self.name.endswith('_PRIVATE') or self.name.endswith('_PLATFORM')
-def get_tags(line: str) -> List[Tag]:
+
+def get_tags(line: str, api_map: ApiMap) -> Tags:
"""Returns a list of all tags on this line."""
_, _, all_tags = line.strip().partition('#')
- return [Tag(e) for e in re.split(r'\s+', all_tags) if e.strip()]
+ return Tags(tuple(
+ decode_api_level_tag(Tag(e), api_map)
+ for e in re.split(r'\s+', all_tags) if e.strip()
+ ))
def is_api_level_tag(tag: Tag) -> bool:
@@ -104,24 +157,21 @@
return api_map[api]
-def decode_api_level_tags(tags: Iterable[Tag], api_map: ApiMap) -> List[Tag]:
- """Decodes API level code names in a list of tags.
+def decode_api_level_tag(tag: Tag, api_map: ApiMap) -> Tag:
+ """Decodes API level code name in a tag.
Raises:
ParseError: An unknown version name was found in a tag.
"""
- decoded_tags = list(tags)
- for idx, tag in enumerate(tags):
- if not is_api_level_tag(tag):
- continue
- name, value = split_tag(tag)
+ if not is_api_level_tag(tag):
+ return tag
- try:
- decoded = str(decode_api_level(value, api_map))
- decoded_tags[idx] = Tag('='.join([name, decoded]))
- except KeyError:
- raise ParseError(f'Unknown version name in tag: {tag}')
- return decoded_tags
+ name, value = split_tag(tag)
+ try:
+ decoded = str(decode_api_level(value, api_map))
+ return Tag(f'{name}={decoded}')
+ except KeyError as ex:
+ raise ParseError(f'Unknown version name in tag: {tag}') from ex
def split_tag(tag: Tag) -> Tuple[str, str]:
@@ -149,55 +199,52 @@
return split_tag(tag)[1]
-def version_is_private(version: str) -> bool:
- """Returns True if the version name should be treated as private."""
- return version.endswith('_PRIVATE') or version.endswith('_PLATFORM')
+def _should_omit_tags(tags: Tags, arch: Arch, api: int, llndk: bool,
+ apex: bool) -> bool:
+ """Returns True if the tagged object should be omitted.
+
+ This defines the rules shared between version tagging and symbol tagging.
+ """
+ # The apex and llndk tags will only exclude APIs from other modes. If in
+ # APEX or LLNDK mode and neither tag is provided, we fall back to the
+ # default behavior because all NDK symbols are implicitly available to APEX
+ # and LLNDK.
+ if tags.has_mode_tags:
+ if not apex and not llndk:
+ return True
+ if apex and not tags.has_apex_tags:
+ return True
+ if llndk and not tags.has_llndk_tags:
+ return True
+ if not symbol_in_arch(tags, arch):
+ return True
+ if not symbol_in_api(tags, arch, api):
+ return True
+ return False
def should_omit_version(version: Version, arch: Arch, api: int, llndk: bool,
apex: bool) -> bool:
- """Returns True if the version section should be ommitted.
+ """Returns True if the version section should be omitted.
We want to omit any sections that do not have any symbols we'll have in the
stub library. Sections that contain entirely future symbols or only symbols
for certain architectures.
"""
- if version_is_private(version.name):
+ if version.is_private:
return True
- if 'platform-only' in version.tags:
+ if version.tags.has_platform_only_tags:
return True
-
- no_llndk_no_apex = ('llndk' not in version.tags
- and 'apex' not in version.tags)
- keep = no_llndk_no_apex or \
- ('llndk' in version.tags and llndk) or \
- ('apex' in version.tags and apex)
- if not keep:
- return True
- if not symbol_in_arch(version.tags, arch):
- return True
- if not symbol_in_api(version.tags, arch, api):
- return True
- return False
+ return _should_omit_tags(version.tags, arch, api, llndk, apex)
def should_omit_symbol(symbol: Symbol, arch: Arch, api: int, llndk: bool,
apex: bool) -> bool:
"""Returns True if the symbol should be omitted."""
- no_llndk_no_apex = 'llndk' not in symbol.tags and 'apex' not in symbol.tags
- keep = no_llndk_no_apex or \
- ('llndk' in symbol.tags and llndk) or \
- ('apex' in symbol.tags and apex)
- if not keep:
- return True
- if not symbol_in_arch(symbol.tags, arch):
- return True
- if not symbol_in_api(symbol.tags, arch, api):
- return True
- return False
+ return _should_omit_tags(symbol.tags, arch, api, llndk, apex)
-def symbol_in_arch(tags: Iterable[Tag], arch: Arch) -> bool:
+def symbol_in_arch(tags: Tags, arch: Arch) -> bool:
"""Returns true if the symbol is present for the given architecture."""
has_arch_tags = False
for tag in tags:
@@ -325,8 +372,7 @@
"""Parses a single version section and returns a Version object."""
assert self.current_line is not None
name = self.current_line.split('{')[0].strip()
- tags = get_tags(self.current_line)
- tags = decode_api_level_tags(tags, self.api_map)
+ tags = get_tags(self.current_line, self.api_map)
symbols: List[Symbol] = []
global_scope = True
cpp_symbols = False
@@ -373,8 +419,7 @@
'Wildcard global symbols are not permitted.')
# Line is now in the format "<symbol-name>; # tags"
name, _, _ = self.current_line.strip().partition(';')
- tags = get_tags(self.current_line)
- tags = decode_api_level_tags(tags, self.api_map)
+ tags = get_tags(self.current_line, self.api_map)
return Symbol(name, tags)
def next_line(self) -> str:
diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py
index 92b1399..c1e8219 100644
--- a/cc/symbolfile/test_symbolfile.py
+++ b/cc/symbolfile/test_symbolfile.py
@@ -19,7 +19,7 @@
import unittest
import symbolfile
-from symbolfile import Arch, Tag
+from symbolfile import Arch, Tag, Tags, Version
# pylint: disable=missing-docstring
@@ -35,12 +35,14 @@
class TagsTest(unittest.TestCase):
def test_get_tags_no_tags(self) -> None:
- self.assertEqual([], symbolfile.get_tags(''))
- self.assertEqual([], symbolfile.get_tags('foo bar baz'))
+ self.assertEqual(Tags(), symbolfile.get_tags('', {}))
+ self.assertEqual(Tags(), symbolfile.get_tags('foo bar baz', {}))
def test_get_tags(self) -> None:
- self.assertEqual(['foo', 'bar'], symbolfile.get_tags('# foo bar'))
- self.assertEqual(['bar', 'baz'], symbolfile.get_tags('foo # bar baz'))
+ self.assertEqual(Tags.from_strs(['foo', 'bar']),
+ symbolfile.get_tags('# foo bar', {}))
+ self.assertEqual(Tags.from_strs(['bar', 'baz']),
+ symbolfile.get_tags('foo # bar baz', {}))
def test_split_tag(self) -> None:
self.assertTupleEqual(('foo', 'bar'),
@@ -77,12 +79,14 @@
}
tags = [
- Tag('introduced=9'),
- Tag('introduced-arm=14'),
- Tag('versioned=16'),
- Tag('arm'),
- Tag('introduced=O'),
- Tag('introduced=P'),
+ symbolfile.decode_api_level_tag(t, api_map) for t in (
+ Tag('introduced=9'),
+ Tag('introduced-arm=14'),
+ Tag('versioned=16'),
+ Tag('arm'),
+ Tag('introduced=O'),
+ Tag('introduced=P'),
+ )
]
expected_tags = [
Tag('introduced=9'),
@@ -92,33 +96,37 @@
Tag('introduced=9000'),
Tag('introduced=9001'),
]
- self.assertListEqual(
- expected_tags, symbolfile.decode_api_level_tags(tags, api_map))
+ self.assertListEqual(expected_tags, tags)
with self.assertRaises(symbolfile.ParseError):
- symbolfile.decode_api_level_tags([Tag('introduced=O')], {})
+ symbolfile.decode_api_level_tag(Tag('introduced=O'), {})
class PrivateVersionTest(unittest.TestCase):
def test_version_is_private(self) -> None:
- self.assertFalse(symbolfile.version_is_private('foo'))
- self.assertFalse(symbolfile.version_is_private('PRIVATE'))
- self.assertFalse(symbolfile.version_is_private('PLATFORM'))
- self.assertFalse(symbolfile.version_is_private('foo_private'))
- self.assertFalse(symbolfile.version_is_private('foo_platform'))
- self.assertFalse(symbolfile.version_is_private('foo_PRIVATE_'))
- self.assertFalse(symbolfile.version_is_private('foo_PLATFORM_'))
+ def mock_version(name: str) -> Version:
+ return Version(name, base=None, tags=Tags(), symbols=[])
- self.assertTrue(symbolfile.version_is_private('foo_PRIVATE'))
- self.assertTrue(symbolfile.version_is_private('foo_PLATFORM'))
+ self.assertFalse(mock_version('foo').is_private)
+ self.assertFalse(mock_version('PRIVATE').is_private)
+ self.assertFalse(mock_version('PLATFORM').is_private)
+ self.assertFalse(mock_version('foo_private').is_private)
+ self.assertFalse(mock_version('foo_platform').is_private)
+ self.assertFalse(mock_version('foo_PRIVATE_').is_private)
+ self.assertFalse(mock_version('foo_PLATFORM_').is_private)
+
+ self.assertTrue(mock_version('foo_PRIVATE').is_private)
+ self.assertTrue(mock_version('foo_PLATFORM').is_private)
class SymbolPresenceTest(unittest.TestCase):
def test_symbol_in_arch(self) -> None:
- self.assertTrue(symbolfile.symbol_in_arch([], Arch('arm')))
- self.assertTrue(symbolfile.symbol_in_arch([Tag('arm')], Arch('arm')))
+ self.assertTrue(symbolfile.symbol_in_arch(Tags(), Arch('arm')))
+ self.assertTrue(
+ symbolfile.symbol_in_arch(Tags.from_strs(['arm']), Arch('arm')))
- self.assertFalse(symbolfile.symbol_in_arch([Tag('x86')], Arch('arm')))
+ self.assertFalse(
+ symbolfile.symbol_in_arch(Tags.from_strs(['x86']), Arch('arm')))
def test_symbol_in_api(self) -> None:
self.assertTrue(symbolfile.symbol_in_api([], Arch('arm'), 9))
@@ -197,81 +205,99 @@
def test_omit_private(self) -> None:
self.assertFalse(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
- False))
+ symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
+ False, False))
self.assertTrue(
symbolfile.should_omit_version(
- symbolfile.Version('foo_PRIVATE', None, [], []), Arch('arm'),
- 9, False, False))
+ symbolfile.Version('foo_PRIVATE', None, Tags(), []),
+ Arch('arm'), 9, False, False))
self.assertTrue(
symbolfile.should_omit_version(
- symbolfile.Version('foo_PLATFORM', None, [], []), Arch('arm'),
- 9, False, False))
+ symbolfile.Version('foo_PLATFORM', None, Tags(), []),
+ Arch('arm'), 9, False, False))
self.assertTrue(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [Tag('platform-only')], []),
+ symbolfile.Version('foo', None,
+ Tags.from_strs(['platform-only']), []),
Arch('arm'), 9, False, False))
def test_omit_llndk(self) -> None:
self.assertTrue(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [Tag('llndk')], []),
+ symbolfile.Version('foo', None, Tags.from_strs(['llndk']), []),
Arch('arm'), 9, False, False))
self.assertFalse(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [], []), Arch('arm'), 9, True,
- False))
+ symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
+ True, False))
self.assertFalse(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [Tag('llndk')], []),
+ symbolfile.Version('foo', None, Tags.from_strs(['llndk']), []),
Arch('arm'), 9, True, False))
def test_omit_apex(self) -> None:
self.assertTrue(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [Tag('apex')], []),
+ symbolfile.Version('foo', None, Tags.from_strs(['apex']), []),
Arch('arm'), 9, False, False))
self.assertFalse(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
- True))
+ symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
+ False, True))
self.assertFalse(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [Tag('apex')], []),
+ symbolfile.Version('foo', None, Tags.from_strs(['apex']), []),
Arch('arm'), 9, False, True))
+ def test_omit_systemapi(self) -> None:
+ self.assertTrue(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, Tags.from_strs(['systemapi']),
+ []), Arch('arm'), 9, False, False))
+
+ self.assertFalse(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
+ False, True))
+ self.assertFalse(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, Tags.from_strs(['systemapi']),
+ []), Arch('arm'), 9, False, True))
+
def test_omit_arch(self) -> None:
self.assertFalse(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
- False))
+ symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
+ False, False))
self.assertFalse(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [Tag('arm')], []), Arch('arm'),
- 9, False, False))
-
- self.assertTrue(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [Tag('x86')], []), Arch('arm'),
- 9, False, False))
-
- def test_omit_api(self) -> None:
- self.assertFalse(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
- False))
- self.assertFalse(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [Tag('introduced=9')], []),
+ symbolfile.Version('foo', None, Tags.from_strs(['arm']), []),
Arch('arm'), 9, False, False))
self.assertTrue(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [Tag('introduced=14')], []),
+ symbolfile.Version('foo', None, Tags.from_strs(['x86']), []),
+ Arch('arm'), 9, False, False))
+
+ def test_omit_api(self) -> None:
+ self.assertFalse(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
+ False, False))
+ self.assertFalse(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None,
+ Tags.from_strs(['introduced=9']), []),
+ Arch('arm'), 9, False, False))
+
+ self.assertTrue(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None,
+ Tags.from_strs(['introduced=14']), []),
Arch('arm'), 9, False, False))
@@ -279,58 +305,72 @@
def test_omit_llndk(self) -> None:
self.assertTrue(
symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', [Tag('llndk')]), Arch('arm'), 9,
- False, False))
+ symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
+ Arch('arm'), 9, False, False))
self.assertFalse(
- symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
Arch('arm'), 9, True, False))
self.assertFalse(
symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', [Tag('llndk')]), Arch('arm'), 9, True,
- False))
+ symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
+ Arch('arm'), 9, True, False))
def test_omit_apex(self) -> None:
self.assertTrue(
symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', [Tag('apex')]), Arch('arm'), 9, False,
- False))
+ symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
+ Arch('arm'), 9, False, False))
self.assertFalse(
- symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
Arch('arm'), 9, False, True))
self.assertFalse(
symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', [Tag('apex')]), Arch('arm'), 9, False,
- True))
+ symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
+ Arch('arm'), 9, False, True))
+
+ def test_omit_systemapi(self) -> None:
+ self.assertTrue(
+ symbolfile.should_omit_symbol(
+ symbolfile.Symbol('foo', Tags.from_strs(['systemapi'])),
+ Arch('arm'), 9, False, False))
+
+ self.assertFalse(
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
+ Arch('arm'), 9, False, True))
+ self.assertFalse(
+ symbolfile.should_omit_symbol(
+ symbolfile.Symbol('foo', Tags.from_strs(['systemapi'])),
+ Arch('arm'), 9, False, True))
def test_omit_arch(self) -> None:
self.assertFalse(
- symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
Arch('arm'), 9, False, False))
self.assertFalse(
symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', [Tag('arm')]), Arch('arm'), 9, False,
- False))
+ symbolfile.Symbol('foo', Tags.from_strs(['arm'])), Arch('arm'),
+ 9, False, False))
self.assertTrue(
symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', [Tag('x86')]), Arch('arm'), 9, False,
- False))
+ symbolfile.Symbol('foo', Tags.from_strs(['x86'])), Arch('arm'),
+ 9, False, False))
def test_omit_api(self) -> None:
self.assertFalse(
- symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
Arch('arm'), 9, False, False))
self.assertFalse(
symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', [Tag('introduced=9')]), Arch('arm'),
- 9, False, False))
+ symbolfile.Symbol('foo', Tags.from_strs(['introduced=9'])),
+ Arch('arm'), 9, False, False))
self.assertTrue(
symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', [Tag('introduced=14')]), Arch('arm'),
- 9, False, False))
+ symbolfile.Symbol('foo', Tags.from_strs(['introduced=14'])),
+ Arch('arm'), 9, False, False))
class SymbolFileParseTest(unittest.TestCase):
@@ -376,11 +416,11 @@
version = parser.parse_version()
self.assertEqual('VERSION_1', version.name)
self.assertIsNone(version.base)
- self.assertEqual(['foo', 'bar'], version.tags)
+ self.assertEqual(Tags.from_strs(['foo', 'bar']), version.tags)
expected_symbols = [
- symbolfile.Symbol('baz', []),
- symbolfile.Symbol('qux', [Tag('woodly'), Tag('doodly')]),
+ symbolfile.Symbol('baz', Tags()),
+ symbolfile.Symbol('qux', Tags.from_strs(['woodly', 'doodly'])),
]
self.assertEqual(expected_symbols, version.symbols)
@@ -388,7 +428,7 @@
version = parser.parse_version()
self.assertEqual('VERSION_2', version.name)
self.assertEqual('VERSION_1', version.base)
- self.assertEqual([], version.tags)
+ self.assertEqual(Tags(), version.tags)
def test_parse_version_eof(self) -> None:
input_file = io.StringIO(textwrap.dedent("""\
@@ -423,12 +463,12 @@
parser.next_line()
symbol = parser.parse_symbol()
self.assertEqual('foo', symbol.name)
- self.assertEqual([], symbol.tags)
+ self.assertEqual(Tags(), symbol.tags)
parser.next_line()
symbol = parser.parse_symbol()
self.assertEqual('bar', symbol.name)
- self.assertEqual(['baz', 'qux'], symbol.tags)
+ self.assertEqual(Tags.from_strs(['baz', 'qux']), symbol.tags)
def test_wildcard_symbol_global(self) -> None:
input_file = io.StringIO(textwrap.dedent("""\
@@ -497,14 +537,15 @@
versions = parser.parse()
expected = [
- symbolfile.Version('VERSION_1', None, [], [
- symbolfile.Symbol('foo', []),
- symbolfile.Symbol('bar', [Tag('baz')]),
+ symbolfile.Version('VERSION_1', None, Tags(), [
+ symbolfile.Symbol('foo', Tags()),
+ symbolfile.Symbol('bar', Tags.from_strs(['baz'])),
]),
- symbolfile.Version('VERSION_2', 'VERSION_1', [Tag('wasd')], [
- symbolfile.Symbol('woodly', []),
- symbolfile.Symbol('doodly', [Tag('asdf')]),
- ]),
+ symbolfile.Version(
+ 'VERSION_2', 'VERSION_1', Tags.from_strs(['wasd']), [
+ symbolfile.Symbol('woodly', Tags()),
+ symbolfile.Symbol('doodly', Tags.from_strs(['asdf'])),
+ ]),
]
self.assertEqual(expected, versions)
@@ -527,10 +568,10 @@
self.assertIsNone(version.base)
expected_symbols = [
- symbolfile.Symbol('foo', []),
- symbolfile.Symbol('bar', [Tag('llndk')]),
- symbolfile.Symbol('baz', [Tag('llndk'), Tag('apex')]),
- symbolfile.Symbol('qux', [Tag('apex')]),
+ symbolfile.Symbol('foo', Tags()),
+ symbolfile.Symbol('bar', Tags.from_strs(['llndk'])),
+ symbolfile.Symbol('baz', Tags.from_strs(['llndk', 'apex'])),
+ symbolfile.Symbol('qux', Tags.from_strs(['apex'])),
]
self.assertEqual(expected_symbols, version.symbols)
diff --git a/cc/testing.go b/cc/testing.go
index f5c5ec5..80cc0ef 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -34,6 +34,7 @@
ctx.RegisterModuleType("cc_object", ObjectFactory)
ctx.RegisterModuleType("cc_genrule", genRuleFactory)
ctx.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory)
+ ctx.RegisterModuleType("ndk_prebuilt_static_stl", NdkPrebuiltStaticStlFactory)
ctx.RegisterModuleType("ndk_prebuilt_object", NdkPrebuiltObjectFactory)
ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
}
@@ -403,7 +404,7 @@
cc_library {
name: "ndk_libunwind",
- sdk_version: "current",
+ sdk_version: "minimum",
stl: "none",
system_shared_libs: [],
}
@@ -428,6 +429,12 @@
ndk_prebuilt_shared_stl {
name: "ndk_libc++_shared",
+ export_include_dirs: ["ndk_libc++_shared"],
+ }
+
+ ndk_prebuilt_static_stl {
+ name: "ndk_libandroid_support",
+ export_include_dirs: ["ndk_libandroid_support"],
}
cc_library_static {
@@ -490,7 +497,7 @@
}
cc_genrule {
- name: "host_bionic_linker_flags",
+ name: "host_bionic_linker_script",
host_supported: true,
device_supported: false,
target: {
@@ -501,7 +508,7 @@
enabled: true,
},
},
- out: ["linker.flags"],
+ out: ["linker.script"],
}
cc_defaults {
@@ -578,9 +585,11 @@
// Additional files needed in tests that disallow non-existent source.
android.MockFS{
- "defaults/cc/common/libc.map.txt": nil,
- "defaults/cc/common/libdl.map.txt": nil,
- "defaults/cc/common/libm.map.txt": nil,
+ "defaults/cc/common/libc.map.txt": nil,
+ "defaults/cc/common/libdl.map.txt": nil,
+ "defaults/cc/common/libm.map.txt": nil,
+ "defaults/cc/common/ndk_libandroid_support": nil,
+ "defaults/cc/common/ndk_libc++_shared": nil,
}.AddToFixture(),
// Place the default cc test modules that are common to all platforms in a location that will not
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/extract_linker/main.go b/cmd/extract_linker/main.go
index ea0bf4e..2dcb894 100644
--- a/cmd/extract_linker/main.go
+++ b/cmd/extract_linker/main.go
@@ -13,7 +13,7 @@
// limitations under the License.
// This tool extracts ELF LOAD segments from our linker binary, and produces an
-// assembly file and linker flags which will embed those segments as sections
+// assembly file and linker script which will embed those segments as sections
// in another binary.
package main
@@ -31,10 +31,10 @@
func main() {
var asmPath string
- var flagsPath string
+ var scriptPath string
flag.StringVar(&asmPath, "s", "", "Path to save the assembly file")
- flag.StringVar(&flagsPath, "f", "", "Path to save the linker flags")
+ flag.StringVar(&scriptPath, "T", "", "Path to save the linker script")
flag.Parse()
f, err := os.Open(flag.Arg(0))
@@ -49,20 +49,30 @@
}
asm := &bytes.Buffer{}
+ script := &bytes.Buffer{}
baseLoadAddr := uint64(0x1000)
load := 0
- linkFlags := []string{}
fmt.Fprintln(asm, ".globl __dlwrap_linker_offset")
fmt.Fprintf(asm, ".set __dlwrap_linker_offset, 0x%x\n", baseLoadAddr)
+ fmt.Fprintln(script, "ENTRY(__dlwrap__start)")
+ fmt.Fprintln(script, "SECTIONS {")
+
for _, prog := range ef.Progs {
if prog.Type != elf.PT_LOAD {
continue
}
- sectionName := fmt.Sprintf(".linker.sect%d", load)
- symName := fmt.Sprintf("__dlwrap_linker_sect%d", load)
+ var progName string
+ progSection := progToFirstSection(prog, ef.Sections)
+ if progSection != nil {
+ progName = progSection.Name
+ } else {
+ progName = fmt.Sprintf(".sect%d", load)
+ }
+ sectionName := ".linker" + progName
+ symName := "__dlwrap_linker" + strings.ReplaceAll(progName, ".", "_")
flags := ""
if prog.Flags&elf.PF_W != 0 {
@@ -75,10 +85,9 @@
fmt.Fprintf(asm, ".globl %s\n%s:\n\n", symName, symName)
- linkFlags = append(linkFlags,
- fmt.Sprintf("-Wl,--undefined=%s", symName),
- fmt.Sprintf("-Wl,--section-start=%s=0x%x",
- sectionName, baseLoadAddr+prog.Vaddr))
+ fmt.Fprintf(script, " %s %d : {\n", sectionName, baseLoadAddr+prog.Vaddr)
+ fmt.Fprintf(script, " KEEP(*(%s));\n", sectionName)
+ fmt.Fprintln(script, " }")
buffer, _ := ioutil.ReadAll(prog.Open())
bytesToAsm(asm, buffer)
@@ -97,16 +106,18 @@
load += 1
}
+ fmt.Fprintln(script, "}")
+ fmt.Fprintln(script, "INSERT BEFORE .note.android.ident;")
+
if asmPath != "" {
if err := ioutil.WriteFile(asmPath, asm.Bytes(), 0777); err != nil {
log.Fatalf("Unable to write %q: %v", asmPath, err)
}
}
- if flagsPath != "" {
- flags := strings.Join(linkFlags, " ")
- if err := ioutil.WriteFile(flagsPath, []byte(flags), 0777); err != nil {
- log.Fatalf("Unable to write %q: %v", flagsPath, err)
+ if scriptPath != "" {
+ if err := ioutil.WriteFile(scriptPath, script.Bytes(), 0777); err != nil {
+ log.Fatalf("Unable to write %q: %v", scriptPath, err)
}
}
}
@@ -125,3 +136,12 @@
}
fmt.Fprintln(asm)
}
+
+func progToFirstSection(prog *elf.Prog, sections []*elf.Section) *elf.Section {
+ for _, section := range sections {
+ if section.Addr == prog.Vaddr {
+ return section
+ }
+ }
+ return nil
+}
diff --git a/cmd/host_bionic_inject/Android.bp b/cmd/host_bionic_verify/Android.bp
similarity index 82%
rename from cmd/host_bionic_inject/Android.bp
rename to cmd/host_bionic_verify/Android.bp
index 16bc179..4e7c379 100644
--- a/cmd/host_bionic_inject/Android.bp
+++ b/cmd/host_bionic_verify/Android.bp
@@ -17,8 +17,7 @@
}
blueprint_go_binary {
- name: "host_bionic_inject",
- deps: ["soong-symbol_inject"],
- srcs: ["host_bionic_inject.go"],
- testSrcs: ["host_bionic_inject_test.go"],
+ name: "host_bionic_verify",
+ srcs: ["host_bionic_verify.go"],
+ testSrcs: ["host_bionic_verify_test.go"],
}
diff --git a/cmd/host_bionic_inject/host_bionic_inject.go b/cmd/host_bionic_verify/host_bionic_verify.go
similarity index 68%
rename from cmd/host_bionic_inject/host_bionic_inject.go
rename to cmd/host_bionic_verify/host_bionic_verify.go
index ce8b062..52400a3 100644
--- a/cmd/host_bionic_inject/host_bionic_inject.go
+++ b/cmd/host_bionic_verify/host_bionic_verify.go
@@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// Verifies a host bionic executable with an embedded linker, then injects
-// the address of the _start function for the linker_wrapper to use.
+// Verifies a host bionic executable with an embedded linker.
package main
import (
@@ -22,19 +21,16 @@
"fmt"
"io"
"os"
-
- "android/soong/symbol_inject"
)
func main() {
- var inputFile, linkerFile, outputFile string
+ var inputFile, linkerFile string
flag.StringVar(&inputFile, "i", "", "Input file")
flag.StringVar(&linkerFile, "l", "", "Linker file")
- flag.StringVar(&outputFile, "o", "", "Output file")
flag.Parse()
- if inputFile == "" || linkerFile == "" || outputFile == "" || flag.NArg() != 0 {
+ if inputFile == "" || linkerFile == "" || flag.NArg() != 0 {
flag.Usage()
os.Exit(1)
}
@@ -46,75 +42,52 @@
}
defer r.Close()
- file, err := symbol_inject.OpenFile(r)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(3)
- }
-
linker, err := elf.Open(linkerFile)
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(4)
}
- startAddr, err := parseElf(r, linker)
+ err = checkElf(r, linker)
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(5)
}
-
- w, err := os.OpenFile(outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(6)
- }
- defer w.Close()
-
- err = symbol_inject.InjectUint64Symbol(file, w, "__dlwrap_original_start", startAddr)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(7)
- }
}
// Check the ELF file, and return the address to the _start function
-func parseElf(r io.ReaderAt, linker *elf.File) (uint64, error) {
+func checkElf(r io.ReaderAt, linker *elf.File) error {
file, err := elf.NewFile(r)
if err != nil {
- return 0, err
+ return err
}
symbols, err := file.Symbols()
if err != nil {
- return 0, err
+ return err
}
for _, prog := range file.Progs {
if prog.Type == elf.PT_INTERP {
- return 0, fmt.Errorf("File should not have a PT_INTERP header")
+ return fmt.Errorf("File should not have a PT_INTERP header")
}
}
if dlwrap_start, err := findSymbol(symbols, "__dlwrap__start"); err != nil {
- return 0, err
+ return err
} else if dlwrap_start.Value != file.Entry {
- return 0, fmt.Errorf("Expected file entry(0x%x) to point to __dlwrap_start(0x%x)",
+ return fmt.Errorf("Expected file entry(0x%x) to point to __dlwrap_start(0x%x)",
file.Entry, dlwrap_start.Value)
}
err = checkLinker(file, linker, symbols)
if err != nil {
- return 0, fmt.Errorf("Linker executable failed verification against app embedded linker: %s\n"+
+ return fmt.Errorf("Linker executable failed verification against app embedded linker: %s\n"+
"linker might not be in sync with crtbegin_dynamic.o.",
err)
}
- start, err := findSymbol(symbols, "_start")
- if err != nil {
- return 0, fmt.Errorf("Failed to find _start symbol")
- }
- return start.Value, nil
+ return nil
}
func findSymbol(symbols []elf.Symbol, name string) (elf.Symbol, error) {
diff --git a/cmd/host_bionic_inject/host_bionic_inject_test.go b/cmd/host_bionic_verify/host_bionic_verify_test.go
similarity index 100%
rename from cmd/host_bionic_inject/host_bionic_inject_test.go
rename to cmd/host_bionic_verify/host_bionic_verify_test.go
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/docs/map_files.md b/docs/map_files.md
index 192530f..1388059 100644
--- a/docs/map_files.md
+++ b/docs/map_files.md
@@ -70,9 +70,11 @@
### apex
-Indicates that the version or symbol is to be exposed in the APEX stubs rather
-than the NDK. May be used in combination with `llndk` if the symbol is exposed
-to both APEX and the LL-NDK.
+Indicates that the version or symbol is to be exposed by an APEX rather than the
+NDK. For APIs exposed by the platform *for* APEX, use `systemapi`.
+
+May be used in combination with `llndk` if the symbol is exposed to both APEX
+and the LL-NDK.
### future
@@ -144,6 +146,12 @@
preferable to keep such APIs in an entirely separate library to protect them
from access via `dlsym`, but this is not always possible.
+### systemapi
+
+This is a synonym of the `apex` tag. It should be used to clarify that the API
+is an API exposed by the system for an APEX, whereas `apex` should be used for
+APIs exposed by an APEX to the platform or another APEX.
+
### var
Used to define a public global variable. By default all symbols are exposed as
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 fc8f557..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,102 +419,94 @@
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.ClasspathFragmentToConfiguredJarList(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
+ classpathJars = configuredJarListToClasspathJars(ctx, configuredJars, BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
} else {
- classpathJars = configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), b.classpathType)
+ classpathJars = configuredJarListToClasspathJars(ctx, configuredJars, b.classpathType)
}
- b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars)
+ b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
}
-func (b *BootclasspathFragmentModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList {
+func (b *BootclasspathFragmentModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
if "art" == proptools.String(b.properties.Image_name) {
return b.getImageConfig(ctx).modules
}
@@ -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 0e14d24..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
@@ -52,10 +58,6 @@
android.Module
classpathFragmentBase() *ClasspathFragmentBase
-
- // ClasspathFragmentToConfiguredJarList returns android.ConfiguredJarList representation of all
- // the jars in this classpath fragment.
- ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList
}
// ClasspathFragmentBase is meant to be embedded in any module types that implement classpathFragment;
@@ -104,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,
}
@@ -168,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 c30a712..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 {
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 fba73d0..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.ClasspathFragmentToConfiguredJarList(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
- b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars)
+ classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
+ b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
}
-func (b *platformBootclasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList {
- return b.getImageConfig(ctx).modules
+func (b *platformBootclasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
+ // 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..101a658 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.Module
+
+ 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,7 +678,7 @@
// 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
@@ -682,29 +690,23 @@
// 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())
+ return c.namingScheme.stubsLibraryModuleName(apiScope, c.module.BaseModuleName())
}
// 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())
+ return c.namingScheme.stubsSourceModuleName(apiScope, c.module.BaseModuleName())
}
// The component names for different outputs of the java_sdk_library.
@@ -753,7 +755,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 +786,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 +832,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 +859,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 +915,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 +947,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
@@ -1713,7 +1715,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 +1734,6 @@
stubsLibraryModuleName(scope *apiScope, baseName string) string
stubsSourceModuleName(scope *apiScope, baseName string) string
-
- apiModuleName(scope *apiScope, baseName string) string
}
type defaultNamingScheme struct {
@@ -1747,10 +1747,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 +1780,7 @@
module := &SdkLibrary{}
// Initialize information common between source and prebuilt.
- module.initCommon(&module.ModuleBase)
+ module.initCommon(module)
module.InitSdkLibraryProperties()
android.InitApexModule(module)
@@ -1932,7 +1928,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)
@@ -2144,7 +2140,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 +2265,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/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index 7ffb056..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.ClasspathFragmentToConfiguredJarList(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) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList {
- global := dexpreopt.GetGlobalConfig(ctx)
- return global.SystemServerJars
+func (p *platformSystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
+ // 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,11 +95,15 @@
ctx.PropertyErrorf("contents", "empty contents are not allowed")
}
- classpathJars := configuredJarListToClasspathJars(ctx, s.ClasspathFragmentToConfiguredJarList(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) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList {
+func (s *SystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
global := dexpreopt.GetGlobalConfig(ctx)
// Convert content names to their appropriate stems, in case a test library is overriding an actual boot jar
@@ -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/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index 7d49492..b05861b 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -28,7 +28,6 @@
runtime-module-host-exports
runtime-module-sdk
statsd-module-sdk
- statsd-module-sdk-for-art
tzdata-module-test-exports
)
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index f2ab6a1..d47eb1f 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) {
@@ -463,6 +484,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..ad2bd75 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,29 @@
jars: ["java/system-module.jar"],
}
+java_import {
+ name: "mysdk_myjavalib.stubs",
+ prefer: false,
+ visibility: ["//visibility:private"],
+ apex_available: ["//apex_available:platform"],
+ jars: ["java/myjavalib.stubs.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 +752,7 @@
libs: [
"mysdk_system-module",
"exported-system-module",
+ "mysdk_myjavalib.stubs",
],
}
`),
@@ -739,6 +775,29 @@
jars: ["java/system-module.jar"],
}
+java_import {
+ name: "mysdk_myjavalib.stubs@current",
+ sdk_member_name: "myjavalib.stubs",
+ visibility: ["//visibility:private"],
+ apex_available: ["//apex_available:platform"],
+ jars: ["java/myjavalib.stubs.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 +805,7 @@
libs: [
"mysdk_system-module@current",
"mysdk_exported-system-module@current",
+ "mysdk_myjavalib.stubs@current",
],
}
@@ -753,12 +813,17 @@
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/turbine-combined/myjavalib.stubs.jar -> java/myjavalib.stubs.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 +1150,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) {
+ // Show that the existing behavior is incorrect as the suffix for the child modules is added
+ // to the version not before it.
+ result.Module("mysdk_myjavalib@current.stubs", "android_common")
+ result.Module("mysdk_myjavalib@current.stubs.source", "android_common")
+ }),
)
}
diff --git a/sdk/update.go b/sdk/update.go
index 36b564f..e2e5997 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -114,8 +114,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) {
@@ -742,13 +750,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 +767,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 +784,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 +795,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)
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/config.go b/ui/build/config.go
index 220e734..b9aaaf8 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -573,6 +573,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 +584,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] == '-' {
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 7128414..19a47ae 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -99,6 +99,21 @@
"--used_env", shared.JoinPath(config.SoongOutDir(), usedEnvFile+suffix),
}
}
+
+func writeEmptyGlobFile(ctx Context, path string) {
+ err := os.MkdirAll(filepath.Dir(path), 0777)
+ if err != nil {
+ ctx.Fatalf("Failed to create parent directories of empty ninja glob file '%s': %s", path, err)
+ }
+
+ if _, err := os.Stat(path); os.IsNotExist(err) {
+ err = ioutil.WriteFile(path, nil, 0666)
+ if err != nil {
+ ctx.Fatalf("Failed to create empty ninja glob file '%s': %s", path, err)
+ }
+ }
+}
+
func bootstrapBlueprint(ctx Context, config Config, integratedBp2Build bool) {
ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
defer ctx.EndTrace()
@@ -106,8 +121,10 @@
var args bootstrap.Args
mainNinjaFile := shared.JoinPath(config.SoongOutDir(), "build.ninja")
- globFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/soong-build-globs.ninja")
bootstrapGlobFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build-globs.ninja")
+ // .bootstrap/build.ninja "includes" .bootstrap/build-globs.ninja for incremental builds
+ // generate an empty glob before running any rule in .bootstrap/build.ninja
+ writeEmptyGlobFile(ctx, bootstrapGlobFile)
bootstrapDepFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja.d")
args.RunGoTests = !config.skipSoongTests
@@ -117,7 +134,10 @@
args.TopFile = "Android.bp"
args.ModuleListFile = filepath.Join(config.FileListDir(), "Android.bp.list")
args.OutFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja")
- args.GlobFile = globFile
+ // The primary builder (aka soong_build) will use bootstrapGlobFile as the globFile to generate build.ninja(.d)
+ // Building soong_build does not require a glob file
+ // Using "" instead of "<soong_build_glob>.ninja" will ensure that an unused glob file is not written to out/soong/.bootstrap during StagePrimary
+ args.GlobFile = ""
args.GeneratingPrimaryBuilder = true
args.EmptyNinjaFile = config.EmptyNinjaFile()