Merge "Reland "Consistently prepend arch-specific headers""
diff --git a/Android.bp b/Android.bp
index 8f7f3e2..d6260b4 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,12 +107,22 @@
},
},
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.
dex_bootjars {
name: "dex_bootjars",
}
+
+// Pseudo-test that's run on checkbuilds to ensure that get_clang_version can
+// parse cc/config/global.go.
+genrule {
+ name: "get_clang_version_test",
+ cmd: "$(location get_clang_version) > $(out)",
+ tools: ["get_clang_version"],
+ srcs: ["cc/config/global.go"],
+ out: ["clang-prebuilts-version.txt"],
+}
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/README.md b/README.md
index b7e93f4..10ddd73 100644
--- a/README.md
+++ b/README.md
@@ -213,8 +213,8 @@
A module name's **scope** is the smallest namespace containing it. Suppose a
source tree has `device/my` and `device/my/display` namespaces. If `libfoo`
-module is defined in `device/co/display/lib/Android.bp`, its namespace is
-`device/co/display`.
+module is defined in `device/my/display/lib/Android.bp`, its namespace is
+`device/my/display`.
The name uniqueness thus means that module's name is unique within its scope. In
other words, "//_scope_:_name_" is globally unique module reference, e.g,
diff --git a/android/apex.go b/android/apex.go
index 4618fe9..b9efe4e 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -54,6 +54,10 @@
// True if this module comes from an updatable apexBundle.
Updatable bool
+ // True if this module can use private platform APIs. Only non-updatable APEX can set this
+ // to true.
+ UsePlatformApis bool
+
// The list of SDK modules that the containing apexBundle depends on.
RequiredSdks SdkRefs
@@ -87,16 +91,31 @@
var ApexInfoProvider = blueprint.NewMutatorProvider(ApexInfo{}, "apex")
+func (i ApexInfo) AddJSONData(d *map[string]interface{}) {
+ (*d)["Apex"] = map[string]interface{}{
+ "ApexVariationName": i.ApexVariationName,
+ "MinSdkVersion": i.MinSdkVersion,
+ "InApexModules": i.InApexModules,
+ "InApexVariants": i.InApexVariants,
+ "ForPrebuiltApex": i.ForPrebuiltApex,
+ }
+}
+
// mergedName gives the name of the alias variation that will be used when multiple apex variations
// of a module can be deduped into one variation. For example, if libfoo is included in both apex.a
// and apex.b, and if the two APEXes have the same min_sdk_version (say 29), then libfoo doesn't
// have to be built twice, but only once. In that case, the two apex variations apex.a and apex.b
-// are configured to have the same alias variation named apex29.
+// are configured to have the same alias variation named apex29. Whether platform APIs is allowed
+// or not also matters; if two APEXes don't have the same allowance, they get different names and
+// thus wouldn't be merged.
func (i ApexInfo) mergedName(ctx PathContext) string {
name := "apex" + strconv.Itoa(i.MinSdkVersion.FinalOrFutureInt())
for _, sdk := range i.RequiredSdks {
name += "_" + sdk.Name + "_" + sdk.Version
}
+ if i.UsePlatformApis {
+ name += "_private"
+ }
return name
}
@@ -527,6 +546,10 @@
merged[index].InApexModules = append(merged[index].InApexModules, apexInfo.InApexModules...)
merged[index].ApexContents = append(merged[index].ApexContents, apexInfo.ApexContents...)
merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable
+ if merged[index].UsePlatformApis != apexInfo.UsePlatformApis {
+ panic(fmt.Errorf("variants having different UsePlatformApis can't be merged"))
+ }
+ merged[index].UsePlatformApis = apexInfo.UsePlatformApis
} else {
seen[mergedName] = len(merged)
apexInfo.ApexVariationName = mergedName
diff --git a/android/apex_test.go b/android/apex_test.go
index e112369..60a639b 100644
--- a/android/apex_test.go
+++ b/android/apex_test.go
@@ -33,10 +33,10 @@
{
name: "single",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"apex10000", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
{"foo", "apex10000"},
@@ -45,11 +45,11 @@
{
name: "merge",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
- {"bar", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"foo", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, false}},
+ {"apex10000_baz_1", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, false}},
wantAliases: [][2]string{
{"bar", "apex10000_baz_1"},
{"foo", "apex10000_baz_1"},
@@ -58,12 +58,12 @@
{
name: "don't merge version",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
- {"bar", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", uncheckedFinalApiLevel(30), false, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex30", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
- {"apex10000", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"apex30", uncheckedFinalApiLevel(30), false, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"apex10000", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
{"bar", "apex30"},
@@ -73,11 +73,11 @@
{
name: "merge updatable",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
- {"bar", FutureApiLevel, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", FutureApiLevel, true, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
+ {"apex10000", FutureApiLevel, true, false, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
{"bar", "apex10000"},
@@ -87,12 +87,12 @@
{
name: "don't merge sdks",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
- {"bar", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"foo", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", FutureApiLevel, false, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000_baz_2", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
- {"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"apex10000_baz_2", FutureApiLevel, false, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"apex10000_baz_1", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
{"bar", "apex10000_baz_2"},
@@ -102,21 +102,36 @@
{
name: "don't merge when for prebuilt_apex",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
- {"bar", FutureApiLevel, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", FutureApiLevel, true, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
// This one should not be merged in with the others because it is for
// a prebuilt_apex.
- {"baz", FutureApiLevel, true, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex},
+ {"baz", FutureApiLevel, true, false, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
- {"baz", FutureApiLevel, true, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex},
+ {"apex10000", FutureApiLevel, true, false, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
+ {"baz", FutureApiLevel, true, false, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex},
},
wantAliases: [][2]string{
{"bar", "apex10000"},
{"foo", "apex10000"},
},
},
+ {
+ name: "don't merge different UsePlatformApis",
+ in: []ApexInfo{
+ {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", FutureApiLevel, false, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+ },
+ wantMerged: []ApexInfo{
+ {"apex10000_private", FutureApiLevel, false, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"apex10000", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+ },
+ wantAliases: [][2]string{
+ {"bar", "apex10000_private"},
+ {"foo", "apex10000"},
+ },
+ },
}
for _, tt := range tests {
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 f906c8a..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))
@@ -756,6 +768,10 @@
cmd.ImplicitDepFile(PathForBazelOut(ctx, *depfile))
}
+ for _, symlinkPath := range buildStatement.SymlinkPaths {
+ cmd.ImplicitSymlinkOutput(PathForBazelOut(ctx, symlinkPath))
+ }
+
// This is required to silence warnings pertaining to unexpected timestamps. Particularly,
// some Bazel builtins (such as files in the bazel_tools directory) have far-future
// timestamps. Without restat, Ninja would emit warnings that the input files of a
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index f93fe2b..f74fed1 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -83,6 +83,36 @@
// or ":<module>") and returns a Bazel-compatible label which corresponds to dependencies on the
// module within the given ctx.
func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList {
+ return bazelLabelForModuleDeps(ctx, modules, false)
+}
+
+// BazelLabelForModuleWholeDeps expects a list of references to other modules, ("<module>"
+// or ":<module>") and returns a Bazel-compatible label which corresponds to dependencies on the
+// module within the given ctx, where prebuilt dependencies will be appended with _alwayslink so
+// they can be handled as whole static libraries.
+func BazelLabelForModuleWholeDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList {
+ return bazelLabelForModuleDeps(ctx, modules, true)
+}
+
+// BazelLabelForModuleDepsExcludes expects two lists: modules (containing modules to include in the
+// list), and excludes (modules to exclude from the list). Both of these should contain references
+// to other modules, ("<module>" or ":<module>"). It returns a Bazel-compatible label list which
+// corresponds to dependencies on the module within the given ctx, and the excluded dependencies.
+func BazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
+ return bazelLabelForModuleDepsExcludes(ctx, modules, excludes, false)
+}
+
+// BazelLabelForModuleWholeDepsExcludes expects two lists: modules (containing modules to include in
+// the list), and excludes (modules to exclude from the list). Both of these should contain
+// references to other modules, ("<module>" or ":<module>"). It returns a Bazel-compatible label
+// list which corresponds to dependencies on the module within the given ctx, and the excluded
+// dependencies. Prebuilt dependencies will be appended with _alwayslink so they can be handled as
+// whole static libraries.
+func BazelLabelForModuleWholeDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
+ return bazelLabelForModuleDepsExcludes(ctx, modules, excludes, true)
+}
+
+func bazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string, isWholeLibs bool) bazel.LabelList {
var labels bazel.LabelList
for _, module := range modules {
bpText := module
@@ -90,7 +120,7 @@
module = ":" + module
}
if m, t := SrcIsModuleWithTag(module); m != "" {
- l := getOtherModuleLabel(ctx, m, t)
+ l := getOtherModuleLabel(ctx, m, t, isWholeLibs)
l.OriginalModuleName = bpText
labels.Includes = append(labels.Includes, l)
} else {
@@ -100,16 +130,12 @@
return labels
}
-// BazelLabelForModuleDeps expects two lists: modules (containing modules to include in the list),
-// and excludes (modules to exclude from the list). Both of these should contain references to other
-// modules, ("<module>" or ":<module>"). It returns a Bazel-compatible label list which corresponds
-// to dependencies on the module within the given ctx, and the excluded dependencies.
-func BazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
- moduleLabels := BazelLabelForModuleDeps(ctx, RemoveListFromList(modules, excludes))
+func bazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string, isWholeLibs bool) bazel.LabelList {
+ moduleLabels := bazelLabelForModuleDeps(ctx, RemoveListFromList(modules, excludes), isWholeLibs)
if len(excludes) == 0 {
return moduleLabels
}
- excludeLabels := BazelLabelForModuleDeps(ctx, excludes)
+ excludeLabels := bazelLabelForModuleDeps(ctx, excludes, isWholeLibs)
return bazel.LabelList{
Includes: moduleLabels.Includes,
Excludes: excludeLabels.Includes,
@@ -273,7 +299,7 @@
for _, p := range paths {
if m, tag := SrcIsModuleWithTag(p); m != "" {
- l := getOtherModuleLabel(ctx, m, tag)
+ l := getOtherModuleLabel(ctx, m, tag, false)
if !InList(l.Label, expandedExcludes) {
l.OriginalModuleName = fmt.Sprintf(":%s", m)
labels.Includes = append(labels.Includes, l)
@@ -304,7 +330,7 @@
// getOtherModuleLabel returns a bazel.Label for the given dependency/tag combination for the
// module. The label will be relative to the current directory if appropriate. The dependency must
// already be resolved by either deps mutator or path deps mutator.
-func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string) bazel.Label {
+func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string, isWholeLibs bool) bazel.Label {
m, _ := ctx.GetDirectDep(dep)
if m == nil {
panic(fmt.Errorf(`Cannot get direct dep %q of %q.
@@ -313,6 +339,11 @@
}
otherLabel := bazelModuleLabel(ctx, m, tag)
label := bazelModuleLabel(ctx, ctx.Module(), "")
+ if isWholeLibs {
+ if m, ok := m.(Module); ok && IsModulePrebuilt(m) {
+ otherLabel += "_alwayslink"
+ }
+ }
if samePackage(label, otherLabel) {
otherLabel = bazelShortLabel(otherLabel)
}
diff --git a/android/config.go b/android/config.go
index da78c7a..ed90c31 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1690,6 +1690,16 @@
return paths
}
+// BuildPathsByModule returns a map from module name to build paths based on the given directory
+// prefix.
+func (l *ConfiguredJarList) BuildPathsByModule(ctx PathContext, dir OutputPath) map[string]WritablePath {
+ paths := map[string]WritablePath{}
+ for _, jar := range l.jars {
+ paths[jar] = dir.Join(ctx, ModuleStem(jar)+".jar")
+ }
+ return paths
+}
+
// UnmarshalJSON converts JSON configuration from raw bytes into a
// ConfiguredJarList structure.
func (l *ConfiguredJarList) UnmarshalJSON(b []byte) error {
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/module.go b/android/module.go
index f745a4a..07d82f1 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1190,6 +1190,10 @@
vintfFragmentsPaths Paths
}
+func (m *ModuleBase) AddJSONData(d *map[string]interface{}) {
+ (*d)["Android"] = map[string]interface{}{}
+}
+
func (m *ModuleBase) ComponentDepsMutator(BottomUpMutatorContext) {}
func (m *ModuleBase) DepsMutator(BottomUpMutatorContext) {}
diff --git a/android/neverallow.go b/android/neverallow.go
index 41b399a..19b58a7 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -55,6 +55,7 @@
AddNeverAllowRules(createCcSdkVariantRules()...)
AddNeverAllowRules(createUncompressDexRules()...)
AddNeverAllowRules(createMakefileGoalRules()...)
+ AddNeverAllowRules(createInitFirstStageRules()...)
}
// Add a NeverAllow rule to the set of rules to apply.
@@ -216,6 +217,15 @@
}
}
+func createInitFirstStageRules() []Rule {
+ return []Rule{
+ NeverAllow().
+ Without("name", "init_first_stage").
+ With("install_in_root", "true").
+ Because("install_in_root is only for init_first_stage."),
+ }
+}
+
func neverallowMutator(ctx BottomUpMutatorContext) {
m, ok := ctx.Module().(Module)
if !ok {
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 43b7cbe..f3493bd 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -166,6 +166,7 @@
func InitPrebuiltModuleWithSrcSupplier(module PrebuiltInterface, srcsSupplier PrebuiltSrcsSupplier, srcsPropertyName string) {
p := module.Prebuilt()
module.AddProperties(&p.properties)
+ module.base().customizableProperties = module.GetProperties()
if srcsSupplier == nil {
panic(fmt.Errorf("srcsSupplier must not be nil"))
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index ced37fe..23524a5 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -259,6 +259,38 @@
}`,
prebuilt: []OsType{Android, BuildOs},
},
+ {
+ name: "prebuilt properties customizable",
+ replaceBp: true,
+ modules: `
+ source {
+ name: "foo",
+ deps: [":bar"],
+ }
+
+ soong_config_module_type {
+ name: "prebuilt_with_config",
+ module_type: "prebuilt",
+ config_namespace: "any_namespace",
+ bool_variables: ["bool_var"],
+ properties: ["prefer"],
+ }
+
+ prebuilt_with_config {
+ name: "bar",
+ prefer: true,
+ srcs: ["prebuilt_file"],
+ soong_config_variables: {
+ bool_var: {
+ prefer: false,
+ conditions_default: {
+ prefer: true,
+ },
+ },
+ },
+ }`,
+ prebuilt: []OsType{Android, BuildOs},
+ },
}
func TestPrebuilts(t *testing.T) {
@@ -394,6 +426,9 @@
ctx.RegisterModuleType("prebuilt", newPrebuiltModule)
ctx.RegisterModuleType("source", newSourceModule)
ctx.RegisterModuleType("override_source", newOverrideSourceModule)
+ ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
+ ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
+ ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
}
type prebuiltModule struct {
diff --git a/android/sdk.go b/android/sdk.go
index 36c576d..e700031 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -38,6 +38,36 @@
type sdkAwareWithoutModule interface {
RequiredSdks
+ // SdkMemberComponentName will return the name to use for a component of this module based on the
+ // base name of this module.
+ //
+ // The baseName is the name returned by ModuleBase.BaseModuleName(), i.e. the name specified in
+ // the name property in the .bp file so will not include the prebuilt_ prefix.
+ //
+ // The componentNameCreator is a func for creating the name of a component from the base name of
+ // the module, e.g. it could just append ".component" to the name passed in.
+ //
+ // This is intended to be called by prebuilt modules that create component models. It is because
+ // prebuilt module base names come in a variety of different forms:
+ // * unversioned - this is the same as the source module.
+ // * internal to an sdk - this is the unversioned name prefixed by the base name of the sdk
+ // module.
+ // * versioned - this is the same as the internal with the addition of an "@<version>" suffix.
+ //
+ // While this can be called from a source module in that case it will behave the same way as the
+ // unversioned name and return the result of calling the componentNameCreator func on the supplied
+ // base name.
+ //
+ // e.g. Assuming the componentNameCreator func simply appends ".component" to the name passed in
+ // then this will work as follows:
+ // * An unversioned name of "foo" will return "foo.component".
+ // * An internal to the sdk name of "sdk_foo" will return "sdk_foo.component".
+ // * A versioned name of "sdk_foo@current" will return "sdk_foo.component@current".
+ //
+ // Note that in the latter case the ".component" suffix is added before the version. Adding it
+ // after would change the version.
+ SdkMemberComponentName(baseName string, componentNameCreator func(string) string) string
+
sdkBase() *SdkBase
MakeMemberOf(sdk SdkRef)
IsInAnySdk() bool
@@ -135,6 +165,18 @@
return s
}
+func (s *SdkBase) SdkMemberComponentName(baseName string, componentNameCreator func(string) string) string {
+ if s.MemberName() == "" {
+ return componentNameCreator(baseName)
+ } else {
+ index := strings.LastIndex(baseName, "@")
+ unversionedName := baseName[:index]
+ unversionedComponentName := componentNameCreator(unversionedName)
+ versionSuffix := baseName[index:]
+ return unversionedComponentName + versionSuffix
+ }
+}
+
// MakeMemberOf sets this module to be a member of a specific SDK
func (s *SdkBase) MakeMemberOf(sdk SdkRef) {
s.properties.ContainingSdk = &sdk
@@ -300,6 +342,22 @@
Name() string
}
+// BpPrintable is a marker interface that must be implemented by any struct that is added as a
+// property value.
+type BpPrintable interface {
+ bpPrintable()
+}
+
+// BpPrintableBase must be embedded within any struct that is added as a
+// property value.
+type BpPrintableBase struct {
+}
+
+func (b BpPrintableBase) bpPrintable() {
+}
+
+var _ BpPrintable = BpPrintableBase{}
+
// An individual member of the SDK, includes all of the variants that the SDK
// requires.
type SdkMember interface {
@@ -317,6 +375,8 @@
// SdkMemberType returns the SdkMemberType that will be used to automatically add the child module
// to the sdk.
+ //
+ // Returning nil will prevent the module being added to the sdk.
SdkMemberType(child Module) SdkMemberType
// ExportMember determines whether a module added to the sdk through this tag will be exported
@@ -643,3 +703,30 @@
// into which to copy the prebuilt files.
Name() string
}
+
+// ExportedComponentsInfo contains information about the components that this module exports to an
+// sdk snapshot.
+//
+// A component of a module is a child module that the module creates and which forms an integral
+// part of the functionality that the creating module provides. A component module is essentially
+// owned by its creator and is tightly coupled to the creator and other components.
+//
+// e.g. the child modules created by prebuilt_apis are not components because they are not tightly
+// coupled to the prebuilt_apis module. Once they are created the prebuilt_apis ignores them. The
+// child impl and stub library created by java_sdk_library (and corresponding import) are components
+// because the creating module depends upon them in order to provide some of its own functionality.
+//
+// A component is exported if it is part of an sdk snapshot. e.g. The xml and impl child modules are
+// components but they are not exported as they are not part of an sdk snapshot.
+//
+// This information is used by the sdk snapshot generation code to ensure that it does not create
+// an sdk snapshot that contains a declaration of the component module and the module that creates
+// it as that would result in duplicate modules when attempting to use the snapshot. e.g. a snapshot
+// that included the java_sdk_library_import "foo" and also a java_import "foo.stubs" would fail
+// as there would be two modules called "foo.stubs".
+type ExportedComponentsInfo struct {
+ // The names of the exported components.
+ Components []string
+}
+
+var ExportedComponentsInfoProvider = blueprint.NewProvider(ExportedComponentsInfo{})
diff --git a/android/testing.go b/android/testing.go
index 191cb8d..b36f62c 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -713,9 +713,11 @@
func (b baseTestingComponent) maybeBuildParamsFromRule(rule string) (TestingBuildParams, []string) {
var searchedRules []string
- for _, p := range b.provider.BuildParamsForTests() {
- searchedRules = append(searchedRules, p.Rule.String())
- if strings.Contains(p.Rule.String(), rule) {
+ buildParams := b.provider.BuildParamsForTests()
+ for _, p := range buildParams {
+ ruleAsString := p.Rule.String()
+ searchedRules = append(searchedRules, ruleAsString)
+ if strings.Contains(ruleAsString, rule) {
return b.newTestingBuildParams(p), searchedRules
}
}
@@ -725,7 +727,7 @@
func (b baseTestingComponent) buildParamsFromRule(rule string) TestingBuildParams {
p, searchRules := b.maybeBuildParamsFromRule(rule)
if p.Rule == nil {
- panic(fmt.Errorf("couldn't find rule %q.\nall rules: %v", rule, searchRules))
+ panic(fmt.Errorf("couldn't find rule %q.\nall rules:\n%s", rule, strings.Join(searchRules, "\n")))
}
return p
}
diff --git a/android/variable.go b/android/variable.go
index c766120..b6e168c 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -101,6 +101,9 @@
Keep_symbols *bool
Keep_symbols_and_debug_frame *bool
}
+ Static_libs []string
+ Whole_static_libs []string
+ Shared_libs []string
}
// eng is true for -eng builds, and can be used to turn on additionaly heavyweight debugging
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 5316d7b..08616a9 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -104,6 +104,7 @@
"LOCAL_NDK_STL_VARIANT": "stl",
"LOCAL_JAR_MANIFEST": "manifest",
"LOCAL_CERTIFICATE": "certificate",
+ "LOCAL_CERTIFICATE_LINEAGE": "lineage",
"LOCAL_PACKAGE_NAME": "name",
"LOCAL_MODULE_RELATIVE_PATH": "relative_install_path",
"LOCAL_PROTOC_OPTIMIZE_TYPE": "proto.type",
diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
index 439f45d..02ab89d 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -794,7 +794,7 @@
include $(BUILD_CTS_SUPPORT_PACKAGE)
`,
expected: `
-android_test {
+android_test_helper_app {
name: "FooTest",
defaults: ["cts_support_defaults"],
test_suites: ["cts"],
@@ -1463,6 +1463,22 @@
}
`,
},
+ {
+ desc: "LOCAL_CERTIFICATE_LINEAGE",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE_LINEAGE := lineage
+include $(BUILD_PACKAGE)
+`,
+ expected: `
+android_test {
+ name: "foo",
+ lineage: "lineage",
+}
+`,
+ },
}
func TestEndToEnd(t *testing.T) {
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 926085b..baaf874 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -130,6 +130,10 @@
// symlinking to the system libs. Default is true.
Updatable *bool
+ // Whether this APEX can use platform APIs or not. Can be set to true only when `updatable:
+ // false`. Default is false.
+ Platform_apis *bool
+
// Whether this APEX is installable to one of the partitions like system, vendor, etc.
// Default: true.
Installable *bool
@@ -170,9 +174,9 @@
// Default is false.
Ignore_system_library_special_case *bool
- // Whenever apex_payload.img of the APEX should include dm-verity hashtree. Should be only
- // used in tests.
- Test_only_no_hashtree *bool
+ // Whenever apex_payload.img of the APEX should include dm-verity hashtree.
+ // Default value is true.
+ Generate_hashtree *bool
// Whenever apex_payload.img of the APEX should not be dm-verity signed. Should be only
// used in tests.
@@ -182,6 +186,12 @@
// used in tests.
Test_only_force_compression *bool
+ // Canonical name of this APEX bundle. Used to determine the path to the
+ // activated APEX on device (i.e. /apex/<apexVariationName>), and used for the
+ // apex mutator variations. For override_apex modules, this is the name of the
+ // overridden base module.
+ ApexVariationName string `blueprint:"mutated"`
+
IsCoverageVariant bool `blueprint:"mutated"`
// List of sanitizer names that this APEX is enabled for
@@ -414,6 +424,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.
@@ -821,6 +834,10 @@
var _ ApexInfoMutator = (*apexBundle)(nil)
+func (a *apexBundle) ApexVariationName() string {
+ return a.properties.ApexVariationName
+}
+
// ApexInfoMutator is responsible for collecting modules that need to have apex variants. They are
// identified by doing a graph walk starting from an apexBundle. Basically, all the (direct and
// indirect) dependencies are collected. But a few types of modules that shouldn't be included in
@@ -909,15 +926,16 @@
// This is the main part of this mutator. Mark the collected dependencies that they need to
// be built for this apexBundle.
- // Note that there are many different names.
- // ApexVariationName: this is the name of the apex variation
+ apexVariationName := proptools.StringDefault(a.properties.Apex_name, mctx.ModuleName()) // could be com.android.foo
+ a.properties.ApexVariationName = apexVariationName
apexInfo := android.ApexInfo{
- ApexVariationName: mctx.ModuleName(), // could be com.android.foo
+ ApexVariationName: apexVariationName,
MinSdkVersion: minSdkVersion,
RequiredSdks: a.RequiredSdks(),
Updatable: a.Updatable(),
- InApexVariants: []string{mctx.ModuleName()}, // could be com.android.foo
- InApexModules: []string{a.Name()}, // could be com.mycompany.android.foo
+ UsePlatformApis: a.UsePlatformApis(),
+ InApexVariants: []string{apexVariationName},
+ InApexModules: []string{a.Name()}, // could be com.mycompany.android.foo
ApexContents: []*android.ApexContents{apexContents},
}
mctx.WalkDeps(func(child, parent android.Module) bool {
@@ -930,6 +948,10 @@
}
type ApexInfoMutator interface {
+ // ApexVariationName returns the name of the APEX variation to use in the apex
+ // mutator etc. It is the same name as ApexInfo.ApexVariationName.
+ ApexVariationName() string
+
// ApexInfoMutator implementations must call BuildForApex(ApexInfo) on any modules that are
// depended upon by an apex and which require an apex specific variant.
ApexInfoMutator(android.TopDownMutatorContext)
@@ -1055,9 +1077,8 @@
}
// 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()
+ if ai, ok := mctx.Module().(ApexInfoMutator); ok && apexModuleTypeRequiresVariant(ai) {
+ apexBundleName := ai.ApexVariationName()
mctx.CreateVariations(apexBundleName)
if strings.HasPrefix(apexBundleName, "com.android.art") {
// Create an alias from the platform variant. This is done to make
@@ -1075,6 +1096,13 @@
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.
+ unprefixedModuleName := android.RemoveOptionalPrebuiltPrefix(mctx.ModuleName())
+ mctx.SetDefaultDependencyVariation(&unprefixedModuleName)
mctx.CreateVariations(apexBundleName)
if strings.HasPrefix(apexBundleName, "com.android.art") {
// TODO(b/183882457): See note for CreateAliasVariation above.
@@ -1083,6 +1111,17 @@
}
}
+// apexModuleTypeRequiresVariant determines whether the module supplied requires an apex specific
+// variant.
+func apexModuleTypeRequiresVariant(module ApexInfoMutator) bool {
+ if a, ok := module.(*apexBundle); ok {
+ // TODO(jiyong): document the reason why the VNDK APEX is an exception here.
+ return !a.vndkApex
+ }
+
+ return true
+}
+
// See android.UpdateDirectlyInAnyApex
// TODO(jiyong): move this to android/apex.go?
func apexDirectlyInAnyMutator(mctx android.BottomUpMutatorContext) {
@@ -1295,6 +1334,10 @@
return proptools.BoolDefault(a.properties.Updatable, true)
}
+func (a *apexBundle) UsePlatformApis() bool {
+ return proptools.BoolDefault(a.properties.Platform_apis, false)
+}
+
// getCertString returns the name of the cert that should be used to sign this APEX. This is
// basically from the "certificate" property, but could be overridden by the device config.
func (a *apexBundle) getCertString(ctx android.BaseModuleContext) string {
@@ -1317,9 +1360,9 @@
return !a.properties.PreventInstall && (a.properties.Installable == nil || proptools.Bool(a.properties.Installable))
}
-// See the test_only_no_hashtree property
-func (a *apexBundle) testOnlyShouldSkipHashtreeGeneration() bool {
- return proptools.Bool(a.properties.Test_only_no_hashtree)
+// See the generate_hashtree property
+func (a *apexBundle) shouldGenerateHashtree() bool {
+ return proptools.BoolDefault(a.properties.Generate_hashtree, true)
}
// See the test_only_unsigned_payload property
@@ -1666,6 +1709,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 {
@@ -1732,7 +1778,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:
@@ -2135,17 +2183,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
@@ -2331,16 +2385,33 @@
})
}
-// 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")
}
+ if a.UsePlatformApis() {
+ ctx.PropertyErrorf("updatable", "updatable APEXes can't use platform APIs")
+ }
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
@@ -2359,7 +2430,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 {
@@ -2411,6 +2482,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 6a7c35c..b5b1d44 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -839,6 +839,7 @@
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib", "mylib3"],
+ binaries: ["foo.rust"],
updatable: false,
}
@@ -887,6 +888,25 @@
stl: "none",
apex_available: [ "myapex" ],
}
+
+ rust_binary {
+ name: "foo.rust",
+ srcs: ["foo.rs"],
+ shared_libs: ["libfoo.shared_from_rust"],
+ prefer_rlib: true,
+ apex_available: ["myapex"],
+ }
+
+ cc_library_shared {
+ name: "libfoo.shared_from_rust",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ stubs: {
+ versions: ["10", "11", "12"],
+ },
+ }
+
`)
apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
@@ -924,7 +944,90 @@
"lib64/mylib.so",
"lib64/mylib3.so",
"lib64/mylib4.so",
+ "bin/foo.rust",
+ "lib64/libc++.so", // by the implicit dependency from foo.rust
+ "lib64/liblog.so", // by the implicit dependency from foo.rust
})
+
+ // Ensure that stub dependency from a rust module is not included
+ ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so")
+ // The rust module is linked to the stub cc library
+ rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"]
+ ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so")
+ ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so")
+}
+
+func TestApexCanUsePrivateApis(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ binaries: ["foo.rust"],
+ updatable: false,
+ platform_apis: true,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ shared_libs: ["mylib2"],
+ system_shared_libs: [],
+ stl: "none",
+ apex_available: [ "myapex" ],
+ }
+
+ cc_library {
+ name: "mylib2",
+ srcs: ["mylib.cpp"],
+ cflags: ["-include mylib.h"],
+ system_shared_libs: [],
+ stl: "none",
+ stubs: {
+ versions: ["1", "2", "3"],
+ },
+ }
+
+ rust_binary {
+ name: "foo.rust",
+ srcs: ["foo.rs"],
+ shared_libs: ["libfoo.shared_from_rust"],
+ prefer_rlib: true,
+ apex_available: ["myapex"],
+ }
+
+ cc_library_shared {
+ name: "libfoo.shared_from_rust",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ stubs: {
+ versions: ["10", "11", "12"],
+ },
+ }
+ `)
+
+ apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
+ copyCmds := apexRule.Args["copy_commands"]
+
+ // Ensure that indirect stubs dep is not included
+ ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so")
+ ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so")
+
+ // Ensure that we are using non-stub variants of mylib2 and libfoo.shared_from_rust (because
+ // of the platform_apis: true)
+ mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000_private").Rule("ld").Args["libFlags"]
+ ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so")
+ ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
+ rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000_private").Rule("rustc").Args["linkFlags"]
+ ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so")
+ ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so")
}
func TestApexWithStubsWithMinSdkVersion(t *testing.T) {
@@ -1695,6 +1798,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 +3471,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) {
@@ -3893,13 +4075,13 @@
}
`)
- module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+ module := ctx.ModuleForTests("myapex", "android_common_com.android.myapex_image")
apexManifestRule := module.Rule("apexManifestRule")
ensureContains(t, apexManifestRule.Args["opt"], "-v name com.android.myapex")
apexRule := module.Rule("apexRule")
ensureContains(t, apexRule.Args["opt_flags"], "--do_not_check_keyname")
- apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+ apexBundle := module.Module().(*apexBundle)
data := android.AndroidMkDataForTest(t, ctx, apexBundle)
name := apexBundle.BaseModuleName()
prefix := "TARGET_"
@@ -4389,7 +4571,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 +4580,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 +4596,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 +4615,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"]
@@ -4442,6 +4624,59 @@
}
}
+func TestPrebuiltApexName(t *testing.T) {
+ testApex(t, `
+ prebuilt_apex {
+ name: "com.company.android.myapex",
+ apex_name: "com.android.myapex",
+ src: "company-myapex-arm.apex",
+ }
+ `).ModuleForTests("com.company.android.myapex", "android_common_com.android.myapex")
+
+ testApex(t, `
+ apex_set {
+ name: "com.company.android.myapex",
+ apex_name: "com.android.myapex",
+ set: "company-myapex.apks",
+ }
+ `).ModuleForTests("com.company.android.myapex", "android_common_com.android.myapex")
+}
+
+func TestPrebuiltApexNameWithPlatformBootclasspath(t *testing.T) {
+ _ = android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithApexBuildComponents,
+ android.FixtureWithRootAndroidBp(`
+ platform_bootclasspath {
+ name: "platform-bootclasspath",
+ fragments: [
+ {
+ apex: "com.android.art",
+ module: "art-bootclasspath-fragment",
+ },
+ ],
+ }
+
+ prebuilt_apex {
+ name: "com.company.android.art",
+ apex_name: "com.android.art",
+ src: "com.company.android.art-arm.apex",
+ exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
+ }
+
+ prebuilt_bootclasspath_fragment {
+ name: "art-bootclasspath-fragment",
+ contents: ["core-oj"],
+ }
+
+ java_import {
+ name: "core-oj",
+ jars: ["prebuilt.jar"],
+ }
+ `),
+ ).RunTest(t)
+}
+
// These tests verify that the prebuilt_apex/deapexer to java_import wiring allows for the
// propagation of paths to dex implementation jars from the former to the latter.
func TestPrebuiltExportDexImplementationJars(t *testing.T) {
@@ -4503,7 +4738,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 +4874,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 +4900,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 +4921,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 +4941,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 +4962,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 +4989,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 +5016,7 @@
jars: ["libbar.jar"],
},
apex_available: ["myapex"],
+ shared_library: false,
}
java_sdk_library {
@@ -4769,7 +5032,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, "failed to find a dex jar path for module 'libfoo'", 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 +5047,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 +5076,7 @@
jars: ["libbar.jar"],
},
apex_available: ["myapex"],
+ shared_library: false,
}
java_sdk_library {
@@ -4817,15 +5087,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 +5123,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 +5150,7 @@
jars: ["libbar.jar"],
},
apex_available: ["myapex"],
+ shared_library: false,
}
java_sdk_library {
@@ -4884,15 +5161,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 +5197,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 +5226,7 @@
jars: ["libbar.jar"],
},
apex_available: ["myapex"],
+ shared_library: false,
}
java_sdk_library {
@@ -4953,15 +5237,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 +6806,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 +6831,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 +6857,7 @@
"com.android.art.debug",
],
hostdex: true,
+ compile_dex: true,
}
apex {
@@ -6577,7 +6871,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 +6886,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 +6919,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 +6950,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 +7004,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 +7073,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 +7109,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 +7144,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 +7160,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 +7527,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..4b1600e 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -16,11 +16,13 @@
import (
"fmt"
+ "sort"
"strings"
"testing"
"android/soong/android"
"android/soong/java"
+ "github.com/google/blueprint/proptools"
)
// Contains tests for bootclasspath_fragment logic from java/bootclasspath_fragment.go as the ART
@@ -82,6 +84,7 @@
"com.android.art",
],
srcs: ["b.java"],
+ compile_dex: true,
}
java_library {
@@ -90,6 +93,7 @@
"com.android.art",
],
srcs: ["b.java"],
+ compile_dex: true,
}
bootclasspath_fragment {
@@ -213,9 +217,10 @@
`,
)
- checkSdkKindStubs := func(message string, info java.HiddenAPIInfo, kind android.SdkKind, expectedPaths ...string) {
+ checkAPIScopeStubs := func(message string, info java.HiddenAPIInfo, apiScope *java.HiddenAPIScope, expectedPaths ...string) {
t.Helper()
- android.AssertPathsRelativeToTopEquals(t, fmt.Sprintf("%s %s", message, kind), expectedPaths, info.TransitiveStubDexJarsByKind[kind])
+ paths := info.TransitiveStubDexJarsByScope.StubDexJarsForScope(apiScope)
+ android.AssertPathsRelativeToTopEquals(t, fmt.Sprintf("%s %s", message, apiScope), expectedPaths, paths)
}
// Check stub dex paths exported by art.
@@ -226,10 +231,10 @@
bazSystemStubs := "out/soong/.intermediates/baz.stubs.system/android_common/dex/baz.stubs.system.jar"
bazTestStubs := "out/soong/.intermediates/baz.stubs.test/android_common/dex/baz.stubs.test.jar"
- checkSdkKindStubs("art", artInfo, android.SdkPublic, bazPublicStubs)
- checkSdkKindStubs("art", artInfo, android.SdkSystem, bazSystemStubs)
- checkSdkKindStubs("art", artInfo, android.SdkTest, bazTestStubs)
- checkSdkKindStubs("art", artInfo, android.SdkCorePlatform)
+ checkAPIScopeStubs("art", artInfo, java.PublicHiddenAPIScope, bazPublicStubs)
+ checkAPIScopeStubs("art", artInfo, java.SystemHiddenAPIScope, bazSystemStubs)
+ checkAPIScopeStubs("art", artInfo, java.TestHiddenAPIScope, bazTestStubs)
+ checkAPIScopeStubs("art", artInfo, java.CorePlatformHiddenAPIScope)
// Check stub dex paths exported by other.
otherFragment := result.Module("other-bootclasspath-fragment", "android_common")
@@ -238,10 +243,10 @@
fooPublicStubs := "out/soong/.intermediates/foo.stubs/android_common/dex/foo.stubs.jar"
fooSystemStubs := "out/soong/.intermediates/foo.stubs.system/android_common/dex/foo.stubs.system.jar"
- checkSdkKindStubs("other", otherInfo, android.SdkPublic, bazPublicStubs, fooPublicStubs)
- checkSdkKindStubs("other", otherInfo, android.SdkSystem, bazSystemStubs, fooSystemStubs)
- checkSdkKindStubs("other", otherInfo, android.SdkTest, bazTestStubs, fooSystemStubs)
- checkSdkKindStubs("other", otherInfo, android.SdkCorePlatform)
+ checkAPIScopeStubs("other", otherInfo, java.PublicHiddenAPIScope, bazPublicStubs, fooPublicStubs)
+ checkAPIScopeStubs("other", otherInfo, java.SystemHiddenAPIScope, bazSystemStubs, fooSystemStubs)
+ checkAPIScopeStubs("other", otherInfo, java.TestHiddenAPIScope, bazTestStubs, fooSystemStubs)
+ checkAPIScopeStubs("other", otherInfo, java.CorePlatformHiddenAPIScope)
}
func checkBootclasspathFragment(t *testing.T, result *android.TestResult, moduleName, variantName string, expectedConfiguredModules string, expectedBootclasspathFragmentFiles string) {
@@ -318,6 +323,7 @@
apex_available: [
"com.android.art",
],
+ compile_dex: true,
}
java_import {
@@ -326,6 +332,7 @@
apex_available: [
"com.android.art",
],
+ compile_dex: true,
}
`),
)
@@ -355,6 +362,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 +388,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 +463,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 +555,7 @@
src: "com.android.art-arm.apex",
},
},
- exported_java_libs: ["foo", "bar"],
+ exported_bootclasspath_fragments: ["mybootclasspathfragment"],
}
java_import {
@@ -519,17 +585,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) {
@@ -629,4 +720,480 @@
checkFragmentExportedDexJar("bar", "out/soong/.intermediates/mybootclasspathfragment/android_common_apex10000/hiddenapi-modular/encoded/bar.jar")
}
+func getDexJarPath(result *android.TestResult, name string) string {
+ module := result.Module(name, "android_common")
+ return module.(java.UsesLibraryDependency).DexJarBuildPath().RelativeToTop().String()
+}
+
+// TestBootclasspathFragment_HiddenAPIList checks to make sure that the correct parameters are
+// passed to the hiddenapi list tool.
+func TestBootclasspathFragment_HiddenAPIList(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithBootclasspathFragment,
+ prepareForTestWithArtApex,
+ prepareForTestWithMyapex,
+ // Configure bootclasspath jars to ensure that hidden API encoding is performed on them.
+ java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz"),
+ java.FixtureConfigureUpdatableBootJars("myapex:foo", "myapex:bar"),
+ // Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
+ // is disabled.
+ android.FixtureAddTextFile("frameworks/base/Android.bp", ""),
+
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ java.FixtureWithLastReleaseApis("foo", "quuz"),
+ ).RunTestWithBp(t, `
+ apex {
+ name: "com.android.art",
+ key: "com.android.art.key",
+ bootclasspath_fragments: ["art-bootclasspath-fragment"],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "com.android.art.key",
+ public_key: "com.android.art.avbpubkey",
+ private_key: "com.android.art.pem",
+ }
+
+ java_library {
+ name: "baz",
+ apex_available: [
+ "com.android.art",
+ ],
+ srcs: ["b.java"],
+ compile_dex: true,
+ }
+
+ java_sdk_library {
+ name: "quuz",
+ apex_available: [
+ "com.android.art",
+ ],
+ srcs: ["b.java"],
+ compile_dex: true,
+ public: {enabled: true},
+ system: {enabled: true},
+ test: {enabled: true},
+ module_lib: {enabled: true},
+ }
+
+ bootclasspath_fragment {
+ name: "art-bootclasspath-fragment",
+ image_name: "art",
+ // Must match the "com.android.art:" entries passed to FixtureConfigureBootJars above.
+ contents: ["baz", "quuz"],
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ bootclasspath_fragments: [
+ "mybootclasspathfragment",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_sdk_library {
+ name: "foo",
+ srcs: ["b.java"],
+ shared_library: false,
+ public: {enabled: true},
+ apex_available: [
+ "myapex",
+ ],
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["b.java"],
+ installable: true,
+ apex_available: [
+ "myapex",
+ ],
+ }
+
+ bootclasspath_fragment {
+ name: "mybootclasspathfragment",
+ contents: [
+ "foo",
+ "bar",
+ ],
+ apex_available: [
+ "myapex",
+ ],
+ fragments: [
+ {
+ apex: "com.android.art",
+ module: "art-bootclasspath-fragment",
+ },
+ ],
+ }
+ `)
+
+ java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{
+ "art-bootclasspath-fragment",
+ "bar",
+ "dex2oatd",
+ "foo",
+ })
+
+ fooStubs := getDexJarPath(result, "foo.stubs")
+ quuzPublicStubs := getDexJarPath(result, "quuz.stubs")
+ quuzSystemStubs := getDexJarPath(result, "quuz.stubs.system")
+ quuzTestStubs := getDexJarPath(result, "quuz.stubs.test")
+ quuzModuleLibStubs := getDexJarPath(result, "quuz.stubs.module_lib")
+
+ // Make sure that the fragment uses the quuz stub dex jars when generating the hidden API flags.
+ fragment := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
+
+ rule := fragment.Rule("modularHiddenAPIStubFlagsFile")
+ command := rule.RuleParams.Command
+ android.AssertStringDoesContain(t, "check correct rule", command, "hiddenapi list")
+
+ // Make sure that the quuz stubs are available for resolving references from the implementation
+ // boot dex jars provided by this module.
+ android.AssertStringDoesContain(t, "quuz widest", command, "--dependency-stub-dex="+quuzModuleLibStubs)
+
+ // Make sure that the quuz stubs are available for resolving references from the different API
+ // stubs provided by this module.
+ android.AssertStringDoesContain(t, "public", command, "--public-stub-classpath="+quuzPublicStubs+":"+fooStubs)
+ android.AssertStringDoesContain(t, "system", command, "--system-stub-classpath="+quuzSystemStubs+":"+fooStubs)
+ android.AssertStringDoesContain(t, "test", command, "--test-stub-classpath="+quuzTestStubs+":"+fooStubs)
+}
+
+// TestBootclasspathFragment_AndroidNonUpdatable checks to make sure that setting
+// additional_stubs: ["android-non-updatable"] causes the source android-non-updatable modules to be
+// added to the hiddenapi list tool.
+func TestBootclasspathFragment_AndroidNonUpdatable(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithBootclasspathFragment,
+ prepareForTestWithArtApex,
+ prepareForTestWithMyapex,
+ // Configure bootclasspath jars to ensure that hidden API encoding is performed on them.
+ java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "myapex:foo", "myapex:bar"),
+ // Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
+ // is disabled.
+ android.FixtureAddTextFile("frameworks/base/Android.bp", ""),
+
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ java.FixtureWithLastReleaseApis("foo", "android-non-updatable"),
+ ).RunTestWithBp(t, `
+ java_sdk_library {
+ name: "android-non-updatable",
+ srcs: ["b.java"],
+ compile_dex: true,
+ public: {
+ enabled: true,
+ },
+ system: {
+ enabled: true,
+ },
+ test: {
+ enabled: true,
+ },
+ module_lib: {
+ enabled: true,
+ },
+ }
+
+ apex {
+ name: "com.android.art",
+ key: "com.android.art.key",
+ bootclasspath_fragments: ["art-bootclasspath-fragment"],
+ java_libs: [
+ "baz",
+ "quuz",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "com.android.art.key",
+ public_key: "com.android.art.avbpubkey",
+ private_key: "com.android.art.pem",
+ }
+
+ java_library {
+ name: "baz",
+ apex_available: [
+ "com.android.art",
+ ],
+ srcs: ["b.java"],
+ compile_dex: true,
+ }
+
+ java_library {
+ name: "quuz",
+ apex_available: [
+ "com.android.art",
+ ],
+ srcs: ["b.java"],
+ compile_dex: true,
+ }
+
+ bootclasspath_fragment {
+ name: "art-bootclasspath-fragment",
+ image_name: "art",
+ // Must match the "com.android.art:" entries passed to FixtureConfigureBootJars above.
+ contents: ["baz", "quuz"],
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ bootclasspath_fragments: [
+ "mybootclasspathfragment",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_sdk_library {
+ name: "foo",
+ srcs: ["b.java"],
+ shared_library: false,
+ public: {enabled: true},
+ apex_available: [
+ "myapex",
+ ],
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["b.java"],
+ installable: true,
+ apex_available: [
+ "myapex",
+ ],
+ }
+
+ bootclasspath_fragment {
+ name: "mybootclasspathfragment",
+ contents: [
+ "foo",
+ "bar",
+ ],
+ apex_available: [
+ "myapex",
+ ],
+ additional_stubs: ["android-non-updatable"],
+ fragments: [
+ {
+ apex: "com.android.art",
+ module: "art-bootclasspath-fragment",
+ },
+ ],
+ }
+ `)
+
+ java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{
+ "android-non-updatable.stubs",
+ "android-non-updatable.stubs.module_lib",
+ "android-non-updatable.stubs.system",
+ "android-non-updatable.stubs.test",
+ "art-bootclasspath-fragment",
+ "bar",
+ "dex2oatd",
+ "foo",
+ })
+
+ nonUpdatablePublicStubs := getDexJarPath(result, "android-non-updatable.stubs")
+ nonUpdatableSystemStubs := getDexJarPath(result, "android-non-updatable.stubs.system")
+ nonUpdatableTestStubs := getDexJarPath(result, "android-non-updatable.stubs.test")
+ nonUpdatableModuleLibStubs := getDexJarPath(result, "android-non-updatable.stubs.module_lib")
+
+ // Make sure that the fragment uses the android-non-updatable modules when generating the hidden
+ // API flags.
+ fragment := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
+
+ rule := fragment.Rule("modularHiddenAPIStubFlagsFile")
+ command := rule.RuleParams.Command
+ android.AssertStringDoesContain(t, "check correct rule", command, "hiddenapi list")
+
+ // Make sure that the module_lib non-updatable stubs are available for resolving references from
+ // the implementation boot dex jars provided by this module.
+ android.AssertStringDoesContain(t, "android-non-updatable widest", command, "--dependency-stub-dex="+nonUpdatableModuleLibStubs)
+
+ // Make sure that the appropriate non-updatable stubs are available for resolving references from
+ // the different API stubs provided by this module.
+ android.AssertStringDoesContain(t, "public", command, "--public-stub-classpath="+nonUpdatablePublicStubs)
+ android.AssertStringDoesContain(t, "system", command, "--system-stub-classpath="+nonUpdatableSystemStubs)
+ android.AssertStringDoesContain(t, "test", command, "--test-stub-classpath="+nonUpdatableTestStubs)
+}
+
+// TestBootclasspathFragment_AndroidNonUpdatable_AlwaysUsePrebuiltSdks checks to make sure that
+// setting additional_stubs: ["android-non-updatable"] causes the prebuilt android-non-updatable
+// modules to be added to the hiddenapi list tool.
+func TestBootclasspathFragment_AndroidNonUpdatable_AlwaysUsePrebuiltSdks(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithBootclasspathFragment,
+ java.PrepareForTestWithJavaDefaultModules,
+ prepareForTestWithArtApex,
+ prepareForTestWithMyapex,
+ // Configure bootclasspath jars to ensure that hidden API encoding is performed on them.
+ java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "myapex:foo", "myapex:bar"),
+ // Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
+ // is disabled.
+ android.FixtureAddTextFile("frameworks/base/Android.bp", ""),
+
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
+ }),
+
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ java.FixtureWithPrebuiltApis(map[string][]string{
+ "current": {"android-non-updatable"},
+ "30": {"foo"},
+ }),
+ ).RunTestWithBp(t, `
+ apex {
+ name: "com.android.art",
+ key: "com.android.art.key",
+ bootclasspath_fragments: ["art-bootclasspath-fragment"],
+ java_libs: [
+ "baz",
+ "quuz",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "com.android.art.key",
+ public_key: "com.android.art.avbpubkey",
+ private_key: "com.android.art.pem",
+ }
+
+ java_library {
+ name: "baz",
+ apex_available: [
+ "com.android.art",
+ ],
+ srcs: ["b.java"],
+ compile_dex: true,
+ }
+
+ java_library {
+ name: "quuz",
+ apex_available: [
+ "com.android.art",
+ ],
+ srcs: ["b.java"],
+ compile_dex: true,
+ }
+
+ bootclasspath_fragment {
+ name: "art-bootclasspath-fragment",
+ image_name: "art",
+ // Must match the "com.android.art:" entries passed to FixtureConfigureBootJars above.
+ contents: ["baz", "quuz"],
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ bootclasspath_fragments: [
+ "mybootclasspathfragment",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_sdk_library {
+ name: "foo",
+ srcs: ["b.java"],
+ shared_library: false,
+ public: {enabled: true},
+ apex_available: [
+ "myapex",
+ ],
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["b.java"],
+ installable: true,
+ apex_available: [
+ "myapex",
+ ],
+ }
+
+ bootclasspath_fragment {
+ name: "mybootclasspathfragment",
+ contents: [
+ "foo",
+ "bar",
+ ],
+ apex_available: [
+ "myapex",
+ ],
+ additional_stubs: ["android-non-updatable"],
+ fragments: [
+ {
+ apex: "com.android.art",
+ module: "art-bootclasspath-fragment",
+ },
+ ],
+ }
+ `)
+
+ java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{
+ "art-bootclasspath-fragment",
+ "bar",
+ "dex2oatd",
+ "foo",
+ "prebuilt_sdk_module-lib_current_android-non-updatable",
+ "prebuilt_sdk_public_current_android-non-updatable",
+ "prebuilt_sdk_system_current_android-non-updatable",
+ "prebuilt_sdk_test_current_android-non-updatable",
+ })
+
+ nonUpdatablePublicStubs := getDexJarPath(result, "sdk_public_current_android-non-updatable")
+ nonUpdatableSystemStubs := getDexJarPath(result, "sdk_system_current_android-non-updatable")
+ nonUpdatableTestStubs := getDexJarPath(result, "sdk_test_current_android-non-updatable")
+ nonUpdatableModuleLibStubs := getDexJarPath(result, "sdk_module-lib_current_android-non-updatable")
+
+ // Make sure that the fragment uses the android-non-updatable modules when generating the hidden
+ // API flags.
+ fragment := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
+
+ rule := fragment.Rule("modularHiddenAPIStubFlagsFile")
+ command := rule.RuleParams.Command
+ android.AssertStringDoesContain(t, "check correct rule", command, "hiddenapi list")
+
+ // Make sure that the module_lib non-updatable stubs are available for resolving references from
+ // the implementation boot dex jars provided by this module.
+ android.AssertStringDoesContain(t, "android-non-updatable widest", command, "--dependency-stub-dex="+nonUpdatableModuleLibStubs)
+
+ // Make sure that the appropriate non-updatable stubs are available for resolving references from
+ // the different API stubs provided by this module.
+ android.AssertStringDoesContain(t, "public", command, "--public-stub-classpath="+nonUpdatablePublicStubs)
+ android.AssertStringDoesContain(t, "system", command, "--system-stub-classpath="+nonUpdatableSystemStubs)
+ android.AssertStringDoesContain(t, "test", command, "--test-stub-classpath="+nonUpdatableTestStubs)
+}
+
// TODO(b/177892522) - add test for host apex.
diff --git a/apex/builder.go b/apex/builder.go
index 021e499..24c049b 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -630,11 +630,7 @@
optFlags = append(optFlags, "--assets_dir "+filepath.Dir(a.mergedNotices.HtmlGzOutput.String()))
}
- if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && ctx.ModuleDir() != "system/apex/shim/build" && a.testOnlyShouldSkipHashtreeGeneration() {
- ctx.PropertyErrorf("test_only_no_hashtree", "not available")
- return
- }
- if (moduleMinSdkVersion.GreaterThan(android.SdkVersion_Android10) || a.testOnlyShouldSkipHashtreeGeneration()) && !compressionEnabled {
+ if (moduleMinSdkVersion.GreaterThan(android.SdkVersion_Android10) && !a.shouldGenerateHashtree()) && !compressionEnabled {
// Apexes which are supposed to be installed in builtin dirs(/system, etc)
// don't need hashtree for activation. Therefore, by removing hashtree from
// apex bundle (filesystem image in it, to be specific), we can save storage.
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 ce12f46..279cf54 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -15,6 +15,8 @@
package apex
import (
+ "fmt"
+ "strings"
"testing"
"android/soong/android"
@@ -31,6 +33,139 @@
PrepareForTestWithApexBuildComponents,
)
+func TestPlatformBootclasspath_Fragments(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithPlatformBootclasspath,
+ prepareForTestWithMyapex,
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ java.FixtureWithLastReleaseApis("foo"),
+ java.FixtureConfigureBootJars("myapex:bar"),
+ android.FixtureWithRootAndroidBp(`
+ platform_bootclasspath {
+ name: "platform-bootclasspath",
+ fragments: [
+ {
+ apex: "myapex",
+ module:"bar-fragment",
+ },
+ ],
+ hidden_api: {
+ unsupported: [
+ "unsupported.txt",
+ ],
+ removed: [
+ "removed.txt",
+ ],
+ max_target_r_low_priority: [
+ "max-target-r-low-priority.txt",
+ ],
+ max_target_q: [
+ "max-target-q.txt",
+ ],
+ max_target_p: [
+ "max-target-p.txt",
+ ],
+ max_target_o_low_priority: [
+ "max-target-o-low-priority.txt",
+ ],
+ blocked: [
+ "blocked.txt",
+ ],
+ unsupported_packages: [
+ "unsupported-packages.txt",
+ ],
+ },
+ }
+
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ bootclasspath_fragments: [
+ "bar-fragment",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ bootclasspath_fragment {
+ name: "bar-fragment",
+ contents: ["bar"],
+ apex_available: ["myapex"],
+ api: {
+ stub_libs: ["foo"],
+ },
+ hidden_api: {
+ unsupported: [
+ "bar-unsupported.txt",
+ ],
+ removed: [
+ "bar-removed.txt",
+ ],
+ max_target_r_low_priority: [
+ "bar-max-target-r-low-priority.txt",
+ ],
+ max_target_q: [
+ "bar-max-target-q.txt",
+ ],
+ max_target_p: [
+ "bar-max-target-p.txt",
+ ],
+ max_target_o_low_priority: [
+ "bar-max-target-o-low-priority.txt",
+ ],
+ blocked: [
+ "bar-blocked.txt",
+ ],
+ unsupported_packages: [
+ "bar-unsupported-packages.txt",
+ ],
+ },
+ }
+
+ java_library {
+ name: "bar",
+ apex_available: ["myapex"],
+ srcs: ["a.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ permitted_packages: ["bar"],
+ }
+
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java"],
+ public: {
+ enabled: true,
+ },
+ compile_dex: true,
+ }
+ `),
+ ).RunTest(t)
+
+ pbcp := result.Module("platform-bootclasspath", "android_common")
+ info := result.ModuleProvider(pbcp, java.MonolithicHiddenAPIInfoProvider).(java.MonolithicHiddenAPIInfo)
+
+ for _, category := range java.HiddenAPIFlagFileCategories {
+ name := category.PropertyName
+ message := fmt.Sprintf("category %s", name)
+ filename := strings.ReplaceAll(name, "_", "-")
+ expected := []string{fmt.Sprintf("%s.txt", filename), fmt.Sprintf("bar-%s.txt", filename)}
+ android.AssertPathsRelativeToTopEquals(t, message, expected, info.FlagsFilesByCategory[category])
+ }
+
+ android.AssertPathsRelativeToTopEquals(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/stub-flags.csv"}, info.StubFlagsPaths)
+ android.AssertPathsRelativeToTopEquals(t, "annotation flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/annotation-flags.csv"}, info.AnnotationFlagsPaths)
+ android.AssertPathsRelativeToTopEquals(t, "metadata flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/metadata.csv"}, info.MetadataPaths)
+ android.AssertPathsRelativeToTopEquals(t, "index flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/index.csv"}, info.IndexPaths)
+ android.AssertPathsRelativeToTopEquals(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/all-flags.csv"}, info.AllFlagsPaths)
+}
+
func TestPlatformBootclasspathDependencies(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForTestWithPlatformBootclasspath,
@@ -99,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",
@@ -132,6 +273,10 @@
apex: "com.android.art",
module: "art-bootclasspath-fragment",
},
+ {
+ apex: "myapex",
+ module: "my-bootclasspath-fragment",
+ },
],
}
`,
@@ -148,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.
@@ -172,6 +318,7 @@
// The fragments.
`com.android.art:art-bootclasspath-fragment`,
+ `myapex:my-bootclasspath-fragment`,
})
}
@@ -275,6 +422,12 @@
platform_bootclasspath {
name: "myplatform-bootclasspath",
+ fragments: [
+ {
+ apex: "myapex",
+ module:"mybootclasspath-fragment",
+ },
+ ],
}
`,
)
@@ -296,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.
@@ -307,6 +460,9 @@
// Only a source module exists.
"myapex:bar",
+
+ // The fragments.
+ "myapex:mybootclasspath-fragment",
})
}
@@ -325,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..f783172 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -16,7 +16,7 @@
import (
"fmt"
- "path/filepath"
+ "io"
"strconv"
"strings"
@@ -24,7 +24,6 @@
"android/soong/java"
"github.com/google/blueprint"
-
"github.com/google/blueprint/proptools"
)
@@ -47,21 +46,55 @@
}
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
+ // Canonical name of this APEX. Used to determine the path to the activated APEX on
+ // device (/apex/<apex_name>). If unspecified, follows the name property.
+ Apex_name *string
+
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 +105,18 @@
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) ApexVariationName() string {
+ return proptools.StringDefault(p.prebuiltCommonProperties.Apex_name, p.ModuleBase.BaseModuleName())
+}
+
func (p *prebuiltCommon) Prebuilt() *android.Prebuilt {
return &p.prebuilt
}
@@ -105,6 +150,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 {
@@ -228,11 +397,11 @@
})
// Create an ApexInfo for the prebuilt_apex.
- apexVariationName := android.RemoveOptionalPrebuiltPrefix(mctx.ModuleName())
+ apexVariationName := p.ApexVariationName()
apexInfo := android.ApexInfo{
ApexVariationName: apexVariationName,
InApexVariants: []string{apexVariationName},
- InApexModules: []string{apexVariationName},
+ InApexModules: []string{p.ModuleBase.BaseModuleName()}, // BaseModuleName() to avoid the prebuilt_ prefix.
ApexContents: []*android.ApexContents{apexContents},
ForPrebuiltApex: true,
}
@@ -272,19 +441,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 +510,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 +526,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 +553,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 +587,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 +650,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 +697,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 +733,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 +743,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 +792,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 +844,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 +858,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 +900,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 +935,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 +946,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 +969,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/aquery.go b/bazel/aquery.go
index d1119cb..8741afb 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -73,12 +73,13 @@
// BuildStatement contains information to register a build statement corresponding (one to one)
// with a Bazel action from Bazel's action graph.
type BuildStatement struct {
- Command string
- Depfile *string
- OutputPaths []string
- InputPaths []string
- Env []KeyValuePair
- Mnemonic string
+ Command string
+ Depfile *string
+ OutputPaths []string
+ InputPaths []string
+ SymlinkPaths []string
+ Env []KeyValuePair
+ Mnemonic string
}
// A helper type for aquery processing which facilitates retrieval of path IDs from their
@@ -234,10 +235,21 @@
OutputPaths: outputPaths,
InputPaths: inputPaths,
Env: actionEntry.EnvironmentVariables,
- Mnemonic: actionEntry.Mnemonic}
- if len(actionEntry.Arguments) < 1 {
+ Mnemonic: actionEntry.Mnemonic,
+ }
+
+ if isSymlinkAction(actionEntry) {
+ if len(inputPaths) != 1 || len(outputPaths) != 1 {
+ return nil, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths)
+ }
+ out := outputPaths[0]
+ outDir := proptools.ShellEscapeIncludingSpaces(filepath.Dir(out))
+ out = proptools.ShellEscapeIncludingSpaces(out)
+ in := proptools.ShellEscapeIncludingSpaces(inputPaths[0])
+ buildStatement.Command = fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -rsf %[3]s %[2]s", outDir, out, in)
+ buildStatement.SymlinkPaths = outputPaths[:]
+ } else if len(actionEntry.Arguments) < 1 {
return nil, fmt.Errorf("received action with no command: [%v]", buildStatement)
- continue
}
buildStatements = append(buildStatements, buildStatement)
}
@@ -245,9 +257,13 @@
return buildStatements, nil
}
+func isSymlinkAction(a action) bool {
+ return a.Mnemonic == "Symlink" || a.Mnemonic == "SolibSymlink"
+}
+
func shouldSkipAction(a action) bool {
- // TODO(b/180945121): Handle symlink actions.
- if a.Mnemonic == "Symlink" || a.Mnemonic == "SourceSymlinkManifest" || a.Mnemonic == "SymlinkTree" || a.Mnemonic == "SolibSymlink" {
+ // TODO(b/180945121): Handle complex symlink actions.
+ if a.Mnemonic == "SymlinkTree" || a.Mnemonic == "SourceSymlinkManifest" {
return true
}
// Middleman actions are not handled like other actions; they are handled separately as a
@@ -278,6 +294,9 @@
return "", fmt.Errorf("undefined path fragment id %d", currId)
}
labels = append([]string{currFragment.Label}, labels...)
+ if currId == currFragment.ParentId {
+ return "", fmt.Errorf("Fragment cannot refer to itself as parent %#v", currFragment)
+ }
currId = currFragment.ParentId
}
return filepath.Join(labels...), nil
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index 7b40dcd..43e4155 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -805,17 +805,229 @@
}
}
+func TestSimpleSymlink(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 3
+ }, {
+ "id": 2,
+ "pathFragmentId": 5
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "Symlink",
+ "inputDepSetIds": [1],
+ "outputIds": [2],
+ "primaryOutputId": 2
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "one"
+ }, {
+ "id": 2,
+ "label": "file_subdir",
+ "parentId": 1
+ }, {
+ "id": 3,
+ "label": "file",
+ "parentId": 2
+ }, {
+ "id": 4,
+ "label": "symlink_subdir",
+ "parentId": 1
+ }, {
+ "id": 5,
+ "label": "symlink",
+ "parentId": 4
+ }]
+}`
+
+ actual, err := AqueryBuildStatements([]byte(inputString))
+
+ if err != nil {
+ t.Errorf("Unexpected error %q", err)
+ }
+
+ expectedBuildStatements := []BuildStatement{
+ BuildStatement{
+ Command: "mkdir -p one/symlink_subdir && " +
+ "rm -f one/symlink_subdir/symlink && " +
+ "ln -rsf one/file_subdir/file one/symlink_subdir/symlink",
+ InputPaths: []string{"one/file_subdir/file"},
+ OutputPaths: []string{"one/symlink_subdir/symlink"},
+ SymlinkPaths: []string{"one/symlink_subdir/symlink"},
+ Mnemonic: "Symlink",
+ },
+ }
+ assertBuildStatements(t, actual, expectedBuildStatements)
+}
+
+func TestSymlinkQuotesPaths(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 3
+ }, {
+ "id": 2,
+ "pathFragmentId": 5
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "SolibSymlink",
+ "inputDepSetIds": [1],
+ "outputIds": [2],
+ "primaryOutputId": 2
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "one"
+ }, {
+ "id": 2,
+ "label": "file subdir",
+ "parentId": 1
+ }, {
+ "id": 3,
+ "label": "file",
+ "parentId": 2
+ }, {
+ "id": 4,
+ "label": "symlink subdir",
+ "parentId": 1
+ }, {
+ "id": 5,
+ "label": "symlink",
+ "parentId": 4
+ }]
+}`
+
+ actual, err := AqueryBuildStatements([]byte(inputString))
+
+ if err != nil {
+ t.Errorf("Unexpected error %q", err)
+ }
+
+ expectedBuildStatements := []BuildStatement{
+ BuildStatement{
+ Command: "mkdir -p 'one/symlink subdir' && " +
+ "rm -f 'one/symlink subdir/symlink' && " +
+ "ln -rsf 'one/file subdir/file' 'one/symlink subdir/symlink'",
+ InputPaths: []string{"one/file subdir/file"},
+ OutputPaths: []string{"one/symlink subdir/symlink"},
+ SymlinkPaths: []string{"one/symlink subdir/symlink"},
+ Mnemonic: "SolibSymlink",
+ },
+ }
+ assertBuildStatements(t, actual, expectedBuildStatements)
+}
+
+func TestSymlinkMultipleInputs(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 2
+ }, {
+ "id": 3,
+ "pathFragmentId": 3
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "Symlink",
+ "inputDepSetIds": [1],
+ "outputIds": [3],
+ "primaryOutputId": 3
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1,2]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "file"
+ }, {
+ "id": 2,
+ "label": "other_file"
+ }, {
+ "id": 3,
+ "label": "symlink"
+ }]
+}`
+
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
+}
+
+func TestSymlinkMultipleOutputs(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 2
+ }, {
+ "id": 3,
+ "pathFragmentId": 3
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "Symlink",
+ "inputDepSetIds": [1],
+ "outputIds": [2,3],
+ "primaryOutputId": 2
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "file"
+ }, {
+ "id": 2,
+ "label": "symlink"
+ }, {
+ "id": 3,
+ "label": "other_symlink"
+ }]
+}`
+
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file"], output ["symlink" "other_symlink"]`)
+}
+
func assertError(t *testing.T, err error, expected string) {
+ t.Helper()
if err == nil {
t.Errorf("expected error '%s', but got no error", expected)
} else if err.Error() != expected {
- t.Errorf("expected error '%s', but got: %s", expected, err.Error())
+ t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error())
}
}
// Asserts that the given actual build statements match the given expected build statements.
// Build statement equivalence is determined using buildStatementEquals.
func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
+ t.Helper()
if len(expected) != len(actual) {
t.Errorf("expected %d build statements, but got %d,\n expected: %v,\n actual: %v",
len(expected), len(actual), expected, actual)
@@ -852,6 +1064,12 @@
if !reflect.DeepEqual(stringSet(first.OutputPaths), stringSet(second.OutputPaths)) {
return false
}
+ if !reflect.DeepEqual(stringSet(first.SymlinkPaths), stringSet(second.SymlinkPaths)) {
+ return false
+ }
+ if first.Depfile != second.Depfile {
+ return false
+ }
return true
}
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 4de5aae..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(
@@ -488,6 +506,9 @@
ret = "{\n"
// Sort and print the struct props by the key.
structProps := extractStructProperties(propertyValue, indent)
+ if len(structProps) == 0 {
+ return "", nil
+ }
for _, k := range android.SortedStringKeys(structProps) {
ret += makeIndent(indent + 1)
ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
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 c464cec..285677a 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -49,6 +49,7 @@
cc.RegisterCCBuildComponents(ctx)
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
+ ctx.RegisterModuleType("cc_prebuilt_library_static", cc.PrebuiltStaticLibraryFactory)
ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
}
@@ -384,19 +385,69 @@
"-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"],
+)`},
+ })
+}
+
+func TestCcLibraryWholeStaticLibsAlwaysLink(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+ dir: "foo/bar",
+ filesystem: map[string]string{
+ "foo/bar/Android.bp": `
+cc_library {
+ name: "a",
+ whole_static_libs: ["whole_static_lib_for_both"],
+ static: {
+ whole_static_libs: ["whole_static_lib_for_static"],
+ },
+ shared: {
+ whole_static_libs: ["whole_static_lib_for_shared"],
+ },
+ bazel_module: { bp2build_available: true },
+}
+
+cc_prebuilt_library_static { name: "whole_static_lib_for_shared" }
+
+cc_prebuilt_library_static { name: "whole_static_lib_for_static" }
+
+cc_prebuilt_library_static { name: "whole_static_lib_for_both" }
+`,
+ },
+ blueprint: soongCcLibraryPreamble,
+ expectedBazelTargets: []string{`cc_library(
+ name = "a",
+ copts = [
+ "-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"],
)`},
})
}
@@ -486,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": [],
+ }),
+ },
)`},
})
}
@@ -623,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",
@@ -651,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",
+ ],
+ },
)`},
})
}
@@ -1231,3 +1290,173 @@
}),
)`}})
}
+
+func TestCcLibraryStrip(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ description: "cc_library strip args",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+ dir: "foo/bar",
+ filesystem: map[string]string{
+ "foo/bar/Android.bp": `
+cc_library {
+ name: "nothing",
+ bazel_module: { bp2build_available: true },
+}
+cc_library {
+ name: "keep_symbols",
+ bazel_module: { bp2build_available: true },
+ strip: {
+ keep_symbols: true,
+ }
+}
+cc_library {
+ name: "keep_symbols_and_debug_frame",
+ bazel_module: { bp2build_available: true },
+ strip: {
+ keep_symbols_and_debug_frame: true,
+ }
+}
+cc_library {
+ name: "none",
+ bazel_module: { bp2build_available: true },
+ strip: {
+ none: true,
+ }
+}
+cc_library {
+ name: "keep_symbols_list",
+ bazel_module: { bp2build_available: true },
+ strip: {
+ keep_symbols_list: ["symbol"],
+ }
+}
+cc_library {
+ name: "all",
+ bazel_module: { bp2build_available: true },
+ strip: {
+ all: true,
+ }
+}
+`,
+ },
+ blueprint: soongCcLibraryPreamble,
+ expectedBazelTargets: []string{`cc_library(
+ name = "all",
+ copts = [
+ "-Ifoo/bar",
+ "-I$(BINDIR)/foo/bar",
+ ],
+ strip = {
+ "all": True,
+ },
+)`, `cc_library(
+ name = "keep_symbols",
+ copts = [
+ "-Ifoo/bar",
+ "-I$(BINDIR)/foo/bar",
+ ],
+ strip = {
+ "keep_symbols": True,
+ },
+)`, `cc_library(
+ name = "keep_symbols_and_debug_frame",
+ copts = [
+ "-Ifoo/bar",
+ "-I$(BINDIR)/foo/bar",
+ ],
+ strip = {
+ "keep_symbols_and_debug_frame": True,
+ },
+)`, `cc_library(
+ name = "keep_symbols_list",
+ copts = [
+ "-Ifoo/bar",
+ "-I$(BINDIR)/foo/bar",
+ ],
+ strip = {
+ "keep_symbols_list": ["symbol"],
+ },
+)`, `cc_library(
+ name = "none",
+ copts = [
+ "-Ifoo/bar",
+ "-I$(BINDIR)/foo/bar",
+ ],
+ strip = {
+ "none": True,
+ },
+)`, `cc_library(
+ name = "nothing",
+ copts = [
+ "-Ifoo/bar",
+ "-I$(BINDIR)/foo/bar",
+ ],
+)`},
+ })
+}
+
+func TestCcLibraryStripWithArch(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ description: "cc_library strip args",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+ dir: "foo/bar",
+ filesystem: map[string]string{
+ "foo/bar/Android.bp": `
+cc_library {
+ name: "multi-arch",
+ bazel_module: { bp2build_available: true },
+ target: {
+ darwin: {
+ strip: {
+ keep_symbols_list: ["foo", "bar"]
+ }
+ },
+ },
+ arch: {
+ arm: {
+ strip: {
+ keep_symbols_and_debug_frame: true,
+ },
+ },
+ arm64: {
+ strip: {
+ keep_symbols: true,
+ },
+ },
+ }
+}
+`,
+ },
+ blueprint: soongCcLibraryPreamble,
+ expectedBazelTargets: []string{`cc_library(
+ name = "multi-arch",
+ copts = [
+ "-Ifoo/bar",
+ "-I$(BINDIR)/foo/bar",
+ ],
+ strip = {
+ "keep_symbols": select({
+ "//build/bazel/platforms/arch:arm64": True,
+ "//conditions:default": None,
+ }),
+ "keep_symbols_and_debug_frame": select({
+ "//build/bazel/platforms/arch:arm": True,
+ "//conditions:default": None,
+ }),
+ "keep_symbols_list": select({
+ "//build/bazel/platforms/os:darwin": [
+ "foo",
+ "bar",
+ ],
+ "//conditions:default": [],
+ }),
+ },
+)`},
+ })
+}
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/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index fae6101..9640024 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -319,7 +319,7 @@
var defStr string
switch mod.Type {
case "cts_support_package":
- mod.Type = "android_test"
+ mod.Type = "android_test_helper_app"
defStr = "cts_support_defaults"
case "cts_package":
mod.Type = "android_test"
@@ -622,12 +622,20 @@
func rewriteAndroidTest(f *Fixer) error {
for _, def := range f.tree.Defs {
mod, ok := def.(*parser.Module)
- if !(ok && mod.Type == "android_test") {
+ if !ok {
+ // The definition is not a module.
+ continue
+ }
+ if mod.Type != "android_test" && mod.Type != "android_test_helper_app" {
+ // The module is not an android_test or android_test_helper_app.
continue
}
// The rewriter converts LOCAL_MODULE_PATH attribute into a struct attribute
// 'local_module_path'. For the android_test module, it should be $(TARGET_OUT_DATA_APPS),
// that is, `local_module_path: { var: "TARGET_OUT_DATA_APPS"}`
+ // 1. if the `key: val` pair matches, (key is `local_module_path`,
+ // and val is `{ var: "TARGET_OUT_DATA_APPS"}`), this property is removed;
+ // 2. o/w, an error msg is thrown.
const local_module_path = "local_module_path"
if prop_local_module_path, ok := mod.GetProperty(local_module_path); ok {
removeProperty(mod, local_module_path)
@@ -637,7 +645,7 @@
continue
}
return indicateAttributeError(mod, "filename",
- "Only LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) is allowed for the android_test")
+ "Only LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) is allowed for the %s", mod.Type)
}
}
return nil
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index 61dfe1a..ebfeb22 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -636,7 +636,7 @@
}
`,
out: `
- android_test {
+ android_test_helper_app {
name: "foo",
defaults: ["cts_support_defaults"],
}
diff --git a/cc/binary.go b/cc/binary.go
index 999b82c..48f70d9 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -146,16 +146,17 @@
// modules common to most binaries, such as bionic libraries.
func (binary *binaryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
deps = binary.baseLinker.linkerDeps(ctx, deps)
- if ctx.toolchain().Bionic() {
- if !Bool(binary.baseLinker.Properties.Nocrt) {
- if binary.static() {
- deps.CrtBegin = "crtbegin_static"
- } else {
- deps.CrtBegin = "crtbegin_dynamic"
- }
- deps.CrtEnd = "crtend_android"
+ if !Bool(binary.baseLinker.Properties.Nocrt) {
+ if binary.static() {
+ deps.CrtBegin = ctx.toolchain().CrtBeginStaticBinary()
+ deps.CrtEnd = ctx.toolchain().CrtEndStaticBinary()
+ } else {
+ deps.CrtBegin = ctx.toolchain().CrtBeginSharedBinary()
+ deps.CrtEnd = ctx.toolchain().CrtEndSharedBinary()
}
+ }
+ if ctx.toolchain().Bionic() {
if binary.static() {
if ctx.selectedStl() == "libc++_static" {
deps.StaticLibs = append(deps.StaticLibs, "libm", "libc")
@@ -169,16 +170,8 @@
deps.LateStaticLibs = append(groupLibs, deps.LateStaticLibs...)
}
- // Embed the linker into host bionic binaries. This is needed to support host bionic,
- // as the linux kernel requires that the ELF interpreter referenced by PT_INTERP be
- // either an absolute path, or relative from CWD. To work around this, we extract
- // the load sections from the runtime linker ELF binary and embed them into each host
- // bionic binary, omitting the PT_INTERP declaration. The kernel will treat it as a static
- // binary, and then we use a special entry point to fix up the arguments passed by
- // the kernel before jumping to the embedded linker.
if ctx.Os() == android.LinuxBionic && !binary.static() {
deps.DynamicLinker = "linker"
- deps.LinkerFlagsFile = "host_bionic_linker_flags"
}
}
@@ -345,12 +338,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 +388,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 +419,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 +521,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 211fe5e..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.BazelLabelForModuleDeps(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.BazelLabelForModuleDeps(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
}
@@ -485,13 +484,18 @@
// Convenience struct to hold all attributes parsed from linker properties.
type linkerAttributes struct {
- deps bazel.LabelListAttribute
- dynamicDeps bazel.LabelListAttribute
- wholeArchiveDeps bazel.LabelListAttribute
- exportedDeps bazel.LabelListAttribute
- useLibcrt bazel.BoolAttribute
- linkopts bazel.StringListAttribute
- versionScript bazel.LabelAttribute
+ deps bazel.LabelListAttribute
+ dynamicDeps bazel.LabelListAttribute
+ wholeArchiveDeps bazel.LabelListAttribute
+ exportedDeps bazel.LabelListAttribute
+ useLibcrt bazel.BoolAttribute
+ linkopts bazel.StringListAttribute
+ versionScript bazel.LabelAttribute
+ stripKeepSymbols bazel.BoolAttribute
+ stripKeepSymbolsAndDebugFrame bazel.BoolAttribute
+ stripKeepSymbolsList bazel.StringListAttribute
+ stripAll bazel.BoolAttribute
+ stripNone bazel.BoolAttribute
}
// FIXME(b/187655838): Use the existing linkerFlags() function instead of duplicating logic here
@@ -515,6 +519,33 @@
var versionScript bazel.LabelAttribute
var useLibcrt bazel.BoolAttribute
+ var stripKeepSymbols bazel.BoolAttribute
+ var stripKeepSymbolsAndDebugFrame bazel.BoolAttribute
+ var stripKeepSymbolsList bazel.StringListAttribute
+ var stripAll bazel.BoolAttribute
+ var stripNone bazel.BoolAttribute
+
+ if libraryDecorator, ok := module.linker.(*libraryDecorator); ok {
+ stripProperties := libraryDecorator.stripper.StripProperties
+ stripKeepSymbols.Value = stripProperties.Strip.Keep_symbols
+ stripKeepSymbolsList.Value = stripProperties.Strip.Keep_symbols_list
+ stripKeepSymbolsAndDebugFrame.Value = stripProperties.Strip.Keep_symbols_and_debug_frame
+ stripAll.Value = stripProperties.Strip.All
+ stripNone.Value = stripProperties.Strip.None
+ }
+
+ for axis, configToProps := range module.GetArchVariantProperties(ctx, &StripProperties{}) {
+ for config, props := range configToProps {
+ if stripProperties, ok := props.(*StripProperties); ok {
+ stripKeepSymbols.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols)
+ stripKeepSymbolsList.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols_list)
+ stripKeepSymbolsAndDebugFrame.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols_and_debug_frame)
+ stripAll.SetSelectValue(axis, config, stripProperties.Strip.All)
+ stripNone.SetSelectValue(axis, config, stripProperties.Strip.None)
+ }
+ }
+ }
+
for _, linkerProps := range module.linker.linkerProps() {
if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok {
// Excludes to parallel Soong:
@@ -522,7 +553,7 @@
staticLibs := android.FirstUniqueStrings(baseLinkerProps.Static_libs)
staticDeps.Value = android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs)
wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs)
- wholeArchiveDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs))
+ wholeArchiveDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs))
sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs)
dynamicDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs))
@@ -549,7 +580,7 @@
staticLibs := android.FirstUniqueStrings(baseLinkerProps.Static_libs)
staticDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs))
wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs)
- wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs))
+ wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs))
sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs)
dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs))
@@ -572,13 +603,15 @@
excludesField string
// reference to the bazel attribute that should be set for the given product variable config
attribute *bazel.LabelListAttribute
+
+ depResolutionFunc func(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList
}
productVarToDepFields := map[string]productVarDep{
// product variables do not support exclude_shared_libs
- "Shared_libs": productVarDep{attribute: &dynamicDeps},
- "Static_libs": productVarDep{"Exclude_static_libs", &staticDeps},
- "Whole_static_libs": productVarDep{"Exclude_static_libs", &wholeArchiveDeps},
+ "Shared_libs": productVarDep{attribute: &dynamicDeps, depResolutionFunc: android.BazelLabelForModuleDepsExcludes},
+ "Static_libs": productVarDep{"Exclude_static_libs", &staticDeps, android.BazelLabelForModuleDepsExcludes},
+ "Whole_static_libs": productVarDep{"Exclude_static_libs", &wholeArchiveDeps, android.BazelLabelForModuleWholeDepsExcludes},
}
productVariableProps := android.ProductVariableProperties(ctx)
@@ -612,7 +645,8 @@
if excludes, ok = excludesProp.Property.([]string); excludesExists && !ok {
ctx.ModuleErrorf("Could not convert product variable %s property", dep.excludesField)
}
- dep.attribute.SetSelectValue(bazel.ProductVariableConfigurationAxis(config), config, android.BazelLabelForModuleDepsExcludes(ctx, android.FirstUniqueStrings(includes), excludes))
+
+ dep.attribute.SetSelectValue(bazel.ProductVariableConfigurationAxis(config), config, dep.depResolutionFunc(ctx, android.FirstUniqueStrings(includes), excludes))
}
}
@@ -630,6 +664,13 @@
linkopts: linkopts,
useLibcrt: useLibcrt,
versionScript: versionScript,
+
+ // Strip properties
+ stripKeepSymbols: stripKeepSymbols,
+ stripKeepSymbolsAndDebugFrame: stripKeepSymbolsAndDebugFrame,
+ stripKeepSymbolsList: stripKeepSymbolsList,
+ stripAll: stripAll,
+ stripNone: stripNone,
}
}
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 818490a..15cb39a 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"}
@@ -826,6 +831,46 @@
hideApexVariantFromMake bool
}
+func (c *Module) AddJSONData(d *map[string]interface{}) {
+ c.AndroidModuleBase().AddJSONData(d)
+ (*d)["Cc"] = map[string]interface{}{
+ "SdkVersion": c.SdkVersion(),
+ "MinSdkVersion": c.MinSdkVersion(),
+ "VndkVersion": c.VndkVersion(),
+ "ProductSpecific": c.ProductSpecific(),
+ "SocSpecific": c.SocSpecific(),
+ "DeviceSpecific": c.DeviceSpecific(),
+ "InProduct": c.InProduct(),
+ "InVendor": c.InVendor(),
+ "InRamdisk": c.InRamdisk(),
+ "InVendorRamdisk": c.InVendorRamdisk(),
+ "InRecovery": c.InRecovery(),
+ "VendorAvailable": c.VendorAvailable(),
+ "ProductAvailable": c.ProductAvailable(),
+ "RamdiskAvailable": c.RamdiskAvailable(),
+ "VendorRamdiskAvailable": c.VendorRamdiskAvailable(),
+ "RecoveryAvailable": c.RecoveryAvailable(),
+ "OdmAvailable": c.OdmAvailable(),
+ "InstallInData": c.InstallInData(),
+ "InstallInRamdisk": c.InstallInRamdisk(),
+ "InstallInSanitizerDir": c.InstallInSanitizerDir(),
+ "InstallInVendorRamdisk": c.InstallInVendorRamdisk(),
+ "InstallInRecovery": c.InstallInRecovery(),
+ "InstallInRoot": c.InstallInRoot(),
+ "IsVndk": c.IsVndk(),
+ "IsVndkExt": c.IsVndkExt(),
+ "IsVndkPrivate": c.IsVndkPrivate(),
+ "IsVndkSp": c.IsVndkSp(),
+ "IsLlndk": c.IsLlndk(),
+ "IsLlndkPublic": c.IsLlndkPublic(),
+ "IsSnapshotLibrary": c.IsSnapshotLibrary(),
+ "IsSnapshotPrebuilt": c.IsSnapshotPrebuilt(),
+ "IsVendorPublicLibrary": c.IsVendorPublicLibrary(),
+ "ApexSdkVersion": c.apexSdkVersion,
+ "TestFor": c.TestFor(),
+ }
+}
+
func (c *Module) SetPreventInstall() {
c.Properties.PreventInstall = true
}
@@ -1254,7 +1299,7 @@
return name
}
-func (c *Module) bootstrap() bool {
+func (c *Module) Bootstrap() bool {
return Bool(c.Properties.Bootstrap)
}
@@ -1305,6 +1350,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
@@ -1495,7 +1544,7 @@
}
func (ctx *moduleContextImpl) bootstrap() bool {
- return ctx.mod.bootstrap()
+ return ctx.mod.Bootstrap()
}
func (ctx *moduleContextImpl) nativeCoverage() bool {
@@ -1673,10 +1722,6 @@
c.makeLinkType = GetMakeLinkType(actx, c)
- if c.maybeGenerateBazelActions(actx) {
- return
- }
-
ctx := &moduleContext{
ModuleContext: actx,
moduleContextImpl: moduleContextImpl{
@@ -1685,6 +1730,11 @@
}
ctx.ctx = ctx
+ if c.maybeGenerateBazelActions(actx) {
+ c.maybeInstall(ctx, apexInfo)
+ return
+ }
+
deps := c.depsToPaths(ctx)
if ctx.Failed() {
return
@@ -1772,19 +1822,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 +1833,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
@@ -2246,16 +2304,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 +2610,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
}
@@ -2638,66 +2686,8 @@
return
}
- sharedLibraryInfo := ctx.OtherModuleProvider(dep, SharedLibraryInfoProvider).(SharedLibraryInfo)
- sharedLibraryStubsInfo := ctx.OtherModuleProvider(dep, SharedLibraryStubsProvider).(SharedLibraryStubsInfo)
-
- if !libDepTag.explicitlyVersioned && len(sharedLibraryStubsInfo.SharedStubLibraries) > 0 {
- useStubs := false
-
- if lib := moduleLibraryInterface(dep); lib.buildStubs() && c.UseVndk() { // LLNDK
- if !apexInfo.IsForPlatform() {
- // For platform libraries, use current version of LLNDK
- // If this is for use_vendor apex we will apply the same rules
- // of apex sdk enforcement below to choose right version.
- useStubs = true
- }
- } else if apexInfo.IsForPlatform() {
- // If not building for APEX, use stubs only when it is from
- // an APEX (and not from platform)
- // However, for host, ramdisk, vendor_ramdisk, recovery or bootstrap modules,
- // always link to non-stub variant
- useStubs = dep.(android.ApexModule).NotInPlatform() && !c.bootstrap()
- if useStubs {
- // Another exception: if this module is a test for an APEX, then
- // it is linked with the non-stub variant of a module in the APEX
- // as if this is part of the APEX.
- testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
- for _, apexContents := range testFor.ApexContents {
- if apexContents.DirectlyInApex(depName) {
- useStubs = false
- break
- }
- }
- }
- if useStubs {
- // Yet another exception: If this module and the dependency are
- // available to the same APEXes then skip stubs between their
- // platform variants. This complements the test_for case above,
- // which avoids the stubs on a direct APEX library dependency, by
- // avoiding stubs for indirect test dependencies as well.
- //
- // TODO(b/183882457): This doesn't work if the two libraries have
- // only partially overlapping apex_available. For that test_for
- // modules would need to be split into APEX variants and resolved
- // separately for each APEX they have access to.
- if android.AvailableToSameApexes(c, dep.(android.ApexModule)) {
- useStubs = false
- }
- }
- } else {
- // If building for APEX, use stubs when the parent is in any APEX that
- // the child is not in.
- useStubs = !android.DirectlyInAllApexes(apexInfo, depName)
- }
-
- // when to use (unspecified) stubs, use the latest one.
- if useStubs {
- stubs := sharedLibraryStubsInfo.SharedStubLibraries
- toUse := stubs[len(stubs)-1]
- sharedLibraryInfo = toUse.SharedLibraryInfo
- depExporterInfo = toUse.FlagExporterInfo
- }
- }
+ sharedLibraryInfo, returnedDepExporterInfo := ChooseStubOrImpl(ctx, dep)
+ depExporterInfo = returnedDepExporterInfo
// Stubs lib doesn't link to the shared lib dependencies. Don't set
// linkFile, depFile, and ptr.
@@ -2878,9 +2868,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
}
@@ -2910,6 +2900,100 @@
return depPaths
}
+// ChooseStubOrImpl determines whether a given dependency should be redirected to the stub variant
+// of the dependency or not, and returns the SharedLibraryInfo and FlagExporterInfo for the right
+// dependency. The stub variant is selected when the dependency crosses a boundary where each side
+// has different level of updatability. For example, if a library foo in an APEX depends on a
+// library bar which provides stable interface and exists in the platform, foo uses the stub variant
+// of bar. If bar doesn't provide a stable interface (i.e. buildStubs() == false) or is in the
+// same APEX as foo, the non-stub variant of bar is used.
+func ChooseStubOrImpl(ctx android.ModuleContext, dep android.Module) (SharedLibraryInfo, FlagExporterInfo) {
+ depName := ctx.OtherModuleName(dep)
+ depTag := ctx.OtherModuleDependencyTag(dep)
+ libDepTag, ok := depTag.(libraryDependencyTag)
+ if !ok || !libDepTag.shared() {
+ panic(fmt.Errorf("Unexpected dependency tag: %T", depTag))
+ }
+
+ thisModule, ok := ctx.Module().(android.ApexModule)
+ if !ok {
+ panic(fmt.Errorf("Not an APEX module: %q", ctx.ModuleName()))
+ }
+
+ useVndk := false
+ bootstrap := false
+ if linkable, ok := ctx.Module().(LinkableInterface); !ok {
+ panic(fmt.Errorf("Not a Linkable module: %q", ctx.ModuleName()))
+ } else {
+ useVndk = linkable.UseVndk()
+ bootstrap = linkable.Bootstrap()
+ }
+
+ sharedLibraryInfo := ctx.OtherModuleProvider(dep, SharedLibraryInfoProvider).(SharedLibraryInfo)
+ depExporterInfo := ctx.OtherModuleProvider(dep, FlagExporterInfoProvider).(FlagExporterInfo)
+ sharedLibraryStubsInfo := ctx.OtherModuleProvider(dep, SharedLibraryStubsProvider).(SharedLibraryStubsInfo)
+ apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+
+ if !libDepTag.explicitlyVersioned && len(sharedLibraryStubsInfo.SharedStubLibraries) > 0 {
+ useStubs := false
+
+ if lib := moduleLibraryInterface(dep); lib.buildStubs() && useVndk { // LLNDK
+ if !apexInfo.IsForPlatform() {
+ // For platform libraries, use current version of LLNDK
+ // If this is for use_vendor apex we will apply the same rules
+ // of apex sdk enforcement below to choose right version.
+ useStubs = true
+ }
+ } else if apexInfo.IsForPlatform() || apexInfo.UsePlatformApis {
+ // If not building for APEX or the containing APEX allows the use of
+ // platform APIs, use stubs only when it is from an APEX (and not from
+ // platform) However, for host, ramdisk, vendor_ramdisk, recovery or
+ // bootstrap modules, always link to non-stub variant
+ useStubs = dep.(android.ApexModule).NotInPlatform() && !bootstrap
+ if useStubs {
+ // Another exception: if this module is a test for an APEX, then
+ // it is linked with the non-stub variant of a module in the APEX
+ // as if this is part of the APEX.
+ testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
+ for _, apexContents := range testFor.ApexContents {
+ if apexContents.DirectlyInApex(depName) {
+ useStubs = false
+ break
+ }
+ }
+ }
+ if useStubs {
+ // Yet another exception: If this module and the dependency are
+ // available to the same APEXes then skip stubs between their
+ // platform variants. This complements the test_for case above,
+ // which avoids the stubs on a direct APEX library dependency, by
+ // avoiding stubs for indirect test dependencies as well.
+ //
+ // TODO(b/183882457): This doesn't work if the two libraries have
+ // only partially overlapping apex_available. For that test_for
+ // modules would need to be split into APEX variants and resolved
+ // separately for each APEX they have access to.
+ if android.AvailableToSameApexes(thisModule, dep.(android.ApexModule)) {
+ useStubs = false
+ }
+ }
+ } else {
+ // If building for APEX, use stubs when the parent is in any APEX that
+ // the child is not in.
+ useStubs = !android.DirectlyInAllApexes(apexInfo, depName)
+ }
+
+ // when to use (unspecified) stubs, use the latest one.
+ if useStubs {
+ stubs := sharedLibraryStubsInfo.SharedStubLibraries
+ toUse := stubs[len(stubs)-1]
+ sharedLibraryInfo = toUse.SharedLibraryInfo
+ depExporterInfo = toUse.FlagExporterInfo
+ }
+ }
+ return sharedLibraryInfo, depExporterInfo
+}
+
// orderStaticModuleDeps rearranges the order of the static library dependencies of the module
// to match the topological order of the dependency tree, including any static analogues of
// direct shared libraries. It returns the ordered static dependencies, and an android.DepSet
@@ -3380,7 +3464,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 ef413fe..0a74e58 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -3662,305 +3662,6 @@
android.AssertStringDoesContain(t, "min sdk version", cFlags, "-target aarch64-linux-android29")
}
-type MemtagNoteType int
-
-const (
- None MemtagNoteType = iota + 1
- Sync
- Async
-)
-
-func (t MemtagNoteType) str() string {
- switch t {
- case None:
- return "none"
- case Sync:
- return "sync"
- case Async:
- return "async"
- default:
- panic("invalid note type")
- }
-}
-
-func checkHasMemtagNote(t *testing.T, m android.TestingModule, expected MemtagNoteType) {
- note_async := "note_memtag_heap_async"
- note_sync := "note_memtag_heap_sync"
-
- found := None
- implicits := m.Rule("ld").Implicits
- for _, lib := range implicits {
- if strings.Contains(lib.Rel(), note_async) {
- found = Async
- break
- } else if strings.Contains(lib.Rel(), note_sync) {
- found = Sync
- break
- }
- }
-
- if found != expected {
- t.Errorf("Wrong Memtag note in target %q: found %q, expected %q", m.Module().(*Module).Name(), found.str(), expected.str())
- }
-}
-
-var prepareForTestWithMemtagHeap = android.GroupFixturePreparers(
- android.FixtureModifyMockFS(func(fs android.MockFS) {
- templateBp := `
- cc_test {
- name: "%[1]s_test",
- gtest: false,
- }
-
- cc_test {
- name: "%[1]s_test_false",
- gtest: false,
- sanitize: { memtag_heap: false },
- }
-
- cc_test {
- name: "%[1]s_test_true",
- gtest: false,
- sanitize: { memtag_heap: true },
- }
-
- cc_test {
- name: "%[1]s_test_true_nodiag",
- gtest: false,
- sanitize: { memtag_heap: true, diag: { memtag_heap: false } },
- }
-
- cc_test {
- name: "%[1]s_test_true_diag",
- gtest: false,
- sanitize: { memtag_heap: true, diag: { memtag_heap: true } },
- }
-
- cc_binary {
- name: "%[1]s_binary",
- }
-
- cc_binary {
- name: "%[1]s_binary_false",
- sanitize: { memtag_heap: false },
- }
-
- cc_binary {
- name: "%[1]s_binary_true",
- sanitize: { memtag_heap: true },
- }
-
- cc_binary {
- name: "%[1]s_binary_true_nodiag",
- sanitize: { memtag_heap: true, diag: { memtag_heap: false } },
- }
-
- cc_binary {
- name: "%[1]s_binary_true_diag",
- sanitize: { memtag_heap: true, diag: { memtag_heap: true } },
- }
- `
- subdirDefaultBp := fmt.Sprintf(templateBp, "default")
- subdirExcludeBp := fmt.Sprintf(templateBp, "exclude")
- subdirSyncBp := fmt.Sprintf(templateBp, "sync")
- subdirAsyncBp := fmt.Sprintf(templateBp, "async")
-
- fs.Merge(android.MockFS{
- "subdir_default/Android.bp": []byte(subdirDefaultBp),
- "subdir_exclude/Android.bp": []byte(subdirExcludeBp),
- "subdir_sync/Android.bp": []byte(subdirSyncBp),
- "subdir_async/Android.bp": []byte(subdirAsyncBp),
- })
- }),
- android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
- variables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
- // "subdir_exclude" is covered by both include and exclude paths. Exclude wins.
- variables.MemtagHeapSyncIncludePaths = []string{"subdir_sync", "subdir_exclude"}
- variables.MemtagHeapAsyncIncludePaths = []string{"subdir_async", "subdir_exclude"}
- }),
-)
-
-func TestSanitizeMemtagHeap(t *testing.T) {
- variant := "android_arm64_armv8-a"
-
- result := android.GroupFixturePreparers(
- prepareForCcTest,
- prepareForTestWithMemtagHeap,
- ).RunTest(t)
- ctx := result.TestContext
-
- checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
-}
-
-func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) {
- variant := "android_arm64_armv8-a"
-
- result := android.GroupFixturePreparers(
- prepareForCcTest,
- prepareForTestWithMemtagHeap,
- android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
- variables.SanitizeDevice = []string{"memtag_heap"}
- }),
- ).RunTest(t)
- ctx := result.TestContext
-
- checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
-}
-
-func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) {
- variant := "android_arm64_armv8-a"
-
- result := android.GroupFixturePreparers(
- prepareForCcTest,
- prepareForTestWithMemtagHeap,
- android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
- variables.SanitizeDevice = []string{"memtag_heap"}
- variables.SanitizeDeviceDiag = []string{"memtag_heap"}
- }),
- ).RunTest(t)
- ctx := result.TestContext
-
- checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
-
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
- checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
-}
-
func TestIncludeDirsExporting(t *testing.T) {
// Trim spaces from the beginning, end and immediately after any newline characters. Leaves
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index e4a8b62..c1d4f17 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -17,6 +17,8 @@
"toolchain.go",
"vndk.go",
+ "bionic.go",
+
"arm_device.go",
"arm64_device.go",
"arm64_fuchsia_device.go",
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 864fba1..af6361b 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -149,6 +149,7 @@
)
type toolchainArm64 struct {
+ toolchainBionic
toolchain64Bit
ldflags string
diff --git a/cc/config/arm64_fuchsia_device.go b/cc/config/arm64_fuchsia_device.go
index 02c0c14..a6b5e8c 100644
--- a/cc/config/arm64_fuchsia_device.go
+++ b/cc/config/arm64_fuchsia_device.go
@@ -82,10 +82,6 @@
return "--target=arm64-fuchsia --sysroot=" + fuchsiaArm64SysRoot + " -I" + fuchsiaArm64SysRoot + "/include"
}
-func (t *toolchainFuchsiaArm64) Bionic() bool {
- return false
-}
-
func (t *toolchainFuchsiaArm64) ToolchainClangCflags() string {
return "-march=armv8-a"
}
diff --git a/cc/config/arm64_linux_host.go b/cc/config/arm64_linux_host.go
index 59c52d1..83bd799 100644
--- a/cc/config/arm64_linux_host.go
+++ b/cc/config/arm64_linux_host.go
@@ -45,6 +45,16 @@
"-Wl,--hash-style=gnu",
"-Wl,--no-undefined-version",
})
+
+ // Embed the linker into host bionic binaries. This is needed to support host bionic,
+ // as the linux kernel requires that the ELF interpreter referenced by PT_INTERP be
+ // either an absolute path, or relative from CWD. To work around this, we extract
+ // the load sections from the runtime linker ELF binary and embed them into each host
+ // bionic binary, omitting the PT_INTERP declaration. The kernel will treat it as a static
+ // binary, and then we use a special entry point to fix up the arguments passed by
+ // the kernel before jumping to the embedded linker.
+ linuxArm64CrtBeginSharedBinary = append(android.CopyOf(bionicCrtBeginSharedBinary),
+ "host_bionic_linker_script")
)
func init() {
@@ -68,6 +78,10 @@
return "${config.Arm64ClangCflags} ${config.LinuxBionicArm64Cflags}"
}
+func (toolchainLinuxArm64) CrtBeginSharedBinary() []string {
+ return linuxArm64CrtBeginSharedBinary
+}
+
func linuxArm64ToolchainFactory(arch android.Arch) Toolchain {
archVariant := "armv8-a" // for host, default to armv8-a
toolchainClangCflags := []string{arm64ClangArchVariantCflagsVar[archVariant]}
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index 439084e..3c27730 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -237,6 +237,7 @@
)
type toolchainArm struct {
+ toolchainBionic
toolchain32Bit
ldflags string
lldflags string
diff --git a/cc/config/bionic.go b/cc/config/bionic.go
new file mode 100644
index 0000000..e87f571
--- /dev/null
+++ b/cc/config/bionic.go
@@ -0,0 +1,37 @@
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+type toolchainBionic struct {
+}
+
+var (
+ bionicDefaultSharedLibraries = []string{"libc", "libm", "libdl"}
+
+ bionicCrtBeginStaticBinary, bionicCrtEndStaticBinary = []string{"crtbegin_static"}, []string{"crtend_android"}
+ bionicCrtBeginSharedBinary, bionicCrtEndSharedBinary = []string{"crtbegin_dynamic"}, []string{"crtend_android"}
+ bionicCrtBeginSharedLibrary, bionicCrtEndSharedLibrary = []string{"crtbegin_so"}, []string{"crtend_so"}
+)
+
+func (toolchainBionic) Bionic() bool { return true }
+
+func (toolchainBionic) DefaultSharedLibraries() []string { return bionicDefaultSharedLibraries }
+
+func (toolchainBionic) CrtBeginStaticBinary() []string { return bionicCrtBeginStaticBinary }
+func (toolchainBionic) CrtBeginSharedBinary() []string { return bionicCrtBeginSharedBinary }
+func (toolchainBionic) CrtBeginSharedLibrary() []string { return bionicCrtBeginSharedLibrary }
+func (toolchainBionic) CrtEndStaticBinary() []string { return bionicCrtEndStaticBinary }
+func (toolchainBionic) CrtEndSharedBinary() []string { return bionicCrtEndSharedBinary }
+func (toolchainBionic) CrtEndSharedLibrary() []string { return bionicCrtEndSharedLibrary }
diff --git a/cc/config/global.go b/cc/config/global.go
index 24e10a4..4957767 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -46,6 +46,7 @@
"-O2",
"-g",
+ "-fdebug-default-version=5",
"-fdebug-info-for-profiling",
"-fno-strict-aliasing",
@@ -145,8 +146,8 @@
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-r416183b"
- ClangDefaultShortVersion = "12.0.5"
+ ClangDefaultVersion = "clang-r416183b1"
+ ClangDefaultShortVersion = "12.0.7"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index fce28c1..ab09751 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -106,6 +106,17 @@
AvailableLibraries() []string
+ CrtBeginStaticBinary() []string
+ CrtBeginSharedBinary() []string
+ CrtBeginSharedLibrary() []string
+ CrtEndStaticBinary() []string
+ CrtEndSharedBinary() []string
+ CrtEndSharedLibrary() []string
+
+ // DefaultSharedLibraries returns the list of shared libraries that will be added to all
+ // targets unless they explicitly specify system_shared_libs.
+ DefaultSharedLibraries() []string
+
Bionic() bool
}
@@ -165,11 +176,22 @@
}
func (toolchainBase) AvailableLibraries() []string {
- return []string{}
+ return nil
+}
+
+func (toolchainBase) CrtBeginStaticBinary() []string { return nil }
+func (toolchainBase) CrtBeginSharedBinary() []string { return nil }
+func (toolchainBase) CrtBeginSharedLibrary() []string { return nil }
+func (toolchainBase) CrtEndStaticBinary() []string { return nil }
+func (toolchainBase) CrtEndSharedBinary() []string { return nil }
+func (toolchainBase) CrtEndSharedLibrary() []string { return nil }
+
+func (toolchainBase) DefaultSharedLibraries() []string {
+ return nil
}
func (toolchainBase) Bionic() bool {
- return true
+ return false
}
func (t toolchainBase) ToolPath() string {
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index 1e25a3b..54dc6d5 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -123,6 +123,7 @@
}
type toolchainX86_64 struct {
+ toolchainBionic
toolchain64Bit
toolchainClangCflags string
}
diff --git a/cc/config/x86_64_fuchsia_device.go b/cc/config/x86_64_fuchsia_device.go
index 0f2013b..d6837c8 100644
--- a/cc/config/x86_64_fuchsia_device.go
+++ b/cc/config/x86_64_fuchsia_device.go
@@ -83,10 +83,6 @@
return "--target=x86_64-fuchsia --sysroot=" + fuchsiaSysRoot + " -I" + fuchsiaSysRoot + "/include"
}
-func (t *toolchainFuchsiaX8664) Bionic() bool {
- return false
-}
-
func (t *toolchainFuchsiaX8664) YasmFlags() string {
return "-f elf64 -m amd64"
}
diff --git a/cc/config/x86_darwin_host.go b/cc/config/x86_darwin_host.go
index b0344af..4e3e2a6 100644
--- a/cc/config/x86_darwin_host.go
+++ b/cc/config/x86_darwin_host.go
@@ -241,10 +241,6 @@
return darwinAvailableLibraries
}
-func (t *toolchainDarwin) Bionic() bool {
- return false
-}
-
func (t *toolchainDarwin) ToolPath() string {
return "${config.MacToolPath}"
}
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index fe83098..1507d98 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -134,6 +134,7 @@
}
type toolchainX86 struct {
+ toolchainBionic
toolchain32Bit
toolchainClangCflags string
}
diff --git a/cc/config/x86_linux_bionic_host.go b/cc/config/x86_linux_bionic_host.go
index fa625e3..e7e5f2d 100644
--- a/cc/config/x86_linux_bionic_host.go
+++ b/cc/config/x86_linux_bionic_host.go
@@ -63,6 +63,16 @@
})
linuxBionicLldflags = ClangFilterUnknownLldflags(linuxBionicLdflags)
+
+ // Embed the linker into host bionic binaries. This is needed to support host bionic,
+ // as the linux kernel requires that the ELF interpreter referenced by PT_INTERP be
+ // either an absolute path, or relative from CWD. To work around this, we extract
+ // the load sections from the runtime linker ELF binary and embed them into each host
+ // bionic binary, omitting the PT_INTERP declaration. The kernel will treat it as a static
+ // binary, and then we use a special entry point to fix up the arguments passed by
+ // the kernel before jumping to the embedded linker.
+ linuxBionicCrtBeginSharedBinary = append(android.CopyOf(bionicCrtBeginSharedBinary),
+ "host_bionic_linker_script")
)
func init() {
@@ -76,6 +86,7 @@
type toolchainLinuxBionic struct {
toolchain64Bit
+ toolchainBionic
}
func (t *toolchainLinuxBionic) Name() string {
@@ -133,14 +144,14 @@
return nil
}
-func (t *toolchainLinuxBionic) Bionic() bool {
- return true
-}
-
func (toolchainLinuxBionic) LibclangRuntimeLibraryArch() string {
return "x86_64"
}
+func (toolchainLinuxBionic) CrtBeginSharedBinary() []string {
+ return linuxBionicCrtBeginSharedBinary
+}
+
var toolchainLinuxBionicSingleton Toolchain = &toolchainLinuxBionic{}
func linuxBionicToolchainFactory(arch android.Arch) Toolchain {
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 13b5511..c406c88 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -245,10 +245,6 @@
return linuxAvailableLibraries
}
-func (t *toolchainLinux) Bionic() bool {
- return false
-}
-
var toolchainLinuxX86Singleton Toolchain = &toolchainLinuxX86{}
var toolchainLinuxX8664Singleton Toolchain = &toolchainLinuxX8664{}
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 196d575..684694b 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,22 @@
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
+ // Common properties shared between both shared and static variants.
+ Shared staticOrSharedAttributes
+ Static staticOrSharedAttributes
- // Attributes pertaining to static variant.
- Static_srcs bazel.LabelListAttribute
- Static_srcs_c bazel.LabelListAttribute
- Static_srcs_as bazel.LabelListAttribute
- Static_copts bazel.StringListAttribute
+ Strip stripAttributes
+}
- 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
+type stripAttributes struct {
+ Keep_symbols bazel.BoolAttribute
+ Keep_symbols_and_debug_frame bazel.BoolAttribute
+ Keep_symbols_list bazel.StringListAttribute
+ All bazel.BoolAttribute
+ None bazel.BoolAttribute
}
type bazelCcLibrary struct {
@@ -323,22 +317,19 @@
Linkopts: linkerAttrs.linkopts,
Use_libcrt: linkerAttrs.useLibcrt,
- 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,
+ Version_script: linkerAttrs.versionScript,
- 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,
+ Strip: stripAttributes{
+ Keep_symbols: linkerAttrs.stripKeepSymbols,
+ Keep_symbols_and_debug_frame: linkerAttrs.stripKeepSymbolsAndDebugFrame,
+ Keep_symbols_list: linkerAttrs.stripKeepSymbolsList,
+ All: linkerAttrs.stripAll,
+ None: linkerAttrs.stripNone,
+ },
+
+ Shared: sharedAttrs,
+
+ Static: staticAttrs,
}
props := bazel.BazelTargetModuleProperties{
@@ -561,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
@@ -584,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)
@@ -597,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{}
@@ -1111,9 +1174,9 @@
deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.StaticProperties.Static.Export_shared_lib_headers...)
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"
+ if !Bool(library.baseLinker.Properties.Nocrt) {
+ deps.CrtBegin = append(deps.CrtBegin, ctx.toolchain().CrtBeginSharedLibrary()...)
+ deps.CrtEnd = append(deps.CrtEnd, ctx.toolchain().CrtEndSharedLibrary()...)
}
deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.SharedProperties.Shared.Whole_static_libs...)
deps.StaticLibs = append(deps.StaticLibs, library.SharedProperties.Shared.Static_libs...)
@@ -1343,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/linkable.go b/cc/linkable.go
index 0a5d16c..231626e 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -165,6 +165,9 @@
// "product_specific: true" modules are included here.
UseVndk() bool
+ // Bootstrap tests if this module is allowed to use non-APEX version of libraries.
+ Bootstrap() bool
+
// IsVndkSp returns true if this is a VNDK-SP module.
IsVndkSp() bool
diff --git a/cc/linker.go b/cc/linker.go
index 895931a..7b16b40 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"`
@@ -344,7 +344,7 @@
// Provide a default system_shared_libs if it is unspecified. Note: If an
// empty list [] is specified, it implies that the module declines the
// default system_shared_libs.
- deps.SystemSharedLibs = []string{"libc", "libm", "libdl"}
+ deps.SystemSharedLibs = append(deps.SystemSharedLibs, ctx.toolchain().DefaultSharedLibraries()...)
}
if inList("libdl", deps.SharedLibs) {
@@ -365,10 +365,10 @@
indexList("libdl", deps.SystemSharedLibs) < indexList("libc", deps.SystemSharedLibs) {
ctx.PropertyErrorf("system_shared_libs", "libdl must be after libc")
}
-
- deps.LateSharedLibs = append(deps.LateSharedLibs, deps.SystemSharedLibs...)
}
+ deps.LateSharedLibs = append(deps.LateSharedLibs, deps.SystemSharedLibs...)
+
if ctx.Fuchsia() {
if ctx.ModuleName() != "libbioniccompat" &&
ctx.ModuleName() != "libcompiler_rt-extras" &&
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..c8cd056 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'])),
]),
]
@@ -411,6 +413,40 @@
""")
self.assertEqual(expected_version, version_file.getvalue())
+ def test_empty_stub(self) -> None:
+ """Tests that empty stubs can be generated.
+
+ This is not a common case, but libraries whose only behavior is to
+ interpose symbols to alter existing behavior do not need to expose
+ their interposing symbols as API, so it's possible for the stub to be
+ empty while still needing a stub to link against. libsigchain is an
+ example of this.
+ """
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_1 {
+ local:
+ *;
+ };
+ """))
+ parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'),
+ 9, llndk=False, apex=True)
+ versions = parser.parse()
+
+ src_file = io.StringIO()
+ version_file = io.StringIO()
+ symbol_list_file = io.StringIO()
+ generator = ndkstubgen.Generator(src_file,
+ version_file,
+ symbol_list_file,
+ Arch('arm'),
+ 9,
+ llndk=False,
+ apex=True)
+ generator.write(versions)
+
+ self.assertEqual('', src_file.getvalue())
+ self.assertEqual('', version_file.getvalue())
+
def main() -> None:
suite = unittest.TestLoader().loadTestsFromName(__name__)
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/sanitize_test.go b/cc/sanitize_test.go
index f126346..4430fc3 100644
--- a/cc/sanitize_test.go
+++ b/cc/sanitize_test.go
@@ -15,6 +15,8 @@
package cc
import (
+ "fmt"
+ "strings"
"testing"
"android/soong/android"
@@ -202,3 +204,302 @@
t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) })
t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") })
}
+
+type MemtagNoteType int
+
+const (
+ None MemtagNoteType = iota + 1
+ Sync
+ Async
+)
+
+func (t MemtagNoteType) str() string {
+ switch t {
+ case None:
+ return "none"
+ case Sync:
+ return "sync"
+ case Async:
+ return "async"
+ default:
+ panic("invalid note type")
+ }
+}
+
+func checkHasMemtagNote(t *testing.T, m android.TestingModule, expected MemtagNoteType) {
+ note_async := "note_memtag_heap_async"
+ note_sync := "note_memtag_heap_sync"
+
+ found := None
+ implicits := m.Rule("ld").Implicits
+ for _, lib := range implicits {
+ if strings.Contains(lib.Rel(), note_async) {
+ found = Async
+ break
+ } else if strings.Contains(lib.Rel(), note_sync) {
+ found = Sync
+ break
+ }
+ }
+
+ if found != expected {
+ t.Errorf("Wrong Memtag note in target %q: found %q, expected %q", m.Module().(*Module).Name(), found.str(), expected.str())
+ }
+}
+
+var prepareForTestWithMemtagHeap = android.GroupFixturePreparers(
+ android.FixtureModifyMockFS(func(fs android.MockFS) {
+ templateBp := `
+ cc_test {
+ name: "%[1]s_test",
+ gtest: false,
+ }
+
+ cc_test {
+ name: "%[1]s_test_false",
+ gtest: false,
+ sanitize: { memtag_heap: false },
+ }
+
+ cc_test {
+ name: "%[1]s_test_true",
+ gtest: false,
+ sanitize: { memtag_heap: true },
+ }
+
+ cc_test {
+ name: "%[1]s_test_true_nodiag",
+ gtest: false,
+ sanitize: { memtag_heap: true, diag: { memtag_heap: false } },
+ }
+
+ cc_test {
+ name: "%[1]s_test_true_diag",
+ gtest: false,
+ sanitize: { memtag_heap: true, diag: { memtag_heap: true } },
+ }
+
+ cc_binary {
+ name: "%[1]s_binary",
+ }
+
+ cc_binary {
+ name: "%[1]s_binary_false",
+ sanitize: { memtag_heap: false },
+ }
+
+ cc_binary {
+ name: "%[1]s_binary_true",
+ sanitize: { memtag_heap: true },
+ }
+
+ cc_binary {
+ name: "%[1]s_binary_true_nodiag",
+ sanitize: { memtag_heap: true, diag: { memtag_heap: false } },
+ }
+
+ cc_binary {
+ name: "%[1]s_binary_true_diag",
+ sanitize: { memtag_heap: true, diag: { memtag_heap: true } },
+ }
+ `
+ subdirDefaultBp := fmt.Sprintf(templateBp, "default")
+ subdirExcludeBp := fmt.Sprintf(templateBp, "exclude")
+ subdirSyncBp := fmt.Sprintf(templateBp, "sync")
+ subdirAsyncBp := fmt.Sprintf(templateBp, "async")
+
+ fs.Merge(android.MockFS{
+ "subdir_default/Android.bp": []byte(subdirDefaultBp),
+ "subdir_exclude/Android.bp": []byte(subdirExcludeBp),
+ "subdir_sync/Android.bp": []byte(subdirSyncBp),
+ "subdir_async/Android.bp": []byte(subdirAsyncBp),
+ })
+ }),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
+ // "subdir_exclude" is covered by both include and exclude paths. Exclude wins.
+ variables.MemtagHeapSyncIncludePaths = []string{"subdir_sync", "subdir_exclude"}
+ variables.MemtagHeapAsyncIncludePaths = []string{"subdir_async", "subdir_exclude"}
+ }),
+)
+
+func TestSanitizeMemtagHeap(t *testing.T) {
+ variant := "android_arm64_armv8-a"
+
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ prepareForTestWithMemtagHeap,
+ ).RunTest(t)
+ ctx := result.TestContext
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
+}
+
+func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) {
+ variant := "android_arm64_armv8-a"
+
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ prepareForTestWithMemtagHeap,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.SanitizeDevice = []string{"memtag_heap"}
+ }),
+ ).RunTest(t)
+ ctx := result.TestContext
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
+}
+
+func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) {
+ variant := "android_arm64_armv8-a"
+
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ prepareForTestWithMemtagHeap,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.SanitizeDevice = []string{"memtag_heap"}
+ variables.SanitizeDeviceDiag = []string{"memtag_heap"}
+ }),
+ ).RunTest(t)
+ ctx := result.TestContext
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
+}
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/strip.go b/cc/strip.go
index b1f34bb..c60e135 100644
--- a/cc/strip.go
+++ b/cc/strip.go
@@ -59,6 +59,7 @@
return !forceDisable && (forceEnable || defaultEnable)
}
+// Keep this consistent with //build/bazel/rules/stripped_shared_library.bzl.
func (stripper *Stripper) strip(actx android.ModuleContext, in android.Path, out android.ModuleOutPath,
flags StripFlags, isStaticLib bool) {
if actx.Darwin() {
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 fb9dc9c..80cc0ef 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -497,7 +497,7 @@
}
cc_genrule {
- name: "host_bionic_linker_flags",
+ name: "host_bionic_linker_script",
host_supported: true,
device_supported: false,
target: {
@@ -508,7 +508,7 @@
enabled: true,
},
},
- out: ["linker.flags"],
+ out: ["linker.script"],
}
cc_defaults {
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/dexpreopt/OWNERS b/dexpreopt/OWNERS
index 166472f..5a2a198 100644
--- a/dexpreopt/OWNERS
+++ b/dexpreopt/OWNERS
@@ -1 +1 @@
-per-file * = ngeoffray@google.com,calin@google.com,mathieuc@google.com
+per-file * = ngeoffray@google.com,calin@google.com,skvadrik@google.com
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/genrule/genrule.go b/genrule/genrule.go
index 77dae75..8372a64 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -211,6 +211,22 @@
return g.outputDeps
}
+func (g *Module) OutputFiles(tag string) (android.Paths, error) {
+ if tag == "" {
+ return append(android.Paths{}, g.outputFiles...), nil
+ }
+ // otherwise, tag should match one of outputs
+ for _, outputFile := range g.outputFiles {
+ if outputFile.Rel() == tag {
+ return android.Paths{outputFile}, nil
+ }
+ }
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+}
+
+var _ android.SourceFileProducer = (*Module)(nil)
+var _ android.OutputFileProducer = (*Module)(nil)
+
func toolDepsMutator(ctx android.BottomUpMutatorContext) {
if g, ok := ctx.Module().(*Module); ok {
for _, tool := range g.properties.Tools {
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 3ce4f85..714d2f8 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -31,12 +31,12 @@
var prepareForGenRuleTest = android.GroupFixturePreparers(
android.PrepareForTestWithArchMutator,
android.PrepareForTestWithDefaults,
-
android.PrepareForTestWithFilegroup,
PrepareForTestWithGenRuleBuildComponents,
android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
ctx.RegisterModuleType("tool", toolFactory)
ctx.RegisterModuleType("output", outputProducerFactory)
+ ctx.RegisterModuleType("use_source", useSourceFactory)
}),
android.FixtureMergeMockFs(android.MockFS{
"tool": nil,
@@ -684,6 +684,42 @@
}
}
+func TestGenruleOutputFiles(t *testing.T) {
+ bp := `
+ genrule {
+ name: "gen",
+ out: ["foo", "sub/bar"],
+ cmd: "echo foo > $(location foo) && echo bar > $(location sub/bar)",
+ }
+ use_source {
+ name: "gen_foo",
+ srcs: [":gen{foo}"],
+ }
+ use_source {
+ name: "gen_bar",
+ srcs: [":gen{sub/bar}"],
+ }
+ use_source {
+ name: "gen_all",
+ srcs: [":gen"],
+ }
+ `
+
+ result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
+ android.AssertPathsRelativeToTopEquals(t,
+ "genrule.tag with output",
+ []string{"out/soong/.intermediates/gen/gen/foo"},
+ result.ModuleForTests("gen_foo", "").Module().(*useSource).srcs)
+ android.AssertPathsRelativeToTopEquals(t,
+ "genrule.tag with output in subdir",
+ []string{"out/soong/.intermediates/gen/gen/sub/bar"},
+ result.ModuleForTests("gen_bar", "").Module().(*useSource).srcs)
+ android.AssertPathsRelativeToTopEquals(t,
+ "genrule.tag with all",
+ []string{"out/soong/.intermediates/gen/gen/foo", "out/soong/.intermediates/gen/gen/sub/bar"},
+ result.ModuleForTests("gen_all", "").Module().(*useSource).srcs)
+}
+
func TestGenruleWithBazel(t *testing.T) {
bp := `
genrule {
@@ -750,3 +786,22 @@
}
var _ android.OutputFileProducer = (*testOutputProducer)(nil)
+
+type useSource struct {
+ android.ModuleBase
+ props struct {
+ Srcs []string `android:"path"`
+ }
+ srcs android.Paths
+}
+
+func (s *useSource) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ s.srcs = android.PathsForModuleSrc(ctx, s.props.Srcs)
+}
+
+func useSourceFactory() android.Module {
+ module := &useSource{}
+ module.AddProperties(&module.props)
+ android.InitAndroidModule(module)
+ return module
+}
diff --git a/java/Android.bp b/java/Android.bp
index 680f3a1..e5b8f96 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",
@@ -92,6 +93,7 @@
"plugin_test.go",
"rro_test.go",
"sdk_test.go",
+ "sdk_library_test.go",
"system_modules_test.go",
"systemserver_classpath_fragment_test.go",
],
diff --git a/java/OWNERS b/java/OWNERS
index 16ef4d8..5242712 100644
--- a/java/OWNERS
+++ b/java/OWNERS
@@ -1 +1 @@
-per-file dexpreopt*.go = ngeoffray@google.com,calin@google.com,mathieuc@google.com
+per-file dexpreopt*.go = ngeoffray@google.com,calin@google.com,skvadrik@google.com
diff --git a/java/app_import.go b/java/app_import.go
index 6fe6204..5a87b07 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -61,7 +61,7 @@
type AndroidAppImportProperties struct {
// A prebuilt apk to import
- Apk *string
+ Apk *string `android:"path"`
// The name of a certificate in the default certificate directory or an android_app_certificate
// module name in the form ":module". Should be empty if presigned or default_dev_cert is set.
diff --git a/java/base.go b/java/base.go
index 440b004..77f6fc6 100644
--- a/java/base.go
+++ b/java/base.go
@@ -155,6 +155,13 @@
// List of java_plugin modules that provide extra errorprone checks.
Extra_check_modules []string
+
+ // 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
}
Proto struct {
@@ -253,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.
@@ -701,7 +708,8 @@
// javaVersion flag.
flags.javaVersion = getJavaVersion(ctx, String(j.properties.Java_version), android.SdkContext(j))
- if ctx.Config().RunErrorProne() {
+ 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?")
}
@@ -972,14 +980,23 @@
}
if len(uniqueSrcFiles) > 0 || len(srcJars) > 0 {
var extraJarDeps android.Paths
- if ctx.Config().RunErrorProne() {
- // If error-prone is enabled, add an additional rule to compile the java files into
- // a separate set of classes (so that they don't overwrite the normal ones and require
- // a rebuild when error-prone is turned off).
- // TODO(ccross): Once we always compile with javac9 we may be able to conditionally
- // enable error-prone without affecting the output class files.
+ if Bool(j.properties.Errorprone.Enabled) {
+ // If error-prone is enabled, enable errorprone flags on the regular
+ // build.
+ flags = enableErrorproneFlags(flags)
+ } 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
+ // rebuilt every time the state of the RUN_ERROR_PRONE variable changes.
+ // We also don't want to run this if errorprone is enabled by default for
+ // this module, or else we could have duplicated errorprone messages.
+ errorproneFlags := enableErrorproneFlags(flags)
errorprone := android.PathForModuleOut(ctx, "errorprone", jarName)
- RunErrorProne(ctx, errorprone, uniqueSrcFiles, srcJars, flags)
+
+ transformJavaToClasses(ctx, errorprone, -1, uniqueSrcFiles, srcJars, errorproneFlags, nil,
+ "errorprone", "errorprone")
+
extraJarDeps = append(extraJarDeps, errorprone)
}
@@ -1275,6 +1292,7 @@
j.linter.minSdkVersion = lintSDKVersionString(j.MinSdkVersion(ctx))
j.linter.targetSdkVersion = lintSDKVersionString(j.TargetSdkVersion(ctx))
j.linter.compileSdkVersion = lintSDKVersionString(j.SdkVersion(ctx))
+ j.linter.compileSdkKind = j.SdkVersion(ctx).Kind
j.linter.javaLanguageLevel = flags.javaVersion.String()
j.linter.kotlinLanguageLevel = "1.3"
if !apexInfo.IsForPlatform() && ctx.Config().UnbundledBuildApps() {
@@ -1303,6 +1321,21 @@
j.outputFile = outputFile.WithoutRel()
}
+// Returns a copy of the supplied flags, but with all the errorprone-related
+// fields copied to the regular build's fields.
+func enableErrorproneFlags(flags javaBuilderFlags) javaBuilderFlags {
+ flags.processorPath = append(flags.errorProneProcessorPath, flags.processorPath...)
+
+ if len(flags.errorProneExtraJavacFlags) > 0 {
+ if len(flags.javacFlags) > 0 {
+ flags.javacFlags += " " + flags.errorProneExtraJavacFlags
+ } else {
+ flags.javacFlags = flags.errorProneExtraJavacFlags
+ }
+ }
+ return flags
+}
+
func (j *Module) compileJavaClasses(ctx android.ModuleContext, jarName string, idx int,
srcFiles, srcJars android.Paths, flags javaBuilderFlags, extraJarDeps android.Paths) android.WritablePath {
@@ -1481,12 +1514,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..4108770 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
@@ -225,13 +227,13 @@
Core_platform_api BootclasspathNestedAPIProperties
}
-// sdkKindToStubLibs calculates the stub library modules for each relevant android.SdkKind from the
+// apiScopeToStubLibs calculates the stub library modules for each relevant *HiddenAPIScope from the
// Stub_libs properties.
-func (p BootclasspathAPIProperties) sdkKindToStubLibs() map[android.SdkKind][]string {
- m := map[android.SdkKind][]string{}
- for _, kind := range []android.SdkKind{android.SdkPublic, android.SdkSystem, android.SdkTest} {
- m[kind] = p.Api.Stub_libs
+func (p BootclasspathAPIProperties) apiScopeToStubLibs() map[*HiddenAPIScope][]string {
+ m := map[*HiddenAPIScope][]string{}
+ for _, apiScope := range hiddenAPISdkLibrarySupportedScopes {
+ m[apiScope] = p.Api.Stub_libs
}
- m[android.SdkCorePlatform] = p.Core_platform_api.Stub_libs
+ m[CorePlatformHiddenAPIScope] = p.Core_platform_api.Stub_libs
return m
}
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 1c7ad78..515dd89 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
@@ -117,8 +121,18 @@
BootclasspathFragmentCoverageAffectedProperties
Coverage BootclasspathFragmentCoverageAffectedProperties
+ // Hidden API related properties.
Hidden_api HiddenAPIFlagFileProperties
+ // The list of additional stub libraries which this fragment's contents use but which are not
+ // provided by another bootclasspath_fragment.
+ //
+ // Note, "android-non-updatable" is treated specially. While no such module exists it is treated
+ // as if it was a java_sdk_library. So, when public API stubs are needed then it will be replaced
+ // with "android-non-updatable.stubs", with "androidn-non-updatable.system.stubs" when the system
+ // stubs are needed and so on.
+ Additional_stubs []string
+
// Properties that allow a fragment to depend on other fragments. This is needed for hidden API
// processing as it needs access to all the classes used by a fragment including those provided
// by other fragments.
@@ -132,19 +146,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 []hiddenAPIModule, 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 +262,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 +314,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 +328,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 +336,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
@@ -362,7 +388,16 @@
func (b *BootclasspathFragmentModule) DepsMutator(ctx android.BottomUpMutatorContext) {
// Add dependencies onto all the modules that provide the API stubs for classes on this
// bootclasspath fragment.
- hiddenAPIAddStubLibDependencies(ctx, b.properties.sdkKindToStubLibs())
+ hiddenAPIAddStubLibDependencies(ctx, b.properties.apiScopeToStubLibs())
+
+ for _, additionalStubModule := range b.properties.Additional_stubs {
+ for _, apiScope := range hiddenAPISdkLibrarySupportedScopes {
+ // Add a dependency onto a possibly scope specific stub library.
+ scopeSpecificDependency := apiScope.scopeSpecificStubModule(ctx, additionalStubModule)
+ tag := hiddenAPIStubsDependencyTag{apiScope: apiScope, fromAdditionalDependency: true}
+ ctx.AddVariationDependencies(nil, tag, scopeSpecificDependency)
+ }
+ }
if SkipDexpreoptBootJars(ctx) {
return
@@ -389,6 +424,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 +438,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 extractBootDexJarsFromHiddenAPIModules 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 +578,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
@@ -561,12 +591,16 @@
// TODO(b/179354495): Stop hidden API processing being conditional once all bootclasspath_fragment
// modules have been updated to support it.
if input.canPerformHiddenAPIProcessing(ctx, b.properties) {
- // Get the content modules that contribute to the hidden API processing.
- hiddenAPIModules := gatherHiddenAPIModuleFromContents(ctx, contents)
-
// 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, hiddenAPIModules, 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,14 +614,12 @@
// Other bootclasspath_fragments that depend on this need the transitive set of stub dex jars
// from this to resolve any references from their code to classes provided by this fragment
// and the fragments this depends upon.
- TransitiveStubDexJarsByKind: input.transitiveStubDexJarsByKind(),
+ TransitiveStubDexJarsByScope: input.transitiveStubDexJarsByScope(),
}
- 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)
@@ -595,10 +627,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)
@@ -612,18 +658,42 @@
// Populate with flag file paths from the properties.
input.extractFlagFilesFromProperties(ctx, &b.properties.Hidden_api)
- // Store the stub dex jars from this module's fragment dependencies.
- input.DependencyStubDexJarsByKind = dependencyHiddenApiInfo.TransitiveStubDexJarsByKind
+ // Add the stub dex jars from this module's fragment dependencies.
+ input.DependencyStubDexJarsByScope.addStubDexJarsByModule(dependencyHiddenApiInfo.TransitiveStubDexJarsByScope)
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 []hiddenAPIModule, 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
@@ -647,9 +717,6 @@
return false
}
- // Copy the dex jars of this fragment's content modules to their predefined locations.
- copyBootJarsToPredefinedLocations(ctx, contents, imageConfig.modules, imageConfig.dexPaths)
-
// Build a profile for the image config and then use that to build the boot image.
profile := bootImageProfileRule(ctx, imageConfig)
buildBootImage(ctx, imageConfig, profile)
@@ -657,6 +724,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
}
@@ -695,6 +768,9 @@
Stub_libs []string
Core_platform_stub_libs []string
+ // Fragment properties
+ Fragments []ApexVariantReference
+
// Flag files by *hiddenAPIFlagFileCategory
Flag_files_by_category FlagFilesByCategory
@@ -735,6 +811,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) {
@@ -757,13 +836,16 @@
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"
// Copy manually curated flag files specified on the bootclasspath_fragment.
if b.Flag_files_by_category != nil {
- for _, category := range hiddenAPIFlagFileCategories {
+ for _, category := range HiddenAPIFlagFileCategories {
paths := b.Flag_files_by_category[category]
if len(paths) > 0 {
dests := []string{}
@@ -772,7 +854,7 @@
builder.CopyToSnapshot(p, dest)
dests = append(dests, dest)
}
- hiddenAPISet.AddProperty(category.propertyName, dests)
+ hiddenAPISet.AddProperty(category.PropertyName, dests)
}
}
}
@@ -838,9 +920,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 []hiddenAPIModule, _ 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.
@@ -849,19 +930,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/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index fba7d1a..b469886 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -245,17 +245,34 @@
otherPublicStubsJar := "out/soong/.intermediates/myothersdklibrary.stubs/android_common/dex/myothersdklibrary.stubs.jar"
// Check that SdkPublic uses public stubs for all sdk libraries.
- android.AssertPathsRelativeToTopEquals(t, "public dex stubs jar", []string{otherPublicStubsJar, publicStubsJar, stubsJar}, info.TransitiveStubDexJarsByKind[android.SdkPublic])
+ android.AssertPathsRelativeToTopEquals(t, "public dex stubs jar", []string{otherPublicStubsJar, publicStubsJar, stubsJar}, info.TransitiveStubDexJarsByScope.StubDexJarsForScope(PublicHiddenAPIScope))
// Check that SdkSystem uses system stubs for mysdklibrary and public stubs for myothersdklibrary
// as it does not provide system stubs.
- android.AssertPathsRelativeToTopEquals(t, "system dex stubs jar", []string{otherPublicStubsJar, systemStubsJar, stubsJar}, info.TransitiveStubDexJarsByKind[android.SdkSystem])
+ android.AssertPathsRelativeToTopEquals(t, "system dex stubs jar", []string{otherPublicStubsJar, systemStubsJar, stubsJar}, info.TransitiveStubDexJarsByScope.StubDexJarsForScope(SystemHiddenAPIScope))
// Check that SdkTest also uses system stubs for mysdklibrary as it does not provide test stubs
// and public stubs for myothersdklibrary as it does not provide test stubs either.
- android.AssertPathsRelativeToTopEquals(t, "test dex stubs jar", []string{otherPublicStubsJar, systemStubsJar, stubsJar}, info.TransitiveStubDexJarsByKind[android.SdkTest])
+ android.AssertPathsRelativeToTopEquals(t, "test dex stubs jar", []string{otherPublicStubsJar, systemStubsJar, stubsJar}, info.TransitiveStubDexJarsByScope.StubDexJarsForScope(TestHiddenAPIScope))
// Check that SdkCorePlatform uses public stubs from the mycoreplatform library.
corePlatformStubsJar := "out/soong/.intermediates/mycoreplatform.stubs/android_common/dex/mycoreplatform.stubs.jar"
- android.AssertPathsRelativeToTopEquals(t, "core platform dex stubs jar", []string{corePlatformStubsJar}, info.TransitiveStubDexJarsByKind[android.SdkCorePlatform])
+ android.AssertPathsRelativeToTopEquals(t, "core platform dex stubs jar", []string{corePlatformStubsJar}, info.TransitiveStubDexJarsByScope.StubDexJarsForScope(CorePlatformHiddenAPIScope))
+
+ // Check the widest stubs.. The list contains the widest stub dex jar provided by each module.
+ expectedWidestPaths := []string{
+ // mycoreplatform's widest API is core platform.
+ corePlatformStubsJar,
+
+ // myothersdklibrary's widest API is public.
+ otherPublicStubsJar,
+
+ // sdklibrary's widest API is system.
+ systemStubsJar,
+
+ // mystublib's only provides one API and so it must be the widest.
+ stubsJar,
+ }
+
+ android.AssertPathsRelativeToTopEquals(t, "widest dex stubs jar", expectedWidestPaths, info.TransitiveStubDexJarsByScope.StubDexJarsForWidestAPIScope())
}
diff --git a/java/builder.go b/java/builder.go
index cde8731..ea011b8 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -279,23 +279,6 @@
transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, flags, deps, "javac", desc)
}
-func RunErrorProne(ctx android.ModuleContext, outputFile android.WritablePath,
- srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
-
- flags.processorPath = append(flags.errorProneProcessorPath, flags.processorPath...)
-
- if len(flags.errorProneExtraJavacFlags) > 0 {
- if len(flags.javacFlags) > 0 {
- flags.javacFlags += " " + flags.errorProneExtraJavacFlags
- } else {
- flags.javacFlags = flags.errorProneExtraJavacFlags
- }
- }
-
- transformJavaToClasses(ctx, outputFile, -1, srcFiles, srcJars, flags, nil,
- "errorprone", "errorprone")
-}
-
// Emits the rule to generate Xref input file (.kzip file) for the given set of source files and source jars
// to compile with given set of builder flags, etc.
func emitXrefRule(ctx android.ModuleContext, xrefFile android.WritablePath, idx int,
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 e1a3650..03769fa 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -15,7 +15,6 @@
package java
import (
- "fmt"
"path/filepath"
"sort"
"strings"
@@ -254,6 +253,9 @@
dexPaths android.WritablePaths // for this image
dexPathsDeps android.WritablePaths // for the dependency images and in this image
+ // Map from module name (without prebuilt_ prefix) to the predefined build path.
+ dexPathsByModule map[string]android.WritablePath
+
// File path to a zip archive with all image files (or nil, if not needed).
zip android.WritablePath
@@ -276,13 +278,24 @@
dexLocationsDeps []string // for the dependency images and in this image
// Paths to image files.
- imagePathOnHost android.OutputPath // first image file path on host
- imagePathOnDevice string // first image file path on device
- imagesDeps android.OutputPaths // all files
+ imagePathOnHost android.OutputPath // first image file path on host
+ imagePathOnDevice string // first image file path on device
- // Only for extensions, paths to the primary boot images.
+ // All the files that constitute this image variant, i.e. .art, .oat and .vdex files.
+ imagesDeps android.OutputPaths
+
+ // The path to the primary image variant's imagePathOnHost field, where primary image variant
+ // means the image variant that this extends.
+ //
+ // This is only set for a variant of an image that extends another image.
primaryImages android.OutputPath
+ // The paths to the primary image variant's imagesDeps field, where primary image variant
+ // means the image variant that this extends.
+ //
+ // This is only set for a variant of an image that extends another image.
+ primaryImagesDeps android.Paths
+
// Rules which should be used in make to install the outputs.
installs android.RuleBuilderInstalls
vdexInstalls android.RuleBuilderInstalls
@@ -345,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
@@ -450,59 +476,33 @@
return true
}
-// copyBootJarsToPredefinedLocations generates commands that will copy boot jars to
-// predefined paths in the global config.
-func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, bootModules []android.Module, bootjars android.ConfiguredJarList, jarPathsPredefined android.WritablePaths) {
- jarPaths := make(android.Paths, bootjars.Len())
- for i, module := range bootModules {
- if module != nil {
- bootDexJar := module.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath()
- jarPaths[i] = bootDexJar
+// copyBootJarsToPredefinedLocations generates commands that will copy boot jars to predefined
+// paths in the global config.
+func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, srcBootDexJarsByModule bootDexJarByModule, dstBootJarsByModule map[string]android.WritablePath) {
+ // Create the super set of module names.
+ names := []string{}
+ names = append(names, android.SortedStringKeys(srcBootDexJarsByModule)...)
+ names = append(names, android.SortedStringKeys(dstBootJarsByModule)...)
+ names = android.SortedUniqueStrings(names)
+ for _, name := range names {
+ src := srcBootDexJarsByModule[name]
+ dst := dstBootJarsByModule[name]
- name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(module))
- if bootjars.Jar(i) != name {
- ctx.ModuleErrorf("expected module %s at position %d but found %s", bootjars.Jar(i), i, name)
- }
- }
- }
-
- // The paths to bootclasspath DEX files need to be known at module GenerateAndroidBuildAction
- // time, before the boot images are built (these paths are used in dexpreopt rule generation for
- // Java libraries and apps). Generate rules that copy bootclasspath DEX jars to the predefined
- // paths.
- for i := range jarPaths {
- input := jarPaths[i]
- output := jarPathsPredefined[i]
- module := bootjars.Jar(i)
- if input == nil {
- if ctx.Config().AllowMissingDependencies() {
- apex := bootjars.Apex(i)
-
- // 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: output,
- Args: map[string]string{
- "error": fmt.Sprintf("missing dependencies: dex jar for %s:%s", module, apex),
- },
- })
- } else {
- ctx.ModuleErrorf("failed to find a dex jar path for module '%s'"+
- ", note that some jars may be filtered out by module constraints", module)
- }
-
+ if src == nil {
+ ctx.ModuleErrorf("module %s does not provide a dex boot jar", name)
+ } else if dst == nil {
+ ctx.ModuleErrorf("module %s is not part of the boot configuration", name)
} else {
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
- Input: input,
- Output: output,
+ Input: src,
+ Output: dst,
})
}
}
}
-// 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 {
@@ -588,7 +588,15 @@
cmd.
Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", image.dexLocationsDeps, ":").
- FlagWithArg("--boot-image=", dexpreopt.PathToLocation(artImage, arch)).Implicit(artImage)
+ // Add the path to the first file in the boot image with the arch specific directory removed,
+ // dex2oat will reconstruct the path to the actual file when it needs it. As the actual path
+ // to the file cannot be passed to the command make sure to add the actual path as an Implicit
+ // dependency to ensure that it is built before the command runs.
+ FlagWithArg("--boot-image=", dexpreopt.PathToLocation(artImage, arch)).Implicit(artImage).
+ // Similarly, the dex2oat tool will automatically find the paths to other files in the base
+ // boot image so make sure to add them as implicit dependencies to ensure that they are built
+ // before this command is run.
+ Implicits(image.primaryImagesDeps)
} else {
// It is a primary image, so it needs a base address.
cmd.FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress())
@@ -876,8 +884,9 @@
ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+sfx, variant.installs.String())
ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+sfx, variant.unstrippedInstalls.String())
}
- imageLocationsOnHost, _ := current.getAnyAndroidVariant().imageLocations()
- ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_"+current.name, strings.Join(imageLocationsOnHost, ":"))
+ imageLocationsOnHost, imageLocationsOnDevice := current.getAnyAndroidVariant().imageLocations()
+ ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_HOST"+current.name, strings.Join(imageLocationsOnHost, ":"))
+ ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICE"+current.name, strings.Join(imageLocationsOnDevice, ":"))
ctx.Strict("DEXPREOPT_IMAGE_ZIP_"+current.name, current.zip.String())
}
ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(imageNames, " "))
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 39a3e11..b13955f 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -100,6 +100,7 @@
// TODO(b/143682396): use module dependencies instead
inputDir := deviceDir.Join(ctx, "dex_"+c.name+"jars_input")
c.dexPaths = c.modules.BuildPaths(ctx, inputDir)
+ c.dexPathsByModule = c.modules.BuildPathsByModule(ctx, inputDir)
c.dexPathsDeps = c.dexPaths
// Create target-specific variants.
@@ -125,6 +126,7 @@
frameworkCfg.dexPathsDeps = append(artCfg.dexPathsDeps, frameworkCfg.dexPathsDeps...)
for i := range targets {
frameworkCfg.variants[i].primaryImages = artCfg.variants[i].imagePathOnHost
+ frameworkCfg.variants[i].primaryImagesDeps = artCfg.variants[i].imagesDeps.Paths()
frameworkCfg.variants[i].dexLocationsDeps = append(artCfg.variants[i].dexLocations, frameworkCfg.variants[i].dexLocationsDeps...)
}
@@ -152,6 +154,9 @@
// later on a singleton adds commands to copy actual jars to the predefined paths.
dexPaths android.WritablePaths
+ // Map from module name (without prebuilt_ prefix) to the predefined build path.
+ dexPathsByModule map[string]android.WritablePath
+
// A list of dex locations (a.k.a. on-device paths) to the boot jars.
dexLocations []string
}
@@ -165,10 +170,11 @@
dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "updatable_bootjars")
dexPaths := updatableBootJars.BuildPaths(ctx, dir)
+ dexPathsByModuleName := updatableBootJars.BuildPathsByModule(ctx, dir)
dexLocations := updatableBootJars.DevicePaths(ctx.Config(), android.Android)
- return updatableBootConfig{updatableBootJars, dexPaths, dexLocations}
+ return updatableBootConfig{updatableBootJars, dexPaths, dexPathsByModuleName, dexLocations}
}).(updatableBootConfig)
}
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index e9693c6..f901434 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -219,7 +219,6 @@
BuiltTool("merge_csv").
Flag("--zip_input").
Flag("--key_field signature").
- FlagWithArg("--header=", "signature,file,startline,startcol,endline,endcol,properties").
FlagWithOutput("--output=", indexCSV).
Inputs(classesJars)
rule.Build(desc, desc)
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index f2649d3..0895951 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -25,9 +25,160 @@
// Contains support for processing hiddenAPI in a modular fashion.
+// HiddenAPIScope encapsulates all the information that the hidden API processing needs about API
+// scopes, i.e. what is called android.SdkKind and apiScope. It does not just use those as they do
+// not provide the information needed by hidden API processing.
+type HiddenAPIScope struct {
+ // The name of the scope, used for debug purposes.
+ name string
+
+ // The corresponding android.SdkKind, used for retrieving paths from java_sdk_library* modules.
+ sdkKind android.SdkKind
+
+ // The option needed to passed to "hiddenapi list".
+ hiddenAPIListOption string
+
+ // The name sof the source stub library modules that contain the API provided by the platform,
+ // i.e. by modules that are not in an APEX.
+ nonUpdatableSourceModule string
+
+ // The names of the prebuilt stub library modules that contain the API provided by the platform,
+ // i.e. by modules that are not in an APEX.
+ nonUpdatablePrebuiltModule string
+}
+
+// initHiddenAPIScope initializes the scope.
+func initHiddenAPIScope(apiScope *HiddenAPIScope) *HiddenAPIScope {
+ sdkKind := apiScope.sdkKind
+ // The platform does not provide a core platform API.
+ if sdkKind != android.SdkCorePlatform {
+ kindAsString := sdkKind.String()
+ var insert string
+ if sdkKind == android.SdkPublic {
+ insert = ""
+ } else {
+ insert = "." + strings.ReplaceAll(kindAsString, "-", "_")
+ }
+
+ nonUpdatableModule := "android-non-updatable"
+
+ // Construct the name of the android-non-updatable source module for this scope.
+ apiScope.nonUpdatableSourceModule = fmt.Sprintf("%s.stubs%s", nonUpdatableModule, insert)
+
+ prebuiltModuleName := func(name string, kind string) string {
+ return fmt.Sprintf("sdk_%s_current_%s", kind, name)
+ }
+
+ // Construct the name of the android-non-updatable prebuilt module for this scope.
+ apiScope.nonUpdatablePrebuiltModule = prebuiltModuleName(nonUpdatableModule, kindAsString)
+ }
+
+ return apiScope
+}
+
+// android-non-updatable takes the name of a module and returns a possibly scope specific name of
+// the module.
+func (l *HiddenAPIScope) scopeSpecificStubModule(ctx android.BaseModuleContext, name string) string {
+ // The android-non-updatable is not a java_sdk_library but there are separate stub libraries for
+ // each scope.
+ // TODO(b/192067200): Remove special handling of android-non-updatable.
+ if name == "android-non-updatable" {
+ if ctx.Config().AlwaysUsePrebuiltSdks() {
+ return l.nonUpdatablePrebuiltModule
+ } else {
+ return l.nonUpdatableSourceModule
+ }
+ } else {
+ // Assume that the module is either a java_sdk_library (or equivalent) and so will provide
+ // separate stub jars for each scope or is a java_library (or equivalent) in which case it will
+ // have the same stub jar for each scope.
+ return name
+ }
+}
+
+func (l *HiddenAPIScope) String() string {
+ return fmt.Sprintf("HiddenAPIScope{%s}", l.name)
+}
+
+var (
+ PublicHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{
+ name: "public",
+ sdkKind: android.SdkPublic,
+ hiddenAPIListOption: "--public-stub-classpath",
+ })
+ SystemHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{
+ name: "system",
+ sdkKind: android.SdkSystem,
+ hiddenAPIListOption: "--system-stub-classpath",
+ })
+ TestHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{
+ name: "test",
+ sdkKind: android.SdkTest,
+ hiddenAPIListOption: "--test-stub-classpath",
+ })
+ ModuleLibHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{
+ name: "module-lib",
+ sdkKind: android.SdkModule,
+ })
+ CorePlatformHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{
+ name: "core-platform",
+ sdkKind: android.SdkCorePlatform,
+ hiddenAPIListOption: "--core-platform-stub-classpath",
+ })
+
+ // hiddenAPIRelevantSdkKinds lists all the android.SdkKind instances that are needed by the hidden
+ // API processing.
+ //
+ // These are roughly in order from narrowest API surface to widest. Widest means the API stubs
+ // with the biggest API surface, e.g. test is wider than system is wider than public.
+ //
+ // Core platform is considered wider than system/module-lib because those modules that provide
+ // core platform APIs either do not have any system/module-lib APIs at all, or if they do it is
+ // because the core platform API is being converted to system/module-lib APIs. In either case the
+ // system/module-lib APIs are subsets of the core platform API.
+ //
+ // This is not strictly in order from narrowest to widest as the Test API is wider than system but
+ // is neither wider or narrower than the module-lib or core platform APIs. However, this works
+ // well enough at the moment.
+ // TODO(b/191644675): Correctly reflect the sub/superset relationships between APIs.
+ hiddenAPIScopes = []*HiddenAPIScope{
+ PublicHiddenAPIScope,
+ SystemHiddenAPIScope,
+ TestHiddenAPIScope,
+ ModuleLibHiddenAPIScope,
+ CorePlatformHiddenAPIScope,
+ }
+
+ // The HiddenAPIScope instances that are supported by a java_sdk_library.
+ //
+ // CorePlatformHiddenAPIScope is not used as the java_sdk_library does not have special support
+ // for core_platform API, instead it is implemented as a customized form of PublicHiddenAPIScope.
+ hiddenAPISdkLibrarySupportedScopes = []*HiddenAPIScope{
+ PublicHiddenAPIScope,
+ SystemHiddenAPIScope,
+ TestHiddenAPIScope,
+ ModuleLibHiddenAPIScope,
+ }
+
+ // The HiddenAPIScope instances that are supported by the `hiddenapi list`.
+ hiddenAPIFlagScopes = []*HiddenAPIScope{
+ PublicHiddenAPIScope,
+ SystemHiddenAPIScope,
+ TestHiddenAPIScope,
+ CorePlatformHiddenAPIScope,
+ }
+)
+
type hiddenAPIStubsDependencyTag struct {
blueprint.BaseDependencyTag
- sdkKind android.SdkKind
+
+ // The api scope for which this dependency was added.
+ apiScope *HiddenAPIScope
+
+ // Indicates that the dependency is not for an API provided by the current bootclasspath fragment
+ // but is an additional API provided by a module that is not part of the current bootclasspath
+ // fragment.
+ fromAdditionalDependency bool
}
func (b hiddenAPIStubsDependencyTag) ExcludeFromApexContents() {
@@ -38,6 +189,11 @@
}
func (b hiddenAPIStubsDependencyTag) SdkMemberType(child android.Module) android.SdkMemberType {
+ // Do not add additional dependencies to the sdk.
+ if b.fromAdditionalDependency {
+ return nil
+ }
+
// If the module is a java_sdk_library then treat it as if it was specific in the java_sdk_libs
// property, otherwise treat if it was specified in the java_header_libs property.
if javaSdkLibrarySdkMemberType.IsInstance(child) {
@@ -65,24 +221,9 @@
var _ android.ExcludeFromApexContentsTag = hiddenAPIStubsDependencyTag{}
var _ android.SdkMemberTypeDependencyTag = hiddenAPIStubsDependencyTag{}
-// hiddenAPIRelevantSdkKinds lists all the android.SdkKind instances that are needed by the hidden
-// API processing.
-//
-// These are in order from narrowest API surface to widest. Widest means the API stubs with the
-// biggest API surface, e.g. test is wider than system is wider than public. Core platform is
-// considered wider than test even though it has no relationship with test because the libraries
-// that provide core platform API don't provide test. While the core platform API is being converted
-// to a system API the system API is still a subset of core platform.
-var hiddenAPIRelevantSdkKinds = []android.SdkKind{
- android.SdkPublic,
- android.SdkSystem,
- android.SdkTest,
- android.SdkCorePlatform,
-}
-
// hiddenAPIComputeMonolithicStubLibModules computes the set of module names that provide stubs
// needed to produce the hidden API monolithic stub flags file.
-func hiddenAPIComputeMonolithicStubLibModules(config android.Config) map[android.SdkKind][]string {
+func hiddenAPIComputeMonolithicStubLibModules(config android.Config) map[*HiddenAPIScope][]string {
var publicStubModules []string
var systemStubModules []string
var testStubModules []string
@@ -115,22 +256,22 @@
testStubModules = append(testStubModules, "jacoco-stubs")
}
- m := map[android.SdkKind][]string{}
- m[android.SdkPublic] = publicStubModules
- m[android.SdkSystem] = systemStubModules
- m[android.SdkTest] = testStubModules
- m[android.SdkCorePlatform] = corePlatformStubModules
+ m := map[*HiddenAPIScope][]string{}
+ m[PublicHiddenAPIScope] = publicStubModules
+ m[SystemHiddenAPIScope] = systemStubModules
+ m[TestHiddenAPIScope] = testStubModules
+ m[CorePlatformHiddenAPIScope] = corePlatformStubModules
return m
}
// hiddenAPIAddStubLibDependencies adds dependencies onto the modules specified in
-// sdkKindToStubLibModules. It adds them in a well known order and uses an SdkKind specific tag to
-// identify the source of the dependency.
-func hiddenAPIAddStubLibDependencies(ctx android.BottomUpMutatorContext, sdkKindToStubLibModules map[android.SdkKind][]string) {
+// apiScopeToStubLibModules. It adds them in a well known order and uses a HiddenAPIScope specific
+// tag to identify the source of the dependency.
+func hiddenAPIAddStubLibDependencies(ctx android.BottomUpMutatorContext, apiScopeToStubLibModules map[*HiddenAPIScope][]string) {
module := ctx.Module()
- for _, sdkKind := range hiddenAPIRelevantSdkKinds {
- modules := sdkKindToStubLibModules[sdkKind]
- ctx.AddDependency(module, hiddenAPIStubsDependencyTag{sdkKind: sdkKind}, modules...)
+ for _, apiScope := range hiddenAPIScopes {
+ modules := apiScopeToStubLibModules[apiScope]
+ ctx.AddDependency(module, hiddenAPIStubsDependencyTag{apiScope: apiScope}, modules...)
}
}
@@ -153,33 +294,21 @@
return dexJar
}
-var sdkKindToHiddenapiListOption = map[android.SdkKind]string{
- android.SdkPublic: "public-stub-classpath",
- android.SdkSystem: "system-stub-classpath",
- android.SdkTest: "test-stub-classpath",
- android.SdkCorePlatform: "core-platform-stub-classpath",
-}
-
-// ruleToGenerateHiddenAPIStubFlagsFile creates a rule to create a hidden API stub flags file.
+// buildRuleToGenerateHiddenAPIStubFlagsFile creates a rule to create a hidden API stub flags file.
//
// The rule is initialized but not built so that the caller can modify it and select an appropriate
// name.
-func ruleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, outputPath android.WritablePath, bootDexJars android.Paths, input HiddenAPIFlagInput) *android.RuleBuilder {
+func buildRuleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, name, desc string, outputPath android.WritablePath, bootDexJars android.Paths, input HiddenAPIFlagInput, moduleStubFlagsPaths android.Paths) {
// Singleton rule which applies hiddenapi on all boot class path dex files.
rule := android.NewRuleBuilder(pctx, ctx)
tempPath := tempPathForRestat(ctx, outputPath)
// Find the widest API stubs provided by the fragments on which this depends, if any.
- var dependencyStubDexJars android.Paths
- for i := len(hiddenAPIRelevantSdkKinds) - 1; i >= 0; i-- {
- kind := hiddenAPIRelevantSdkKinds[i]
- stubsForKind := input.DependencyStubDexJarsByKind[kind]
- if len(stubsForKind) != 0 {
- dependencyStubDexJars = stubsForKind
- break
- }
- }
+ dependencyStubDexJars := input.DependencyStubDexJarsByScope.StubDexJarsForWidestAPIScope()
+
+ // Add widest API stubs from the additional dependencies of this, if any.
+ dependencyStubDexJars = append(dependencyStubDexJars, input.AdditionalStubDexJarsByScope.StubDexJarsForWidestAPIScope()...)
command := rule.Command().
Tool(ctx.Config().HostToolPath(ctx, "hiddenapi")).
@@ -187,24 +316,46 @@
FlagForEachInput("--dependency-stub-dex=", dependencyStubDexJars).
FlagForEachInput("--boot-dex=", bootDexJars)
- // Iterate over the sdk kinds in a fixed order.
- for _, sdkKind := range hiddenAPIRelevantSdkKinds {
- // Merge in the stub dex jar paths for this kind from the fragments on which it depends. They
- // will be needed to resolve dependencies from this fragment's stubs to classes in the other
- // fragment's APIs.
- dependencyPaths := input.DependencyStubDexJarsByKind[sdkKind]
- paths := append(dependencyPaths, input.StubDexJarsByKind[sdkKind]...)
+ // If no module stub flags paths are provided then this must be being called for a
+ // bootclasspath_fragment and not the whole platform_bootclasspath.
+ if moduleStubFlagsPaths == nil {
+ // This is being run on a fragment of the bootclasspath.
+ command.Flag("--fragment")
+ }
+
+ // Iterate over the api scopes in a fixed order.
+ for _, apiScope := range hiddenAPIFlagScopes {
+ // Merge in the stub dex jar paths for this api scope from the fragments on which it depends.
+ // They will be needed to resolve dependencies from this fragment's stubs to classes in the
+ // other fragment's APIs.
+ var paths android.Paths
+ paths = append(paths, input.DependencyStubDexJarsByScope.StubDexJarsForScope(apiScope)...)
+ paths = append(paths, input.AdditionalStubDexJarsByScope.StubDexJarsForScope(apiScope)...)
+ paths = append(paths, input.StubDexJarsByScope.StubDexJarsForScope(apiScope)...)
if len(paths) > 0 {
- option := sdkKindToHiddenapiListOption[sdkKind]
- command.FlagWithInputList("--"+option+"=", paths, ":")
+ option := apiScope.hiddenAPIListOption
+ command.FlagWithInputList(option+"=", paths, ":")
}
}
// Add the output path.
command.FlagWithOutput("--out-api-flags=", tempPath)
+ // If there are stub flag files that have been generated by fragments on which this depends then
+ // use them to validate the stub flag file generated by the rules created by this method.
+ if len(moduleStubFlagsPaths) > 0 {
+ validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, moduleStubFlagsPaths)
+
+ // Add the file that indicates that the file generated by this is valid.
+ //
+ // This will cause the validation rule above to be run any time that the output of this rule
+ // changes but the validation will run in parallel with other rules that depend on this file.
+ command.Validation(validFile)
+ }
+
commitChangeForRestat(rule, tempPath, outputPath)
- return rule
+
+ rule.Build(name, desc)
}
// HiddenAPIFlagFileProperties contains paths to the flag files that can be used to augment the
@@ -248,8 +399,8 @@
}
type hiddenAPIFlagFileCategory struct {
- // propertyName is the name of the property for this category.
- propertyName string
+ // PropertyName is the name of the property for this category.
+ PropertyName string
// propertyValueReader retrieves the value of the property for this category from the set of
// properties.
@@ -262,12 +413,12 @@
// The flag file category for removed members of the API.
//
-// This is extracted from hiddenAPIFlagFileCategories as it is needed to add the dex signatures
+// This is extracted from HiddenAPIFlagFileCategories as it is needed to add the dex signatures
// list of removed API members that are generated automatically from the removed.txt files provided
// by API stubs.
var hiddenAPIRemovedFlagFileCategory = &hiddenAPIFlagFileCategory{
// See HiddenAPIFlagFileProperties.Removed
- propertyName: "removed",
+ PropertyName: "removed",
propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
return properties.Removed
},
@@ -276,10 +427,10 @@
},
}
-var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{
+var HiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{
// See HiddenAPIFlagFileProperties.Unsupported
{
- propertyName: "unsupported",
+ PropertyName: "unsupported",
propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
return properties.Unsupported
},
@@ -290,7 +441,7 @@
hiddenAPIRemovedFlagFileCategory,
// See HiddenAPIFlagFileProperties.Max_target_r_low_priority
{
- propertyName: "max_target_r_low_priority",
+ PropertyName: "max_target_r_low_priority",
propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
return properties.Max_target_r_low_priority
},
@@ -300,7 +451,7 @@
},
// See HiddenAPIFlagFileProperties.Max_target_q
{
- propertyName: "max_target_q",
+ PropertyName: "max_target_q",
propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
return properties.Max_target_q
},
@@ -310,7 +461,7 @@
},
// See HiddenAPIFlagFileProperties.Max_target_p
{
- propertyName: "max_target_p",
+ PropertyName: "max_target_p",
propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
return properties.Max_target_p
},
@@ -320,7 +471,7 @@
},
// See HiddenAPIFlagFileProperties.Max_target_o_low_priority
{
- propertyName: "max_target_o_low_priority",
+ PropertyName: "max_target_o_low_priority",
propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
return properties.Max_target_o_low_priority
},
@@ -330,7 +481,7 @@
},
// See HiddenAPIFlagFileProperties.Blocked
{
- propertyName: "blocked",
+ PropertyName: "blocked",
propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
return properties.Blocked
},
@@ -340,7 +491,7 @@
},
// See HiddenAPIFlagFileProperties.Unsupported_packages
{
- propertyName: "unsupported_packages",
+ PropertyName: "unsupported_packages",
propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
return properties.Unsupported_packages
},
@@ -355,7 +506,7 @@
// append appends the supplied flags files to the corresponding category in this map.
func (s FlagFilesByCategory) append(other FlagFilesByCategory) {
- for _, category := range hiddenAPIFlagFileCategories {
+ for _, category := range HiddenAPIFlagFileCategories {
s[category] = append(s[category], other[category]...)
}
}
@@ -377,8 +528,9 @@
// that category.
FlagFilesByCategory FlagFilesByCategory
- // The paths to the stub dex jars for each of the android.SdkKind in hiddenAPIRelevantSdkKinds.
- TransitiveStubDexJarsByKind StubDexJarsByKind
+ // The paths to the stub dex jars for each of the *HiddenAPIScope in hiddenAPIScopes provided by
+ // this fragment and the fragments on which this depends.
+ TransitiveStubDexJarsByScope StubDexJarsByModule
// The output from the hidden API processing needs to be made available to other modules.
HiddenAPIFlagOutput
@@ -386,8 +538,8 @@
func newHiddenAPIInfo() *HiddenAPIInfo {
info := HiddenAPIInfo{
- FlagFilesByCategory: FlagFilesByCategory{},
- TransitiveStubDexJarsByKind: StubDexJarsByKind{},
+ FlagFilesByCategory: FlagFilesByCategory{},
+ TransitiveStubDexJarsByScope: StubDexJarsByModule{},
}
return &info
}
@@ -398,34 +550,111 @@
for _, fragment := range fragments {
if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) {
info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo)
- i.TransitiveStubDexJarsByKind.append(info.TransitiveStubDexJarsByKind)
+ i.TransitiveStubDexJarsByScope.addStubDexJarsByModule(info.TransitiveStubDexJarsByScope)
}
}
-
- // Dedup and sort paths.
- i.TransitiveStubDexJarsByKind.dedupAndSort()
}
var HiddenAPIInfoProvider = blueprint.NewProvider(HiddenAPIInfo{})
-// StubDexJarsByKind maps an android.SdkKind to the paths to stub dex jars appropriate for that
-// level. See hiddenAPIRelevantSdkKinds for a list of the acceptable android.SdkKind values.
-type StubDexJarsByKind map[android.SdkKind]android.Paths
+// ModuleStubDexJars contains the stub dex jars provided by a single module.
+//
+// It maps a *HiddenAPIScope to the path to stub dex jars appropriate for that scope. See
+// hiddenAPIScopes for a list of the acceptable *HiddenAPIScope values.
+type ModuleStubDexJars map[*HiddenAPIScope]android.Path
-// append appends the supplied kind specific stub dex jar pargs to the corresponding kind in this
+// stubDexJarForWidestAPIScope returns the stub dex jars for the widest API scope provided by this
// map.
-func (s StubDexJarsByKind) append(other StubDexJarsByKind) {
- for _, kind := range hiddenAPIRelevantSdkKinds {
- s[kind] = append(s[kind], other[kind]...)
+//
+// The relative width of APIs is determined by their order in hiddenAPIScopes.
+func (s ModuleStubDexJars) stubDexJarForWidestAPIScope() android.Path {
+ for i := len(hiddenAPIScopes) - 1; i >= 0; i-- {
+ apiScope := hiddenAPIScopes[i]
+ if stubsForAPIScope, ok := s[apiScope]; ok {
+ return stubsForAPIScope
+ }
+ }
+
+ return nil
+}
+
+// StubDexJarsByModule contains the stub dex jars provided by a set of modules.
+//
+// It maps a module name to the path to the stub dex jars provided by that module.
+type StubDexJarsByModule map[string]ModuleStubDexJars
+
+// addStubDexJar adds a stub dex jar path provided by the specified module for the specified scope.
+func (s StubDexJarsByModule) addStubDexJar(ctx android.ModuleContext, module android.Module, scope *HiddenAPIScope, stubDexJar android.Path) {
+ name := android.RemoveOptionalPrebuiltPrefix(module.Name())
+ if name == scope.nonUpdatablePrebuiltModule || name == scope.nonUpdatableSourceModule {
+ // Treat all *android-non-updatable* modules as if they were part of an android-non-updatable
+ // java_sdk_library.
+ // TODO(b/192067200): Remove once android-non-updatable is a java_sdk_library or equivalent.
+ name = "android-non-updatable"
+ } else if name == "legacy.art.module.platform.api" {
+ // Treat legacy.art.module.platform.api as if it was an API scope provided by the
+ // art.module.public.api java_sdk_library which will be the case once the former has been
+ // migrated to a module_lib API.
+ name = "art.module.public.api"
+ } else if name == "legacy.i18n.module.platform.api" {
+ // Treat legacy.i18n.module.platform.api as if it was an API scope provided by the
+ // i18n.module.public.api java_sdk_library which will be the case once the former has been
+ // migrated to a module_lib API.
+ name = "i18n.module.public.api"
+ } else if name == "conscrypt.module.platform.api" {
+ // Treat conscrypt.module.platform.api as if it was an API scope provided by the
+ // conscrypt.module.public.api java_sdk_library which will be the case once the former has been
+ // migrated to a module_lib API.
+ name = "conscrypt.module.public.api"
+ }
+ stubDexJarsByScope := s[name]
+ if stubDexJarsByScope == nil {
+ stubDexJarsByScope = ModuleStubDexJars{}
+ s[name] = stubDexJarsByScope
+ }
+ stubDexJarsByScope[scope] = stubDexJar
+}
+
+// addStubDexJarsByModule adds the stub dex jars in the supplied StubDexJarsByModule to this map.
+func (s StubDexJarsByModule) addStubDexJarsByModule(other StubDexJarsByModule) {
+ for module, stubDexJarsByScope := range other {
+ s[module] = stubDexJarsByScope
}
}
-// dedupAndSort removes duplicates in the stub dex jar paths and sorts them into a consistent and
-// deterministic order.
-func (s StubDexJarsByKind) dedupAndSort() {
- for kind, paths := range s {
- s[kind] = android.SortedUniquePaths(paths)
+// StubDexJarsForWidestAPIScope returns a list of stub dex jars containing the widest API scope
+// provided by each module.
+//
+// The relative width of APIs is determined by their order in hiddenAPIScopes.
+func (s StubDexJarsByModule) StubDexJarsForWidestAPIScope() android.Paths {
+ stubDexJars := android.Paths{}
+ modules := android.SortedStringKeys(s)
+ for _, module := range modules {
+ stubDexJarsByScope := s[module]
+
+ stubDexJars = append(stubDexJars, stubDexJarsByScope.stubDexJarForWidestAPIScope())
}
+
+ return stubDexJars
+}
+
+// StubDexJarsForScope returns a list of stub dex jars containing the stub dex jars provided by each
+// module for the specified scope.
+//
+// If a module does not provide a stub dex jar for the supplied scope then it does not contribute to
+// the returned list.
+func (s StubDexJarsByModule) StubDexJarsForScope(scope *HiddenAPIScope) android.Paths {
+ stubDexJars := android.Paths{}
+ modules := android.SortedStringKeys(s)
+ for _, module := range modules {
+ stubDexJarsByScope := s[module]
+ // Not every module will have the same set of
+ if jars, ok := stubDexJarsByScope[scope]; ok {
+ stubDexJars = append(stubDexJars, jars)
+ }
+ }
+
+ return stubDexJars
}
// HiddenAPIFlagInput encapsulates information obtained from a module and its dependencies that are
@@ -435,14 +664,21 @@
// from the stub dex files.
FlagFilesByCategory FlagFilesByCategory
- // StubDexJarsByKind contains the stub dex jars for different android.SdkKind and which determine
+ // StubDexJarsByScope contains the stub dex jars for different *HiddenAPIScope and which determine
// the initial flags for each dex member.
- StubDexJarsByKind StubDexJarsByKind
+ StubDexJarsByScope StubDexJarsByModule
- // DependencyStubDexJarsByKind contains the stub dex jars provided by the fragments on which this
- // depends. It is the result of merging HiddenAPIInfo.TransitiveStubDexJarsByKind from each
+ // DependencyStubDexJarsByScope contains the stub dex jars provided by the fragments on which this
+ // depends. It is the result of merging HiddenAPIInfo.TransitiveStubDexJarsByScope from each
// fragment on which this depends.
- DependencyStubDexJarsByKind StubDexJarsByKind
+ DependencyStubDexJarsByScope StubDexJarsByModule
+
+ // AdditionalStubDexJarsByScope contains stub dex jars provided by other modules in addition to
+ // the ones that are obtained from fragments on which this depends.
+ //
+ // These are kept separate from stub dex jars in HiddenAPIFlagInput.DependencyStubDexJarsByScope
+ // as there are not propagated transitively to other fragments that depend on this.
+ AdditionalStubDexJarsByScope StubDexJarsByModule
// RemovedTxtFiles is the list of removed.txt files provided by java_sdk_library modules that are
// specified in the bootclasspath_fragment's stub_libs and contents properties.
@@ -452,8 +688,10 @@
// newHiddenAPIFlagInput creates a new initialize HiddenAPIFlagInput struct.
func newHiddenAPIFlagInput() HiddenAPIFlagInput {
input := HiddenAPIFlagInput{
- FlagFilesByCategory: FlagFilesByCategory{},
- StubDexJarsByKind: StubDexJarsByKind{},
+ FlagFilesByCategory: FlagFilesByCategory{},
+ StubDexJarsByScope: StubDexJarsByModule{},
+ DependencyStubDexJarsByScope: StubDexJarsByModule{},
+ AdditionalStubDexJarsByScope: StubDexJarsByModule{},
}
return input
@@ -469,7 +707,7 @@
// required as the whole point of adding something to the bootclasspath fragment is to add it to
// the bootclasspath in order to be used by something else in the system. Without any stubs it
// cannot do that.
- if len(i.StubDexJarsByKind) == 0 {
+ if len(i.StubDexJarsByScope) == 0 {
return false
}
@@ -500,14 +738,15 @@
//
// That includes paths to the stub dex jars as well as paths to the *removed.txt files.
func (i *HiddenAPIFlagInput) gatherStubLibInfo(ctx android.ModuleContext, contents []android.Module) {
- addFromModule := func(ctx android.ModuleContext, module android.Module, kind android.SdkKind) {
- dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, kind)
+ addFromModule := func(ctx android.ModuleContext, module android.Module, apiScope *HiddenAPIScope) {
+ sdkKind := apiScope.sdkKind
+ dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, sdkKind)
if dexJar != nil {
- i.StubDexJarsByKind[kind] = append(i.StubDexJarsByKind[kind], dexJar)
+ i.StubDexJarsByScope.addStubDexJar(ctx, module, apiScope, dexJar)
}
if sdkLibrary, ok := module.(SdkLibraryDependency); ok {
- removedTxtFile := sdkLibrary.SdkRemovedTxtFile(ctx, kind)
+ removedTxtFile := sdkLibrary.SdkRemovedTxtFile(ctx, sdkKind)
i.RemovedTxtFiles = append(i.RemovedTxtFiles, removedTxtFile.AsPaths()...)
}
}
@@ -515,40 +754,44 @@
// If the contents includes any java_sdk_library modules then add them to the stubs.
for _, module := range contents {
if _, ok := module.(SdkLibraryDependency); ok {
- // Add information for every possible kind needed by hidden API. SdkCorePlatform is not used
- // as the java_sdk_library does not have special support for core_platform API, instead it is
- // implemented as a customized form of SdkPublic.
- for _, kind := range []android.SdkKind{android.SdkPublic, android.SdkSystem, android.SdkTest} {
- addFromModule(ctx, module, kind)
+ // Add information for every possible API scope needed by hidden API.
+ for _, apiScope := range hiddenAPISdkLibrarySupportedScopes {
+ addFromModule(ctx, module, apiScope)
}
}
}
- 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
- addFromModule(ctx, module, kind)
+ apiScope := hiddenAPIStubsTag.apiScope
+ if hiddenAPIStubsTag.fromAdditionalDependency {
+ dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, apiScope.sdkKind)
+ if dexJar != nil {
+ i.AdditionalStubDexJarsByScope.addStubDexJar(ctx, module, apiScope, dexJar)
+ }
+ } else {
+ addFromModule(ctx, module, apiScope)
+ }
}
})
// Normalize the paths, i.e. remove duplicates and sort.
- i.StubDexJarsByKind.dedupAndSort()
i.RemovedTxtFiles = android.SortedUniquePaths(i.RemovedTxtFiles)
}
// extractFlagFilesFromProperties extracts the paths to flag files that are specified in the
// supplied properties and stores them in this struct.
func (i *HiddenAPIFlagInput) extractFlagFilesFromProperties(ctx android.ModuleContext, p *HiddenAPIFlagFileProperties) {
- for _, category := range hiddenAPIFlagFileCategories {
+ for _, category := range HiddenAPIFlagFileCategories {
paths := android.PathsForModuleSrc(ctx, category.propertyValueReader(p))
i.FlagFilesByCategory[category] = paths
}
}
-func (i *HiddenAPIFlagInput) transitiveStubDexJarsByKind() StubDexJarsByKind {
- transitive := i.DependencyStubDexJarsByKind
- transitive.append(i.StubDexJarsByKind)
+func (i *HiddenAPIFlagInput) transitiveStubDexJarsByScope() StubDexJarsByModule {
+ transitive := i.DependencyStubDexJarsByScope
+ transitive.addStubDexJarsByModule(i.StubDexJarsByScope)
return transitive
}
@@ -571,6 +814,45 @@
AllFlagsPath android.Path
}
+// bootDexJarByModule is a map from base module name (without prebuilt_ prefix) to the boot dex
+// path.
+type bootDexJarByModule map[string]android.Path
+
+// addPath adds the path for a module to the map.
+func (b bootDexJarByModule) addPath(module android.Module, path android.Path) {
+ 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.
//
@@ -595,42 +877,20 @@
// 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.
- var validFile android.WritablePath
-
- // If there are flag files that have been generated by fragments on which this depends then use
- // them to validate the flag file generated by the rules created by this method.
- if len(allFlagsPaths) > 0 {
- // The flags file generated by the rule created by this method needs to be validated to ensure
- // that it is consistent with the flag files generated by the individual fragments.
-
- validFile = pathForValidation(ctx, outputPath)
-
- // Create a rule to validate the output from the following rule.
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().
- BuiltTool("verify_overlaps").
- Input(outputPath).
- Inputs(allFlagsPaths).
- // If validation passes then update the file that records that.
- Text("&& touch").Output(validFile)
- rule.Build(name+"Validation", desc+" validation")
- }
-
// Create the rule that will generate the flag files.
tempPath := tempPathForRestat(ctx, outputPath)
rule := android.NewRuleBuilder(pctx, ctx)
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.
- for _, category := range hiddenAPIFlagFileCategories {
+ for _, category := range HiddenAPIFlagFileCategories {
paths := flagFilesByCategory[category]
for _, path := range paths {
category.commandMutator(command, path)
@@ -645,7 +905,11 @@
commitChangeForRestat(rule, tempPath, outputPath)
- if validFile != nil {
+ // If there are flag files that have been generated by fragments on which this depends then use
+ // them to validate the flag file generated by the rules created by this method.
+ if len(allFlagsPaths) > 0 {
+ validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, allFlagsPaths)
+
// Add the file that indicates that the file generated by this is valid.
//
// This will cause the validation rule above to be run any time that the output of this rule
@@ -656,8 +920,27 @@
rule.Build(name, desc)
}
-// hiddenAPIGenerateAllFlagsForBootclasspathFragment will generate all the flags for a fragment
-// of the bootclasspath.
+// buildRuleValidateOverlappingCsvFiles checks that the modular CSV files, i.e. the files generated
+// by the individual bootclasspath_fragment modules are subsets of the monolithic CSV file.
+func buildRuleValidateOverlappingCsvFiles(ctx android.BuilderContext, name string, desc string, monolithicFilePath android.WritablePath, modularFilePaths android.Paths) android.WritablePath {
+ // The file which is used to record that the flags file is valid.
+ validFile := pathForValidation(ctx, monolithicFilePath)
+
+ // Create a rule to validate the output from the following rule.
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ BuiltTool("verify_overlaps").
+ Input(monolithicFilePath).
+ Inputs(modularFilePaths).
+ // If validation passes then update the file that records that.
+ Text("&& touch").Output(validFile)
+ rule.Build(name+"Validation", desc+" validation")
+
+ return validFile
+}
+
+// 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.
@@ -670,19 +953,19 @@
// * metadata.csv
// * index.csv
// * all-flags.csv
-func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext, contents []hiddenAPIModule, 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 := extractBootDexJarsFromHiddenAPIModules(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.Build("modularHiddenAPIStubFlagsFile", "modular hiddenapi stub flags")
+ buildRuleToGenerateHiddenAPIStubFlagsFile(ctx, "modularHiddenAPIStubFlagsFile", "modular hiddenapi stub flags", stubFlagsCSV, bootDexInfoByModule.bootDexJars(), input, nil)
// Extract the classes jars from the contents.
- classesJars := extractClassJarsFromHiddenAPIModules(ctx, contents)
+ classesJars := extractClassesJarsFromModules(contents)
// Generate the set of flags from the annotations in the source code.
annotationFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "annotation-flags.csv")
@@ -706,16 +989,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
}
@@ -737,58 +1033,215 @@
return android.OptionalPathForPath(output)
}
-// gatherHiddenAPIModuleFromContents gathers the hiddenAPIModule from the supplied contents.
-func gatherHiddenAPIModuleFromContents(ctx android.ModuleContext, contents []android.Module) []hiddenAPIModule {
- hiddenAPIModules := []hiddenAPIModule{}
+// extractBootDexJarsFromModules extracts the boot dex jars from the supplied modules.
+func extractBootDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule {
+ bootDexJars := bootDexJarByModule{}
for _, module := range contents {
- if hiddenAPI, ok := module.(hiddenAPIModule); ok {
- hiddenAPIModules = append(hiddenAPIModules, hiddenAPI)
- } else if _, ok := module.(*DexImport); ok {
- // Ignore this for the purposes of hidden API processing
- } else {
- ctx.ModuleErrorf("module %s does not implement hiddenAPIModule", module)
+ hiddenAPIModule := hiddenAPIModuleFromModule(ctx, module)
+ if hiddenAPIModule == nil {
+ continue
}
- }
- return hiddenAPIModules
-}
-
-// extractBootDexJarsFromHiddenAPIModules extracts the boot dex jars from the supplied modules.
-func extractBootDexJarsFromHiddenAPIModules(ctx android.ModuleContext, contents []hiddenAPIModule) android.Paths {
- bootDexJars := android.Paths{}
- for _, module := range contents {
- bootDexJar := module.bootDexJar()
- if bootDexJar == nil {
- if ctx.Config().AlwaysUsePrebuiltSdks() {
- // TODO(b/179354495): Remove this work around 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
}
-// extractClassJarsFromHiddenAPIModules extracts the class jars from the supplied modules.
-func extractClassJarsFromHiddenAPIModules(ctx android.ModuleContext, contents []hiddenAPIModule) android.Paths {
+func hiddenAPIModuleFromModule(ctx android.BaseModuleContext, module android.Module) hiddenAPIModule {
+ if hiddenAPIModule, ok := module.(hiddenAPIModule); ok {
+ return hiddenAPIModule
+ } else if _, ok := module.(*DexImport); ok {
+ // Ignore this for the purposes of hidden API processing
+ } else {
+ ctx.ModuleErrorf("module %s does not implement hiddenAPIModule", module)
+ }
+
+ 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{}
for _, module := range contents {
- classesJars = append(classesJars, module.classesJars()...)
+ classesJars = append(classesJars, retrieveClassesJarsFromModule(module)...)
}
return classesJars
}
+
+// retrieveClassesJarsFromModule retrieves the classes jars from the supplied module.
+func retrieveClassesJarsFromModule(module android.Module) android.Paths {
+ if hiddenAPIModule, ok := module.(hiddenAPIModule); ok {
+ return hiddenAPIModule.classesJars()
+ }
+
+ return nil
+}
+
+// deferReportingMissingBootDexJar returns true if a missing boot dex jar should not be reported by
+// Soong but should instead only be reported in ninja if the file is actually built.
+func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.Module) bool {
+ // TODO(b/179354495): Remove this workaround when it is unnecessary.
+ // Prebuilt modules like framework-wifi do not yet provide dex implementation jars. So,
+ // create a fake one that will cause a build error only if it is used.
+ if ctx.Config().AlwaysUsePrebuiltSdks() {
+ return true
+ }
+
+ // Any missing dependency should be allowed.
+ if ctx.Config().AllowMissingDependencies() {
+ return true
+ }
+
+ // 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.
+ // Ideally, a bootclasspath_fragment module should never have a platform variant created for it
+ // but unfortunately, due to b/187910671 it does.
+ //
+ // That causes issues when obtaining a boot dex jar for a prebuilt module as a prebuilt module
+ // used by a bootclasspath_fragment can only provide a boot dex jar when it is part of APEX, i.e.
+ // has an APEX variant not a platform variant.
+ //
+ // There are some other situations when a prebuilt module used by a bootclasspath_fragment cannot
+ // provide a boot dex jar:
+ // 1. If the bootclasspath_fragment is not exported by the prebuilt_apex/apex_set module then it
+ // does not have an APEX variant and only has a platform variant and neither do its content
+ // modules.
+ // 2. Some build configurations, e.g. setting TARGET_BUILD_USE_PREBUILT_SDKS causes all
+ // java_sdk_library_import modules to be treated as preferred and as many of them are not part
+ // of an apex they cannot provide a boot dex jar.
+ //
+ // The first case causes problems when the affected prebuilt modules are preferred but that is an
+ // invalid configuration and it is ok for it to fail as the work to enable that is not yet
+ // complete. The second case is used for building targets that do not use boot dex jars and so
+ // deferring error reporting to ninja is fine as the affected ninja targets should never be built.
+ // That is handled above.
+ //
+ // A platform_bootclasspath module can use libraries from both platform and APEX variants. Unlike
+ // the bootclasspath_fragment it supports dex_import modules which provides the dex file. So, it
+ // can obtain a boot dex jar from a prebuilt that is not part of an APEX. However, it is assumed
+ // that if the library can be part of an APEX then it is the APEX variant that is used.
+ //
+ // This check handles the slightly different requirements of the bootclasspath_fragment and
+ // platform_bootclasspath modules by only deferring error reporting for the platform variant of
+ // a prebuilt modules that has other variants which are part of an APEX.
+ //
+ // TODO(b/187910671): Remove this once platform variants are no longer created unnecessarily.
+ if android.IsModulePrebuilt(module) {
+ if am, ok := module.(android.ApexModule); ok && am.InAnyApex() {
+ apexInfo := ctx.OtherModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
+ if apexInfo.IsForPlatform() {
+ return true
+ }
+ }
+ }
+
+ // A bootclasspath module that is part of a versioned sdk never provides a boot dex jar as there
+ // is no equivalently versioned prebuilt APEX file from which it can be obtained. However,
+ // versioned bootclasspath modules are processed by Soong so in order to avoid them causing build
+ // failures missing boot dex jars need to be deferred.
+ if android.IsModuleInVersionedSdk(ctx.Module()) {
+ return true
+ }
+
+ return false
+}
+
+// handleMissingDexBootFile will either log a warning or create an error rule to create the fake
+// file depending on the value returned from deferReportingMissingBootDexJar.
+func handleMissingDexBootFile(ctx android.ModuleContext, module android.Module, fake android.WritablePath) {
+ if deferReportingMissingBootDexJar(ctx, module) {
+ // 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),
+ },
+ })
+ } else {
+ ctx.ModuleErrorf("module %s does not provide a dex jar", module)
+ }
+}
+
+// retrieveEncodedBootDexJarFromModule returns a path to the boot dex jar from the supplied module's
+// DexJarBuildPath() method.
+//
+// The returned path will usually be to a dex jar file that has been encoded with hidden API flags.
+// However, under certain conditions, e.g. errors, or special build configurations it will return
+// a path to a fake file.
+func retrieveEncodedBootDexJarFromModule(ctx android.ModuleContext, module android.Module) android.Path {
+ bootDexJar := module.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath()
+ if bootDexJar == nil {
+ fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/encoded-dex/%s.jar", module.Name()))
+ bootDexJar = fake
+
+ handleMissingDexBootFile(ctx, module, fake)
+ }
+ return bootDexJar
+}
+
+// extractEncodedDexJarsFromModules extracts the encoded dex jars from the supplied modules.
+func extractEncodedDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule {
+ encodedDexJarsByModuleName := bootDexJarByModule{}
+ for _, module := range contents {
+ path := retrieveEncodedBootDexJarFromModule(ctx, module)
+ encodedDexJarsByModuleName.addPath(module, path)
+ }
+ return encodedDexJarsByModuleName
+}
diff --git a/java/hiddenapi_monolithic.go b/java/hiddenapi_monolithic.go
index a6bf8c7..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.
@@ -99,4 +122,4 @@
i.AllFlagsPaths = android.FirstUniquePaths(i.AllFlagsPaths)
}
-var monolithicHiddenAPIInfoProvider = blueprint.NewProvider(MonolithicHiddenAPIInfo{})
+var MonolithicHiddenAPIInfoProvider = blueprint.NewProvider(MonolithicHiddenAPIInfo{})
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 bd373c1..0f9965d 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1392,3 +1392,95 @@
assertDeepEquals(t, "Default installable value should be true.", proptools.BoolPtr(true),
module.properties.Installable)
}
+
+func TestErrorproneEnabled(t *testing.T) {
+ ctx, _ := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ errorprone: {
+ enabled: true,
+ },
+ }
+ `)
+
+ javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
+
+ // Test that the errorprone plugins are passed to javac
+ expectedSubstring := "-Xplugin:ErrorProne"
+ if !strings.Contains(javac.Args["javacFlags"], expectedSubstring) {
+ t.Errorf("expected javacFlags to contain %q, got %q", expectedSubstring, javac.Args["javacFlags"])
+ }
+
+ // Modules with errorprone { enabled: true } will include errorprone checks
+ // in the main javac build rule. Only when RUN_ERROR_PRONE is true will
+ // the explicit errorprone build rule be created.
+ 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 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/kotlin_test.go b/java/kotlin_test.go
index 1c146a1..fd2f3ca 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -185,7 +185,6 @@
buildOS := android.BuildOs.String()
kapt := result.ModuleForTests("foo", "android_common").Rule("kapt")
- //kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
javac := result.ModuleForTests("foo", "android_common").Description("javac")
errorprone := result.ModuleForTests("foo", "android_common").Description("errorprone")
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/lint.go b/java/lint.go
index 1511cfe..dd5e4fb 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -78,6 +78,7 @@
minSdkVersion string
targetSdkVersion string
compileSdkVersion string
+ compileSdkKind android.SdkKind
javaLanguageLevel string
kotlinLanguageLevel string
outputs lintOutputs
@@ -389,13 +390,25 @@
rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
rule.Command().Text("rm -f").Output(html).Output(text).Output(xml)
+ var apiVersionsName, apiVersionsPrebuilt string
+ if l.compileSdkKind == android.SdkModule {
+ // When compiling an SDK module we use the filtered database because otherwise lint's
+ // NewApi check produces too many false positives; This database excludes information
+ // about classes created in mainline modules hence removing those false positives.
+ apiVersionsName = "api_versions_public_filtered.xml"
+ apiVersionsPrebuilt = "prebuilts/sdk/current/public/data/api-versions-filtered.xml"
+ } else {
+ apiVersionsName = "api_versions.xml"
+ apiVersionsPrebuilt = "prebuilts/sdk/current/public/data/api-versions.xml"
+ }
+
var annotationsZipPath, apiVersionsXMLPath android.Path
if ctx.Config().AlwaysUsePrebuiltSdks() {
annotationsZipPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/annotations.zip")
- apiVersionsXMLPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/api-versions.xml")
+ apiVersionsXMLPath = android.PathForSource(ctx, apiVersionsPrebuilt)
} else {
annotationsZipPath = copiedAnnotationsZipPath(ctx)
- apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx)
+ apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx, apiVersionsName)
}
cmd := rule.Command()
@@ -487,23 +500,27 @@
l.copyLintDependencies(ctx)
}
+func findModuleOrErr(ctx android.SingletonContext, moduleName string) android.Module {
+ var res android.Module
+ ctx.VisitAllModules(func(m android.Module) {
+ if ctx.ModuleName(m) == moduleName {
+ if res == nil {
+ res = m
+ } else {
+ ctx.Errorf("lint: multiple %s modules found: %s and %s", moduleName,
+ ctx.ModuleSubDir(m), ctx.ModuleSubDir(res))
+ }
+ }
+ })
+ return res
+}
+
func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) {
if ctx.Config().AlwaysUsePrebuiltSdks() {
return
}
- var frameworkDocStubs android.Module
- ctx.VisitAllModules(func(m android.Module) {
- if ctx.ModuleName(m) == "framework-doc-stubs" {
- if frameworkDocStubs == nil {
- frameworkDocStubs = m
- } else {
- ctx.Errorf("lint: multiple framework-doc-stubs modules found: %s and %s",
- ctx.ModuleSubDir(m), ctx.ModuleSubDir(frameworkDocStubs))
- }
- }
- })
-
+ frameworkDocStubs := findModuleOrErr(ctx, "framework-doc-stubs")
if frameworkDocStubs == nil {
if !ctx.Config().AllowMissingDependencies() {
ctx.Errorf("lint: missing framework-doc-stubs")
@@ -511,6 +528,14 @@
return
}
+ filteredDb := findModuleOrErr(ctx, "api-versions-xml-public-filtered")
+ if filteredDb == nil {
+ if !ctx.Config().AllowMissingDependencies() {
+ ctx.Errorf("lint: missing api-versions-xml-public-filtered")
+ }
+ return
+ }
+
ctx.Build(pctx, android.BuildParams{
Rule: android.CpIfChanged,
Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".annotations.zip"),
@@ -520,7 +545,13 @@
ctx.Build(pctx, android.BuildParams{
Rule: android.CpIfChanged,
Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".api_versions.xml"),
- Output: copiedAPIVersionsXmlPath(ctx),
+ Output: copiedAPIVersionsXmlPath(ctx, "api_versions.xml"),
+ })
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.CpIfChanged,
+ Input: android.OutputFileForModule(ctx, filteredDb, ""),
+ Output: copiedAPIVersionsXmlPath(ctx, "api_versions_public_filtered.xml"),
})
}
@@ -528,8 +559,8 @@
return android.PathForOutput(ctx, "lint", "annotations.zip")
}
-func copiedAPIVersionsXmlPath(ctx android.PathContext) android.WritablePath {
- return android.PathForOutput(ctx, "lint", "api_versions.xml")
+func copiedAPIVersionsXmlPath(ctx android.PathContext, name string) android.WritablePath {
+ return android.PathForOutput(ctx, "lint", name)
}
func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) {
diff --git a/java/lint_test.go b/java/lint_test.go
index 6d64de7..9cf1c33 100644
--- a/java/lint_test.go
+++ b/java/lint_test.go
@@ -219,3 +219,72 @@
t.Error("did not restrict baselining NewApi")
}
}
+
+func TestJavaLintDatabaseSelectionFull(t *testing.T) {
+ testCases := []string{
+ "current", "core_platform", "system_current", "S", "30", "10000",
+ }
+ bp := `
+ java_library {
+ name: "foo",
+ srcs: [
+ "a.java",
+ ],
+ min_sdk_version: "29",
+ sdk_version: "XXX",
+ lint: {
+ strict_updatability_linting: true,
+ },
+ }
+`
+ for _, testCase := range testCases {
+ thisBp := strings.Replace(bp, "XXX", testCase, 1)
+
+ result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules, FixtureWithPrebuiltApis(map[string][]string{
+ "30": {"foo"},
+ "10000": {"foo"},
+ })).
+ RunTestWithBp(t, thisBp)
+
+ foo := result.ModuleForTests("foo", "android_common")
+ sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
+ if strings.Contains(*sboxProto.Commands[0].Command,
+ "/api_versions_public_filtered.xml") {
+ t.Error("used public-filtered lint api database for case", testCase)
+ }
+ if !strings.Contains(*sboxProto.Commands[0].Command,
+ "/api_versions.xml") {
+ t.Error("did not use full api database for case", testCase)
+ }
+ }
+
+}
+
+func TestJavaLintDatabaseSelectionPublicFiltered(t *testing.T) {
+ bp := `
+ java_library {
+ name: "foo",
+ srcs: [
+ "a.java",
+ ],
+ min_sdk_version: "29",
+ sdk_version: "module_current",
+ lint: {
+ strict_updatability_linting: true,
+ },
+ }
+`
+ result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules).
+ RunTestWithBp(t, bp)
+
+ foo := result.ModuleForTests("foo", "android_common")
+ sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
+ if !strings.Contains(*sboxProto.Commands[0].Command,
+ "/api_versions_public_filtered.xml") {
+ t.Error("did not use public-filtered lint api database", *sboxProto.Commands[0].Command)
+ }
+ if strings.Contains(*sboxProto.Commands[0].Command,
+ "/api_versions.xml") {
+ t.Error("used full api database")
+ }
+}
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 87c695c..d72cee0 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.
@@ -123,8 +119,8 @@
}
// Add dependencies onto the stub lib modules.
- sdkKindToStubLibModules := hiddenAPIComputeMonolithicStubLibModules(ctx.Config())
- hiddenAPIAddStubLibDependencies(ctx, sdkKindToStubLibModules)
+ apiLevelToStubLibModules := hiddenAPIComputeMonolithicStubLibModules(ctx.Config())
+ hiddenAPIAddStubLibDependencies(ctx, apiLevelToStubLibModules)
}
func (b *platformBootclasspathModule) BootclasspathDepsMutator(ctx android.BottomUpMutatorContext) {
@@ -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,12 +291,19 @@
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)
- // Create the input to pass to ruleToGenerateHiddenAPIStubFlagsFile
+ 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 buildRuleToGenerateHiddenAPIStubFlagsFile
input := newHiddenAPIFlagInput()
// Gather stub library information from the dependencies on modules provided by
@@ -291,45 +313,60 @@
// Use the flag files from this module and all the fragments.
input.FlagFilesByCategory = monolithicInfo.FlagsFilesByCategory
- hiddenAPIModules := gatherHiddenAPIModuleFromContents(ctx, modules)
-
// Generate the monolithic stub-flags.csv file.
- bootDexJars := extractBootDexJarsFromHiddenAPIModules(ctx, hiddenAPIModules)
stubFlags := hiddenAPISingletonPaths(ctx).stubFlags
- rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlags, bootDexJars, input)
- rule.Build("platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags")
-
- // Extract the classes jars from the contents.
- classesJars := extractClassJarsFromHiddenAPIModules(ctx, hiddenAPIModules)
+ buildRuleToGenerateHiddenAPIStubFlagsFile(ctx, "platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags", stubFlags, bootDexJarByModule.bootDexJars(), input, monolithicInfo.StubFlagsPaths)
// 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()
@@ -339,10 +376,10 @@
// 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)
+ ctx.SetProvider(MonolithicHiddenAPIInfoProvider, monolithicInfo)
return monolithicInfo
}
@@ -390,11 +427,13 @@
generateUpdatableBcpPackagesRule(ctx, imageConfig, updatableModules)
// Copy non-updatable module dex jars to their predefined locations.
- copyBootJarsToPredefinedLocations(ctx, nonUpdatableModules, imageConfig.modules, imageConfig.dexPaths)
+ nonUpdatableBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, nonUpdatableModules)
+ copyBootJarsToPredefinedLocations(ctx, nonUpdatableBootDexJarsByModule, imageConfig.dexPathsByModule)
// Copy updatable module dex jars to their predefined locations.
config := GetUpdatableBootConfig(ctx)
- copyBootJarsToPredefinedLocations(ctx, updatableModules, config.modules, config.dexPaths)
+ updatableBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, updatableModules)
+ copyBootJarsToPredefinedLocations(ctx, updatableBootDexJarsByModule, config.dexPathsByModule)
// Build a profile for the image config and then use that to build the boot image.
profile := bootImageProfileRule(ctx, imageConfig)
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index ed5549d..1c2a3ae 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -15,8 +15,6 @@
package java
import (
- "fmt"
- "strings"
"testing"
"android/soong/android"
@@ -152,116 +150,6 @@
})
}
-func TestPlatformBootclasspath_Fragments(t *testing.T) {
- result := android.GroupFixturePreparers(
- prepareForTestWithPlatformBootclasspath,
- PrepareForTestWithJavaSdkLibraryFiles,
- FixtureWithLastReleaseApis("foo"),
- android.FixtureWithRootAndroidBp(`
- platform_bootclasspath {
- name: "platform-bootclasspath",
- fragments: [
- {module:"bar-fragment"},
- ],
- hidden_api: {
- unsupported: [
- "unsupported.txt",
- ],
- removed: [
- "removed.txt",
- ],
- max_target_r_low_priority: [
- "max-target-r-low-priority.txt",
- ],
- max_target_q: [
- "max-target-q.txt",
- ],
- max_target_p: [
- "max-target-p.txt",
- ],
- max_target_o_low_priority: [
- "max-target-o-low-priority.txt",
- ],
- blocked: [
- "blocked.txt",
- ],
- unsupported_packages: [
- "unsupported-packages.txt",
- ],
- },
- }
-
- bootclasspath_fragment {
- name: "bar-fragment",
- contents: ["bar"],
- api: {
- stub_libs: ["foo"],
- },
- hidden_api: {
- unsupported: [
- "bar-unsupported.txt",
- ],
- removed: [
- "bar-removed.txt",
- ],
- max_target_r_low_priority: [
- "bar-max-target-r-low-priority.txt",
- ],
- max_target_q: [
- "bar-max-target-q.txt",
- ],
- max_target_p: [
- "bar-max-target-p.txt",
- ],
- max_target_o_low_priority: [
- "bar-max-target-o-low-priority.txt",
- ],
- blocked: [
- "bar-blocked.txt",
- ],
- unsupported_packages: [
- "bar-unsupported-packages.txt",
- ],
- },
- }
-
- java_library {
- name: "bar",
- srcs: ["a.java"],
- system_modules: "none",
- sdk_version: "none",
- compile_dex: true,
- }
-
- java_sdk_library {
- name: "foo",
- srcs: ["a.java"],
- public: {
- enabled: true,
- },
- compile_dex: true,
- }
- `),
- ).RunTest(t)
-
- pbcp := result.Module("platform-bootclasspath", "android_common")
- info := result.ModuleProvider(pbcp, monolithicHiddenAPIInfoProvider).(MonolithicHiddenAPIInfo)
-
- for _, category := range hiddenAPIFlagFileCategories {
- name := category.propertyName
- message := fmt.Sprintf("category %s", name)
- filename := strings.ReplaceAll(name, "_", "-")
- expected := []string{fmt.Sprintf("%s.txt", filename), fmt.Sprintf("bar-%s.txt", filename)}
- android.AssertPathsRelativeToTopEquals(t, message, expected, info.FlagsFilesByCategory[category])
- }
-
- android.AssertPathsRelativeToTopEquals(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/stub-flags.csv"}, info.StubFlagsPaths)
- android.AssertPathsRelativeToTopEquals(t, "annotation flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/annotation-flags.csv"}, info.AnnotationFlagsPaths)
- android.AssertPathsRelativeToTopEquals(t, "metadata flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/metadata.csv"}, info.MetadataPaths)
- android.AssertPathsRelativeToTopEquals(t, "index flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/index.csv"}, info.IndexPaths)
- android.AssertPathsRelativeToTopEquals(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/all-flags.csv"}, info.AllFlagsPaths)
-}
-
func TestPlatformBootclasspathVariant(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForTestWithPlatformBootclasspath,
@@ -430,11 +318,41 @@
// Make sure that the foo-hiddenapi-annotations.jar is included in the inputs to the rules that
// creates the index.csv file.
platformBootclasspath := result.ModuleForTests("myplatform-bootclasspath", "android_common")
- indexRule := platformBootclasspath.Rule("monolithic_hidden_API_index")
- CheckHiddenAPIRuleInputs(t, `
-.intermediates/bar/android_common/javac/bar.jar
-.intermediates/foo-hiddenapi-annotations/android_common/javac/foo-hiddenapi-annotations.jar
-.intermediates/foo/android_common/javac/foo.jar
-`,
- indexRule)
+
+ var rule android.TestingBuildParams
+
+ // All the intermediate rules use the same inputs.
+ expectedIntermediateInputs := `
+ out/soong/.intermediates/bar/android_common/javac/bar.jar
+ out/soong/.intermediates/foo-hiddenapi-annotations/android_common/javac/foo-hiddenapi-annotations.jar
+ out/soong/.intermediates/foo/android_common/javac/foo.jar
+ `
+
+ // Check flags output.
+ rule = platformBootclasspath.Output("hiddenapi-monolithic/annotation-flags-from-classes.csv")
+ CheckHiddenAPIRuleInputs(t, "intermediate flags", expectedIntermediateInputs, rule)
+
+ rule = platformBootclasspath.Output("out/soong/hiddenapi/hiddenapi-flags.csv")
+ CheckHiddenAPIRuleInputs(t, "monolithic flags", `
+ out/soong/.intermediates/myplatform-bootclasspath/android_common/hiddenapi-monolithic/annotation-flags-from-classes.csv
+ out/soong/hiddenapi/hiddenapi-stub-flags.txt
+ `, rule)
+
+ // Check metadata output.
+ rule = platformBootclasspath.Output("hiddenapi-monolithic/metadata-from-classes.csv")
+ CheckHiddenAPIRuleInputs(t, "intermediate metadata", expectedIntermediateInputs, rule)
+
+ rule = platformBootclasspath.Output("out/soong/hiddenapi/hiddenapi-unsupported.csv")
+ CheckHiddenAPIRuleInputs(t, "monolithic metadata", `
+ out/soong/.intermediates/myplatform-bootclasspath/android_common/hiddenapi-monolithic/metadata-from-classes.csv
+ `, rule)
+
+ // Check index output.
+ rule = platformBootclasspath.Output("hiddenapi-monolithic/index-from-classes.csv")
+ CheckHiddenAPIRuleInputs(t, "intermediate index", expectedIntermediateInputs, rule)
+
+ rule = platformBootclasspath.Output("out/soong/hiddenapi/hiddenapi-index.csv")
+ CheckHiddenAPIRuleInputs(t, "monolithic index", `
+ out/soong/.intermediates/myplatform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
+ `, rule)
}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 9492729..567e292 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -631,9 +631,17 @@
Doctag_files []string `android:"path"`
}
+// commonSdkLibraryAndImportModule defines the interface that must be provided by a module that
+// embeds the commonToSdkLibraryAndImport struct.
+type commonSdkLibraryAndImportModule interface {
+ android.SdkAware
+
+ BaseModuleName() string
+}
+
// Common code between sdk library and sdk library import
type commonToSdkLibraryAndImport struct {
- moduleBase *android.ModuleBase
+ module commonSdkLibraryAndImportModule
scopePaths map[*apiScope]*scopePaths
@@ -648,13 +656,13 @@
EmbeddableSdkLibraryComponent
}
-func (c *commonToSdkLibraryAndImport) initCommon(moduleBase *android.ModuleBase) {
- c.moduleBase = moduleBase
+func (c *commonToSdkLibraryAndImport) initCommon(module commonSdkLibraryAndImportModule) {
+ c.module = module
- moduleBase.AddProperties(&c.commonSdkLibraryProperties)
+ module.AddProperties(&c.commonSdkLibraryProperties)
// Initialize this as an sdk library component.
- c.initSdkLibraryComponent(moduleBase)
+ c.initSdkLibraryComponent(module)
}
func (c *commonToSdkLibraryAndImport) initCommonAfterDefaultsApplied(ctx android.DefaultableHookContext) bool {
@@ -670,41 +678,50 @@
// Only track this sdk library if this can be used as a shared library.
if c.sharedLibrary() {
// Use the name specified in the module definition as the owner.
- c.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.moduleBase.BaseModuleName())
+ c.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.module.BaseModuleName())
}
return true
}
+// uniqueApexVariations provides common implementation of the ApexModule.UniqueApexVariations
+// method.
+func (c *commonToSdkLibraryAndImport) uniqueApexVariations() bool {
+ // A java_sdk_library that is a shared library produces an XML file that makes the shared library
+ // usable from an AndroidManifest.xml's <uses-library> entry. That XML file contains the name of
+ // the APEX and so it needs a unique variation per APEX.
+ return c.sharedLibrary()
+}
+
func (c *commonToSdkLibraryAndImport) generateCommonBuildActions(ctx android.ModuleContext) {
c.doctagPaths = android.PathsForModuleSrc(ctx, c.commonSdkLibraryProperties.Doctag_files)
}
// Module name of the runtime implementation library
func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string {
- return c.moduleBase.BaseModuleName() + ".impl"
+ return c.module.BaseModuleName() + ".impl"
}
// Module name of the XML file for the lib
func (c *commonToSdkLibraryAndImport) xmlPermissionsModuleName() string {
- return c.moduleBase.BaseModuleName() + sdkXmlFileSuffix
+ return c.module.BaseModuleName() + sdkXmlFileSuffix
}
// Name of the java_library module that compiles the stubs source.
func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string {
- return c.namingScheme.stubsLibraryModuleName(apiScope, c.moduleBase.BaseModuleName())
+ baseName := c.module.BaseModuleName()
+ return c.module.SdkMemberComponentName(baseName, func(name string) string {
+ return c.namingScheme.stubsLibraryModuleName(apiScope, name)
+ })
}
// Name of the droidstubs module that generates the stubs source and may also
// generate/check the API.
func (c *commonToSdkLibraryAndImport) stubsSourceModuleName(apiScope *apiScope) string {
- return c.namingScheme.stubsSourceModuleName(apiScope, c.moduleBase.BaseModuleName())
-}
-
-// Name of the droidstubs module that generates/checks the API. Only used if it
-// requires different arts to the stubs source generating module.
-func (c *commonToSdkLibraryAndImport) apiModuleName(apiScope *apiScope) string {
- return c.namingScheme.apiModuleName(apiScope, c.moduleBase.BaseModuleName())
+ baseName := c.module.BaseModuleName()
+ return c.module.SdkMemberComponentName(baseName, func(name string) string {
+ return c.namingScheme.stubsSourceModuleName(apiScope, name)
+ })
}
// The component names for different outputs of the java_sdk_library.
@@ -753,7 +770,7 @@
if scope, ok := scopeByName[scopeName]; ok {
paths := c.findScopePaths(scope)
if paths == nil {
- return nil, fmt.Errorf("%q does not provide api scope %s", c.moduleBase.BaseModuleName(), scopeName)
+ return nil, fmt.Errorf("%q does not provide api scope %s", c.module.BaseModuleName(), scopeName)
}
switch component {
@@ -784,7 +801,7 @@
if c.doctagPaths != nil {
return c.doctagPaths, nil
} else {
- return nil, fmt.Errorf("no doctag_files specified on %s", c.moduleBase.BaseModuleName())
+ return nil, fmt.Errorf("no doctag_files specified on %s", c.module.BaseModuleName())
}
}
return nil, nil
@@ -830,7 +847,7 @@
// If a specific numeric version has been requested then use prebuilt versions of the sdk.
if !sdkVersion.ApiLevel.IsPreview() {
- return PrebuiltJars(ctx, c.moduleBase.BaseModuleName(), sdkVersion)
+ return PrebuiltJars(ctx, c.module.BaseModuleName(), sdkVersion)
}
paths := c.selectScopePaths(ctx, sdkVersion.Kind)
@@ -857,7 +874,7 @@
scopes = append(scopes, s.name)
}
}
- ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.moduleBase.BaseModuleName(), scopes)
+ ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.module.BaseModuleName(), scopes)
return nil
}
@@ -913,7 +930,7 @@
// any app that includes code which depends (directly or indirectly) on the stubs
// library will have the appropriate <uses-library> invocation inserted into its
// manifest if necessary.
- componentProps.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.moduleBase.BaseModuleName())
+ componentProps.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.module.BaseModuleName())
}
return componentProps
@@ -945,8 +962,8 @@
sdkLibraryComponentProperties SdkLibraryComponentProperties
}
-func (e *EmbeddableSdkLibraryComponent) initSdkLibraryComponent(moduleBase *android.ModuleBase) {
- moduleBase.AddProperties(&e.sdkLibraryComponentProperties)
+func (e *EmbeddableSdkLibraryComponent) initSdkLibraryComponent(module android.Module) {
+ module.AddProperties(&e.sdkLibraryComponentProperties)
}
// to satisfy SdkLibraryComponentDependency
@@ -1168,6 +1185,10 @@
module.Library.GenerateAndroidBuildActions(ctx)
}
+ // Collate the components exported by this module. All scope specific modules are exported but
+ // the impl and xml component modules are not.
+ exportedComponents := map[string]struct{}{}
+
// Record the paths to the header jars of the library (stubs and impl).
// When this java_sdk_library is depended upon from others via "libs" property,
// the recorded paths will be returned depending on the link type of the caller.
@@ -1182,8 +1203,14 @@
// Extract information from the dependency. The exact information extracted
// is determined by the nature of the dependency which is determined by the tag.
scopeTag.extractDepInfo(ctx, to, scopePaths)
+
+ exportedComponents[ctx.OtherModuleName(to)] = struct{}{}
}
})
+
+ // Make the set of components exported by this module available for use elsewhere.
+ exportedComponentInfo := android.ExportedComponentsInfo{Components: android.SortedStringKeys(exportedComponents)}
+ ctx.SetProvider(android.ExportedComponentsInfoProvider, exportedComponentInfo)
}
func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {
@@ -1510,6 +1537,7 @@
mctx.CreateModule(DroidstubsFactory, &props)
}
+// Implements android.ApexModule
func (module *SdkLibrary) DepIsInSameApex(mctx android.BaseModuleContext, dep android.Module) bool {
depTag := mctx.OtherModuleDependencyTag(dep)
if depTag == xmlPermissionsFileTag {
@@ -1518,6 +1546,11 @@
return module.Library.DepIsInSameApex(mctx, dep)
}
+// Implements android.ApexModule
+func (module *SdkLibrary) UniqueApexVariations() bool {
+ return module.uniqueApexVariations()
+}
+
// Creates the xml file that publicizes the runtime library
func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) {
props := struct {
@@ -1713,7 +1746,7 @@
module.addHostAndDeviceProperties()
module.AddProperties(&module.sdkLibraryProperties)
- module.initSdkLibraryComponent(&module.ModuleBase)
+ module.initSdkLibraryComponent(module)
module.properties.Installable = proptools.BoolPtr(true)
module.deviceProperties.IsSDKLibrary = true
@@ -1732,8 +1765,6 @@
stubsLibraryModuleName(scope *apiScope, baseName string) string
stubsSourceModuleName(scope *apiScope, baseName string) string
-
- apiModuleName(scope *apiScope, baseName string) string
}
type defaultNamingScheme struct {
@@ -1747,10 +1778,6 @@
return scope.stubsSourceModuleName(baseName)
}
-func (s *defaultNamingScheme) apiModuleName(scope *apiScope, baseName string) string {
- return scope.apiModuleName(baseName)
-}
-
var _ sdkLibraryComponentNamingScheme = (*defaultNamingScheme)(nil)
func moduleStubLinkType(name string) (stub bool, ret sdkLinkType) {
@@ -1784,7 +1811,7 @@
module := &SdkLibrary{}
// Initialize information common between source and prebuilt.
- module.initCommon(&module.ModuleBase)
+ module.initCommon(module)
module.InitSdkLibraryProperties()
android.InitApexModule(module)
@@ -1932,7 +1959,7 @@
module.AddProperties(&module.properties, allScopeProperties)
// Initialize information common between source and prebuilt.
- module.initCommon(&module.ModuleBase)
+ module.initCommon(module)
android.InitPrebuiltModule(module, &[]string{""})
android.InitApexModule(module)
@@ -2078,6 +2105,11 @@
return nil
}
+// Implements android.ApexModule
+func (module *SdkLibraryImport) UniqueApexVariations() bool {
+ return module.uniqueApexVariations()
+}
+
func (module *SdkLibraryImport) OutputFiles(tag string) (android.Paths, error) {
return module.commonOutputFiles(tag)
}
@@ -2144,7 +2176,7 @@
// Get the path of the dex implementation jar from the `deapexer` module.
di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
- if dexOutputPath := di.PrebuiltExportPath(module.BaseModuleName(), ".dexjar"); dexOutputPath != nil {
+ if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(module.BaseModuleName())); dexOutputPath != nil {
module.dexJarFile = dexOutputPath
module.initHiddenAPI(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil)
} else {
@@ -2269,6 +2301,13 @@
}
}
+var _ android.RequiredFilesFromPrebuiltApex = (*SdkLibraryImport)(nil)
+
+func (module *SdkLibraryImport) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
+ name := module.BaseModuleName()
+ return requiredFilesFromPrebuiltApexForImport(name)
+}
+
//
// java_sdk_library_xml
//
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 2520dde..8e0618e 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -110,7 +110,7 @@
`)
// check the existence of the internal modules
- result.ModuleForTests("foo", "android_common")
+ foo := result.ModuleForTests("foo", "android_common")
result.ModuleForTests(apiScopePublic.stubsLibraryModuleName("foo"), "android_common")
result.ModuleForTests(apiScopeSystem.stubsLibraryModuleName("foo"), "android_common")
result.ModuleForTests(apiScopeTest.stubsLibraryModuleName("foo"), "android_common")
@@ -122,6 +122,17 @@
result.ModuleForTests("foo.api.system.28", "")
result.ModuleForTests("foo.api.test.28", "")
+ exportedComponentsInfo := result.ModuleProvider(foo.Module(), android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo)
+ expectedFooExportedComponents := []string{
+ "foo.stubs",
+ "foo.stubs.source",
+ "foo.stubs.source.system",
+ "foo.stubs.source.test",
+ "foo.stubs.system",
+ "foo.stubs.test",
+ }
+ android.AssertArrayString(t, "foo exported components", expectedFooExportedComponents, exportedComponentsInfo.Components)
+
bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac")
// tests if baz is actually linked to the stubs lib
android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.system.jar")
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index 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/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 394fcc5..31ec3f1 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -26,9 +26,18 @@
"system/logging/rust",
"system/security",
"system/tools/aidl",
+ "tools/security/fuzzing/example_rust_fuzzer",
+ }
+
+ DownstreamRustAllowedPaths = []string{
+ // Add downstream allowed Rust paths here.
}
RustModuleTypes = []string{
+ // Don't add rust_bindgen or rust_protobuf as these are code generation modules
+ // and can be expected to be in paths without Rust code.
+ "rust_benchmark",
+ "rust_benchmark_host",
"rust_binary",
"rust_binary_host",
"rust_library",
@@ -37,6 +46,7 @@
"rust_ffi",
"rust_ffi_shared",
"rust_ffi_static",
+ "rust_fuzz",
"rust_library_host",
"rust_library_host_dylib",
"rust_library_host_rlib",
diff --git a/rust/rust.go b/rust/rust.go
index b8c8be5..05f6399 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -35,7 +35,7 @@
android.AddNeverAllowRules(
android.NeverAllow().
- NotIn(config.RustAllowedPaths...).
+ NotIn(append(config.RustAllowedPaths, config.DownstreamRustAllowedPaths...)...).
ModuleType(config.RustModuleTypes...))
android.RegisterModuleType("rust_defaults", defaultsFactory)
@@ -288,6 +288,10 @@
return mod.Properties.VndkVersion != ""
}
+func (mod *Module) Bootstrap() bool {
+ return false
+}
+
func (mod *Module) MustUseVendorVariant() bool {
return true
}
@@ -952,7 +956,7 @@
directRlibDeps := []*Module{}
directDylibDeps := []*Module{}
directProcMacroDeps := []*Module{}
- directSharedLibDeps := [](cc.LinkableInterface){}
+ directSharedLibDeps := []cc.SharedLibraryInfo{}
directStaticLibDeps := [](cc.LinkableInterface){}
directSrcProvidersDeps := []*Module{}
directSrcDeps := [](android.SourceFileProducer){}
@@ -1073,14 +1077,23 @@
directStaticLibDeps = append(directStaticLibDeps, ccDep)
mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, makeLibName)
case cc.IsSharedDepTag(depTag):
+ // For the shared lib dependencies, we may link to the stub variant
+ // of the dependency depending on the context (e.g. if this
+ // dependency crosses the APEX boundaries).
+ sharedLibraryInfo, exportedInfo := cc.ChooseStubOrImpl(ctx, dep)
+
+ // Re-get linkObject as ChooseStubOrImpl actually tells us which
+ // object (either from stub or non-stub) to use.
+ linkObject = android.OptionalPathForPath(sharedLibraryInfo.SharedLibrary)
+ linkPath = linkPathFromFilePath(linkObject.Path())
+
depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
depPaths.linkObjects = append(depPaths.linkObjects, linkObject.String())
- exportedInfo := ctx.OtherModuleProvider(dep, cc.FlagExporterInfoProvider).(cc.FlagExporterInfo)
depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
- directSharedLibDeps = append(directSharedLibDeps, ccDep)
+ directSharedLibDeps = append(directSharedLibDeps, sharedLibraryInfo)
// Record baseLibName for snapshots.
mod.Properties.SnapshotSharedLibs = append(mod.Properties.SnapshotSharedLibs, cc.BaseLibName(depName))
@@ -1135,11 +1148,11 @@
var sharedLibFiles android.Paths
var sharedLibDepFiles android.Paths
for _, dep := range directSharedLibDeps {
- sharedLibFiles = append(sharedLibFiles, dep.OutputFile().Path())
- if dep.Toc().Valid() {
- sharedLibDepFiles = append(sharedLibDepFiles, dep.Toc().Path())
+ sharedLibFiles = append(sharedLibFiles, dep.SharedLibrary)
+ if dep.TableOfContents.Valid() {
+ sharedLibDepFiles = append(sharedLibDepFiles, dep.TableOfContents.Path())
} else {
- sharedLibDepFiles = append(sharedLibDepFiles, dep.OutputFile().Path())
+ sharedLibDepFiles = append(sharedLibDepFiles, dep.SharedLibrary)
}
}
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 1c02bd0..635be10 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -265,3 +265,19 @@
"linker_config_proto",
],
}
+
+python_binary_host {
+ name: "get_clang_version",
+ main: "get_clang_version.py",
+ srcs: [
+ "get_clang_version.py",
+ ],
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ },
+ },
+}
diff --git a/scripts/OWNERS b/scripts/OWNERS
index 2b9c2de..1830a18 100644
--- a/scripts/OWNERS
+++ b/scripts/OWNERS
@@ -1,6 +1,6 @@
per-file system-clang-format,system-clang-format-2 = enh@google.com,smoreland@google.com
per-file build-mainline-modules.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com
per-file build-aml-prebuilts.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com
-per-file construct_context.py = ngeoffray@google.com,calin@google.com,mathieuc@google.com,skvadrik@google.com
+per-file construct_context.py = ngeoffray@google.com,calin@google.com,skvadrik@google.com
per-file conv_linker_config.py = kiyoungkim@google.com, jiyong@google.com, jooyung@google.com
per-file gen_ndk*.sh = sophiez@google.com, allenhair@google.com
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/scripts/get_clang_version.py b/scripts/get_clang_version.py
new file mode 100755
index 0000000..622fca1
--- /dev/null
+++ b/scripts/get_clang_version.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+#
+# 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.
+"""A tool to report the current clang version used during build"""
+
+import os
+import re
+import sys
+
+
+ANDROID_BUILD_TOP = os.environ.get("ANDROID_BUILD_TOP", ".")
+
+def get_clang_prebuilts_version(global_go):
+ # TODO(b/187231324): Get clang version from the json file once it is no longer
+ # hard-coded in global.go
+ if global_go is None:
+ global_go = ANDROID_BUILD_TOP + '/build/soong/cc/config/global.go'
+ with open(global_go) as infile:
+ contents = infile.read()
+
+ regex_rev = r'\tClangDefaultVersion\s+= "clang-(?P<rev>r\d+[a-z]?\d?)"'
+ match_rev = re.search(regex_rev, contents)
+ if match_rev is None:
+ raise RuntimeError('Parsing clang info failed')
+ return match_rev.group('rev')
+
+
+def main():
+ global_go = sys.argv[1] if len(sys.argv) > 1 else None
+ print(get_clang_prebuilts_version(global_go));
+
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/get_clang_version_test.py b/scripts/get_clang_version_test.py
new file mode 100644
index 0000000..e57df6c
--- /dev/null
+++ b/scripts/get_clang_version_test.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+"""Unit tests for get_clang_version.py."""
+
+import unittest
+
+import get_clang_version
+
+class GetClangVersionTest(unittest.TestCase):
+ """Unit tests for get_clang_version."""
+
+ def test_get_clang_version(self):
+ """Test parsing of clang prebuilts version."""
+ self.assertIsNotNone(get_clang_version.get_clang_prebuilts_version())
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/scripts/hiddenapi/merge_csv.py b/scripts/hiddenapi/merge_csv.py
index b047aab..a65326c 100755
--- a/scripts/hiddenapi/merge_csv.py
+++ b/scripts/hiddenapi/merge_csv.py
@@ -55,14 +55,15 @@
if entry.endswith('.uau'):
csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r'))))
-headers = set()
if args.header:
fieldnames = args.header.split(',')
else:
+ headers = {}
# Build union of all columns from source files:
for reader in csv_readers:
- headers = headers.union(reader.fieldnames)
- fieldnames = sorted(headers)
+ for fieldname in reader.fieldnames:
+ headers[fieldname] = ""
+ fieldnames = list(headers.keys())
# By default chain the csv readers together so that the resulting output is
# the concatenation of the rows from each of them:
diff --git a/scripts/hiddenapi/verify_overlaps.py b/scripts/hiddenapi/verify_overlaps.py
index c8e3879..bb0917e 100755
--- a/scripts/hiddenapi/verify_overlaps.py
+++ b/scripts/hiddenapi/verify_overlaps.py
@@ -47,9 +47,9 @@
if signature in allFlagsBySignature:
allFlags = allFlagsBySignature.get(signature)
if allFlags != row:
- mismatchingSignatures.append((signature, row[None], allFlags[None]))
+ mismatchingSignatures.append((signature, row.get(None, []), allFlags.get(None, [])))
else:
- mismatchingSignatures.append((signature, row[None], []))
+ mismatchingSignatures.append((signature, row.get(None, []), []))
if mismatchingSignatures:
@@ -60,7 +60,7 @@
for mismatch in mismatchingSignatures:
print()
print("< " + mismatch[0] + "," + ",".join(mismatch[1]))
- if mismatch[2] != None:
+ if mismatch[2] != []:
print("> " + mismatch[0] + "," + ",".join(mismatch[2]))
else:
print("> " + mismatch[0] + " - missing")
diff --git a/scripts/rbc-run b/scripts/rbc-run
new file mode 100755
index 0000000..e2fa6d1
--- /dev/null
+++ b/scripts/rbc-run
@@ -0,0 +1,16 @@
+#! /bin/bash
+# Convert and run one configuration
+# Args: <product>-<variant>
+[[ $# -eq 1 && "$1" =~ ^(.*)-(.*)$ ]] || { echo Usage: ${0##*/} PRODUCT-VARIANT >&2; exit 1; }
+declare -r product="${BASH_REMATCH[1]:-aosp_arm}"
+declare -r variant="${BASH_REMATCH[2]:-eng}"
+set -eu
+declare -r output_root=${OUT_DIR:-out}
+declare -r runner="$output_root/soong/.bootstrap/bin/rbcrun"
+declare -r converter="$output_root/soong/.bootstrap/bin/mk2rbc"
+declare -r launcher=$output_root/launchers/run.rbc
+$converter -mode=write -r --outdir $output_root --launcher=$launcher $product
+printf "#TARGET_PRODUCT=$product TARGET_BUILD_VARIANT=$variant\n"
+env TARGET_PRODUCT=$product TARGET_BUILD_VARIANT=$variant \
+ $runner RBC_OUT="make,global" RBC_DEBUG="${RBC_DEBUG:-}" $launcher
+
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index d9fe281..a458cba 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -15,12 +15,54 @@
package sdk
import (
+ "fmt"
+ "path/filepath"
"testing"
"android/soong/android"
"android/soong/java"
)
+// fixtureAddPlatformBootclasspathForBootclasspathFragment adds a platform_bootclasspath module that
+// references the bootclasspath fragment.
+func fixtureAddPlatformBootclasspathForBootclasspathFragment(apex, fragment string) android.FixturePreparer {
+ return android.GroupFixturePreparers(
+ // Add a platform_bootclasspath module.
+ android.FixtureAddTextFile("frameworks/base/boot/Android.bp", fmt.Sprintf(`
+ platform_bootclasspath {
+ name: "platform-bootclasspath",
+ fragments: [
+ {
+ apex: "%s",
+ module: "%s",
+ },
+ ],
+ }
+ `, apex, fragment)),
+ android.FixtureAddFile("frameworks/base/config/boot-profile.txt", nil),
+ android.FixtureAddFile("build/soong/scripts/check_boot_jars/package_allowed_list.txt", nil),
+ )
+}
+
+// fixtureAddPrebuiltApexForBootclasspathFragment adds a prebuilt_apex that exports the fragment.
+func fixtureAddPrebuiltApexForBootclasspathFragment(apex, fragment string) android.FixturePreparer {
+ apexFile := fmt.Sprintf("%s.apex", apex)
+ dir := "prebuilts/apex"
+ return android.GroupFixturePreparers(
+ // A preparer to add a prebuilt apex to the test fixture.
+ android.FixtureAddTextFile(filepath.Join(dir, "Android.bp"), fmt.Sprintf(`
+ prebuilt_apex {
+ name: "%s",
+ src: "%s",
+ exported_bootclasspath_fragments: [
+ "%s",
+ ],
+ }
+ `, apex, apexFile, fragment)),
+ android.FixtureAddFile(filepath.Join(dir, apexFile), nil),
+ )
+}
+
func TestSnapshotWithBootclasspathFragment_ImageName(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForSdkTestWithJava,
@@ -34,20 +76,8 @@
"system/sepolicy/apex/com.android.art-file_contexts": nil,
}),
- // platform_bootclasspath that depends on the fragment.
- android.FixtureAddTextFile("frameworks/base/boot/Android.bp", `
- platform_bootclasspath {
- name: "platform-bootclasspath",
- fragments: [
- {
- apex: "com.android.art",
- module: "mybootclasspathfragment",
- },
- ],
- }
- `),
- // Needed for platform_bootclasspath
- android.FixtureAddFile("frameworks/base/config/boot-profile.txt", nil),
+ // Add a platform_bootclasspath that depends on the fragment.
+ fixtureAddPlatformBootclasspathForBootclasspathFragment("com.android.art", "mybootclasspathfragment"),
java.FixtureConfigureBootJars("com.android.art:mybootlib"),
android.FixtureWithRootAndroidBp(`
@@ -89,19 +119,8 @@
`),
).RunTest(t)
- // A preparer to add a prebuilt apex to the test fixture.
- prepareWithPrebuiltApex := android.GroupFixturePreparers(
- android.FixtureAddTextFile("prebuilts/apex/Android.bp", `
- prebuilt_apex {
- name: "com.android.art",
- src: "art.apex",
- exported_bootclasspath_fragments: [
- "mybootclasspathfragment",
- ],
- }
- `),
- android.FixtureAddFile("prebuilts/apex/art.apex", nil),
- )
+ // A preparer to update the test fixture used when processing an unpackage snapshot.
+ preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("com.android.art", "mybootclasspathfragment")
CheckSnapshot(t, result, "mysdk", "",
checkUnversionedAndroidBpContents(`
@@ -154,10 +173,30 @@
checkAllCopyRules(`
.intermediates/mybootlib/android_common/javac/mybootlib.jar -> java/mybootlib.jar
`),
- snapshotTestPreparer(checkSnapshotWithoutSource, prepareWithPrebuiltApex),
- snapshotTestPreparer(checkSnapshotWithSourcePreferred, prepareWithPrebuiltApex),
- snapshotTestPreparer(checkSnapshotPreferredWithSource, prepareWithPrebuiltApex),
+ 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) {
@@ -166,6 +205,12 @@
java.PrepareForTestWithJavaDefaultModules,
java.PrepareForTestWithJavaSdkLibraryFiles,
java.FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary", "mycoreplatform"),
+ java.FixtureConfigureUpdatableBootJars("myapex:mybootlib", "myapex:myothersdklibrary"),
+ prepareForSdkTestWithApex,
+
+ // Add a platform_bootclasspath that depends on the fragment.
+ fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"),
+
android.FixtureWithRootAndroidBp(`
sdk {
name: "mysdk",
@@ -179,8 +224,16 @@
],
}
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ min_sdk_version: "2",
+ bootclasspath_fragments: ["mybootclasspathfragment"],
+ }
+
bootclasspath_fragment {
name: "mybootclasspathfragment",
+ apex_available: ["myapex"],
contents: [
// This should be automatically added to the sdk_snapshot as a java_boot_libs module.
"mybootlib",
@@ -198,35 +251,48 @@
java_library {
name: "mybootlib",
+ apex_available: ["myapex"],
srcs: ["Test.java"],
system_modules: "none",
sdk_version: "none",
+ min_sdk_version: "2",
compile_dex: true,
+ permitted_packages: ["mybootlib"],
}
java_sdk_library {
name: "mysdklibrary",
+ apex_available: ["myapex"],
srcs: ["Test.java"],
shared_library: false,
public: {enabled: true},
+ min_sdk_version: "2",
}
java_sdk_library {
name: "myothersdklibrary",
+ apex_available: ["myapex"],
srcs: ["Test.java"],
- shared_library: false,
+ compile_dex: true,
public: {enabled: true},
+ min_sdk_version: "2",
+ permitted_packages: ["myothersdklibrary"],
}
java_sdk_library {
name: "mycoreplatform",
+ apex_available: ["myapex"],
srcs: ["Test.java"],
- shared_library: false,
+ compile_dex: true,
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.
@@ -235,7 +301,7 @@
name: "mybootclasspathfragment",
prefer: false,
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
+ apex_available: ["myapex"],
contents: [
"mybootlib",
"myothersdklibrary",
@@ -259,7 +325,7 @@
name: "mybootlib",
prefer: false,
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
+ apex_available: ["myapex"],
jars: ["java/mybootlib.jar"],
}
@@ -267,8 +333,9 @@
name: "myothersdklibrary",
prefer: false,
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- shared_library: false,
+ apex_available: ["myapex"],
+ shared_library: true,
+ compile_dex: true,
public: {
jars: ["sdk_library/public/myothersdklibrary-stubs.jar"],
stub_srcs: ["sdk_library/public/myothersdklibrary_stub_sources"],
@@ -282,7 +349,7 @@
name: "mysdklibrary",
prefer: false,
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
+ apex_available: ["myapex"],
shared_library: false,
public: {
jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
@@ -297,8 +364,9 @@
name: "mycoreplatform",
prefer: false,
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- shared_library: false,
+ apex_available: ["myapex"],
+ shared_library: true,
+ compile_dex: true,
public: {
jars: ["sdk_library/public/mycoreplatform-stubs.jar"],
stub_srcs: ["sdk_library/public/mycoreplatform_stub_sources"],
@@ -315,7 +383,7 @@
name: "mysdk_mybootclasspathfragment@current",
sdk_member_name: "mybootclasspathfragment",
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
+ apex_available: ["myapex"],
contents: [
"mysdk_mybootlib@current",
"mysdk_myothersdklibrary@current",
@@ -339,7 +407,7 @@
name: "mysdk_mybootlib@current",
sdk_member_name: "mybootlib",
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
+ apex_available: ["myapex"],
jars: ["java/mybootlib.jar"],
}
@@ -347,8 +415,9 @@
name: "mysdk_myothersdklibrary@current",
sdk_member_name: "myothersdklibrary",
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- shared_library: false,
+ apex_available: ["myapex"],
+ shared_library: true,
+ compile_dex: true,
public: {
jars: ["sdk_library/public/myothersdklibrary-stubs.jar"],
stub_srcs: ["sdk_library/public/myothersdklibrary_stub_sources"],
@@ -362,7 +431,7 @@
name: "mysdk_mysdklibrary@current",
sdk_member_name: "mysdklibrary",
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
+ apex_available: ["myapex"],
shared_library: false,
public: {
jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
@@ -377,8 +446,9 @@
name: "mysdk_mycoreplatform@current",
sdk_member_name: "mycoreplatform",
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- shared_library: false,
+ apex_available: ["myapex"],
+ shared_library: true,
+ compile_dex: true,
public: {
jars: ["sdk_library/public/mycoreplatform-stubs.jar"],
stub_srcs: ["sdk_library/public/mycoreplatform_stub_sources"],
@@ -416,7 +486,154 @@
.intermediates/mycoreplatform.stubs/android_common/javac/mycoreplatform.stubs.jar -> sdk_library/public/mycoreplatform-stubs.jar
.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt
.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),
+ )
}
// Test that bootclasspath_fragment works with sdk.
@@ -482,7 +699,12 @@
java.PrepareForTestWithJavaDefaultModules,
java.PrepareForTestWithJavaSdkLibraryFiles,
java.FixtureWithLastReleaseApis("mysdklibrary"),
+ java.FixtureConfigureUpdatableBootJars("myapex:mybootlib"),
prepareForSdkTestWithApex,
+
+ // Add a platform_bootclasspath that depends on the fragment.
+ fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"),
+
android.MockFS{
"my-blocked.txt": nil,
"my-max-target-o-low-priority.txt": nil,
@@ -549,6 +771,7 @@
sdk_version: "none",
min_sdk_version: "1",
compile_dex: true,
+ permitted_packages: ["mybootlib"],
}
java_sdk_library {
@@ -560,6 +783,9 @@
`),
).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.
@@ -633,5 +859,8 @@
.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
`),
+ snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
+ snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
+ snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
)
}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 6f769a3..a2cfe6d 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -663,16 +663,28 @@
}
func TestSnapshotWithJavaSystemModules(t *testing.T) {
- result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_header_libs: ["exported-system-module"],
+ java_sdk_libs: ["myjavalib"],
java_system_modules: ["my-system-modules"],
}
+ java_sdk_library {
+ name: "myjavalib",
+ apex_available: ["//apex_available:anyapex"],
+ srcs: ["Test.java"],
+ sdk_version: "current",
+ shared_library: false,
+ public: {
+ enabled: true,
+ },
+ }
+
java_system_modules {
name: "my-system-modules",
- libs: ["system-module", "exported-system-module"],
+ libs: ["system-module", "exported-system-module", "myjavalib.stubs"],
}
java_library {
@@ -710,6 +722,21 @@
jars: ["java/system-module.jar"],
}
+java_sdk_library_import {
+ name: "myjavalib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:anyapex"],
+ shared_library: false,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "current",
+ },
+}
+
java_system_modules_import {
name: "my-system-modules",
prefer: false,
@@ -717,6 +744,7 @@
libs: [
"mysdk_system-module",
"exported-system-module",
+ "myjavalib.stubs",
],
}
`),
@@ -739,6 +767,21 @@
jars: ["java/system-module.jar"],
}
+java_sdk_library_import {
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:anyapex"],
+ shared_library: false,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "current",
+ },
+}
+
java_system_modules_import {
name: "mysdk_my-system-modules@current",
sdk_member_name: "my-system-modules",
@@ -746,6 +789,7 @@
libs: [
"mysdk_system-module@current",
"mysdk_exported-system-module@current",
+ "mysdk_myjavalib.stubs@current",
],
}
@@ -753,12 +797,16 @@
name: "mysdk@current",
visibility: ["//visibility:public"],
java_header_libs: ["mysdk_exported-system-module@current"],
+ java_sdk_libs: ["mysdk_myjavalib@current"],
java_system_modules: ["mysdk_my-system-modules@current"],
}
`),
checkAllCopyRules(`
.intermediates/exported-system-module/android_common/turbine-combined/exported-system-module.jar -> java/exported-system-module.jar
.intermediates/system-module/android_common/turbine-combined/system-module.jar -> java/system-module.jar
+.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
`),
)
}
@@ -1085,7 +1133,14 @@
checkMergeZips(
".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
".intermediates/mysdk/common_os/tmp/sdk_library/system/myjavalib_stub_sources.zip",
- ".intermediates/mysdk/common_os/tmp/sdk_library/test/myjavalib_stub_sources.zip"),
+ ".intermediates/mysdk/common_os/tmp/sdk_library/test/myjavalib_stub_sources.zip",
+ ),
+ snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
+ // Make sure that the name of the child modules created by a versioned java_sdk_library_import
+ // module is correct, i.e. the suffix is added before the version and not after.
+ result.Module("mysdk_myjavalib.stubs@current", "android_common")
+ result.Module("mysdk_myjavalib.stubs.source@current", "android_common")
+ }),
)
}
diff --git a/sdk/update.go b/sdk/update.go
index 36b564f..b146b62 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -22,7 +22,6 @@
"android/soong/apex"
"android/soong/cc"
-
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -114,8 +113,16 @@
gc.indentLevel--
}
-func (gc *generatedContents) Printfln(format string, args ...interface{}) {
- fmt.Fprintf(&(gc.content), strings.Repeat(" ", gc.indentLevel)+format+"\n", args...)
+// IndentedPrintf will add spaces to indent the line to the appropriate level before printing the
+// arguments.
+func (gc *generatedContents) IndentedPrintf(format string, args ...interface{}) {
+ fmt.Fprintf(&(gc.content), strings.Repeat(" ", gc.indentLevel)+format, args...)
+}
+
+// UnindentedPrintf does not add spaces to indent the line to the appropriate level before printing
+// the arguments.
+func (gc *generatedContents) UnindentedPrintf(format string, args ...interface{}) {
+ fmt.Fprintf(&(gc.content), format, args...)
}
func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderContext, implicits android.Paths) {
@@ -140,8 +147,8 @@
// Collect all the members.
//
-// Updates the sdk module with a list of sdkMemberVariantDeps and details as to which multilibs
-// (32/64/both) are used by this sdk variant.
+// Updates the sdk module with a list of sdkMemberVariantDep instances and details as to which
+// multilibs (32/64/both) are used by this sdk variant.
func (s *sdk) collectMembers(ctx android.ModuleContext) {
s.multilibUsages = multilibNone
ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
@@ -149,6 +156,11 @@
if memberTag, ok := tag.(android.SdkMemberTypeDependencyTag); ok {
memberType := memberTag.SdkMemberType(child)
+ // If a nil SdkMemberType was returned then this module should not be added to the sdk.
+ if memberType == nil {
+ return false
+ }
+
// Make sure that the resolved module is allowed in the member list property.
if !memberType.IsInstance(child) {
ctx.ModuleErrorf("module %q is not valid in property %s", ctx.OtherModuleName(child), memberType.SdkPropertyName())
@@ -157,8 +169,15 @@
// Keep track of which multilib variants are used by the sdk.
s.multilibUsages = s.multilibUsages.addArchType(child.Target().Arch.ArchType)
+ var exportedComponentsInfo android.ExportedComponentsInfo
+ if ctx.OtherModuleHasProvider(child, android.ExportedComponentsInfoProvider) {
+ exportedComponentsInfo = ctx.OtherModuleProvider(child, android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo)
+ }
+
export := memberTag.ExportMember()
- s.memberVariantDeps = append(s.memberVariantDeps, sdkMemberVariantDep{s, memberType, child.(android.SdkAware), export})
+ s.memberVariantDeps = append(s.memberVariantDeps, sdkMemberVariantDep{
+ s, memberType, child.(android.SdkAware), export, exportedComponentsInfo,
+ })
// Recurse down into the member's dependencies as it may have dependencies that need to be
// automatically added to the sdk.
@@ -245,26 +264,41 @@
// the contents (header files, stub libraries, etc) into the zip file.
func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) android.OutputPath {
- allMembersByName := make(map[string]struct{})
- exportedMembersByName := make(map[string]struct{})
+ // Aggregate all the sdkMemberVariantDep instances from all the sdk variants.
hasLicenses := false
var memberVariantDeps []sdkMemberVariantDep
for _, sdkVariant := range sdkVariants {
memberVariantDeps = append(memberVariantDeps, sdkVariant.memberVariantDeps...)
+ }
- // Record the names of all the members, both explicitly specified and implicitly
- // included.
- for _, memberVariantDep := range sdkVariant.memberVariantDeps {
- name := memberVariantDep.variant.Name()
- allMembersByName[name] = struct{}{}
+ // Filter out any sdkMemberVariantDep that is a component of another.
+ memberVariantDeps = filterOutComponents(ctx, memberVariantDeps)
- if memberVariantDep.export {
- exportedMembersByName[name] = struct{}{}
- }
+ // Record the names of all the members, both explicitly specified and implicitly
+ // included.
+ allMembersByName := make(map[string]struct{})
+ exportedMembersByName := make(map[string]struct{})
- if memberVariantDep.memberType == android.LicenseModuleSdkMemberType {
- hasLicenses = true
- }
+ addMember := func(name string, export bool) {
+ allMembersByName[name] = struct{}{}
+ if export {
+ exportedMembersByName[name] = struct{}{}
+ }
+ }
+
+ for _, memberVariantDep := range memberVariantDeps {
+ name := memberVariantDep.variant.Name()
+ export := memberVariantDep.export
+
+ addMember(name, export)
+
+ // Add any components provided by the module.
+ for _, component := range memberVariantDep.exportedComponentsInfo.Components {
+ addMember(component, export)
+ }
+
+ if memberVariantDep.memberType == android.LicenseModuleSdkMemberType {
+ hasLicenses = true
}
}
@@ -423,6 +457,47 @@
return outputZipFile
}
+// filterOutComponents removes any item from the deps list that is a component of another item in
+// the deps list, e.g. if the deps list contains "foo" and "foo.stubs" which is component of "foo"
+// then it will remove "foo.stubs" from the deps.
+func filterOutComponents(ctx android.ModuleContext, deps []sdkMemberVariantDep) []sdkMemberVariantDep {
+ // Collate the set of components that all the modules added to the sdk provide.
+ components := map[string]*sdkMemberVariantDep{}
+ for i, _ := range deps {
+ dep := &deps[i]
+ for _, c := range dep.exportedComponentsInfo.Components {
+ components[c] = dep
+ }
+ }
+
+ // If no module provides components then return the input deps unfiltered.
+ if len(components) == 0 {
+ return deps
+ }
+
+ filtered := make([]sdkMemberVariantDep, 0, len(deps))
+ for _, dep := range deps {
+ name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(dep.variant))
+ if owner, ok := components[name]; ok {
+ // This is a component of another module that is a member of the sdk.
+
+ // If the component is exported but the owning module is not then the configuration is not
+ // supported.
+ if dep.export && !owner.export {
+ ctx.ModuleErrorf("Module %s is internal to the SDK but provides component %s which is used outside the SDK")
+ continue
+ }
+
+ // This module must not be added to the list of members of the sdk as that would result in a
+ // duplicate module in the sdk snapshot.
+ continue
+ }
+
+ filtered = append(filtered, dep)
+ }
+ return filtered
+}
+
// addSnapshotModule adds the sdk_snapshot/module_exports_snapshot module to the builder.
func (s *sdk) addSnapshotModule(ctx android.ModuleContext, builder *snapshotBuilder, sdkVariants []*sdk, memberVariantDeps []sdkMemberVariantDep) {
bpFile := builder.bpFile
@@ -742,13 +817,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 +834,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 +851,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 +862,94 @@
switch v := value.(type) {
case *bpPropertySet:
addComment(name)
- contents.Printfln("%s: {", name)
+ contents.IndentedPrintf("%s: {\n", name)
outputPropertySet(contents, v)
- contents.Printfln("},")
+ contents.IndentedPrintf("},\n")
}
}
contents.Dedent()
}
+// outputNamedValue outputs a value that has an associated name. The name will be indented, followed
+// by the value and then followed by a , and a newline.
+func outputNamedValue(contents *generatedContents, name string, value reflect.Value) {
+ contents.IndentedPrintf("%s: ", name)
+ outputUnnamedValue(contents, value)
+ contents.UnindentedPrintf(",\n")
+}
+
+// outputUnnamedValue outputs a single value. The value is not indented and is not followed by
+// either a , or a newline. With multi-line values, e.g. slices, all but the first line will be
+// indented and all but the last line will end with a newline.
+func outputUnnamedValue(contents *generatedContents, value reflect.Value) {
+ valueType := value.Type()
+ switch valueType.Kind() {
+ case reflect.Bool:
+ contents.UnindentedPrintf("%t", value.Bool())
+
+ case reflect.String:
+ contents.UnindentedPrintf("%q", value)
+
+ case reflect.Ptr:
+ outputUnnamedValue(contents, value.Elem())
+
+ case reflect.Slice:
+ length := value.Len()
+ if length == 0 {
+ contents.UnindentedPrintf("[]")
+ } else {
+ firstValue := value.Index(0)
+ if length == 1 && !multiLineValue(firstValue) {
+ contents.UnindentedPrintf("[")
+ outputUnnamedValue(contents, firstValue)
+ contents.UnindentedPrintf("]")
+ } else {
+ contents.UnindentedPrintf("[\n")
+ contents.Indent()
+ for i := 0; i < length; i++ {
+ itemValue := value.Index(i)
+ contents.IndentedPrintf("")
+ outputUnnamedValue(contents, itemValue)
+ contents.UnindentedPrintf(",\n")
+ }
+ contents.Dedent()
+ contents.IndentedPrintf("]")
+ }
+ }
+
+ case reflect.Struct:
+ // Avoid unlimited recursion by requiring every structure to implement android.BpPrintable.
+ v := value.Interface()
+ if _, ok := v.(android.BpPrintable); !ok {
+ panic(fmt.Errorf("property value %#v of type %T does not implement android.BpPrintable", v, v))
+ }
+ contents.UnindentedPrintf("{\n")
+ contents.Indent()
+ for f := 0; f < valueType.NumField(); f++ {
+ fieldType := valueType.Field(f)
+ if fieldType.Anonymous {
+ continue
+ }
+ fieldValue := value.Field(f)
+ fieldName := fieldType.Name
+ propertyName := proptools.PropertyNameForField(fieldName)
+ outputNamedValue(contents, propertyName, fieldValue)
+ }
+ contents.Dedent()
+ contents.IndentedPrintf("}")
+
+ default:
+ panic(fmt.Errorf("Unknown type: %T of value %#v", value, value))
+ }
+}
+
+// multiLineValue returns true if the supplied value may require multiple lines in the output.
+func multiLineValue(value reflect.Value) bool {
+ kind := value.Kind()
+ return kind == reflect.Slice || kind == reflect.Struct
+}
+
func (s *sdk) GetAndroidBpContentsForTests() string {
contents := &generatedContents{}
generateBpContents(contents, s.builderForTests.bpFile)
@@ -1086,9 +1219,18 @@
type sdkMemberVariantDep struct {
// The sdk variant that depends (possibly indirectly) on the member variant.
sdkVariant *sdk
+
+ // The type of sdk member the variant is to be treated as.
memberType android.SdkMemberType
- variant android.SdkAware
- export bool
+
+ // The variant that is added to the sdk.
+ variant android.SdkAware
+
+ // True if the member should be exported, i.e. accessible, from outside the sdk.
+ export bool
+
+ // The names of additional component modules provided by the variant.
+ exportedComponentsInfo android.ExportedComponentsInfo
}
var _ android.SdkMember = (*sdkMember)(nil)
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index f1c2d0d..a29d4c3 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -145,6 +145,9 @@
// If set to true, allow this module to be dexed and installed on devices.
Installable *bool
+ // Make this module available when building for ramdisk
+ Ramdisk_available *bool
+
// Make this module available when building for recovery
Recovery_available *bool
@@ -396,6 +399,7 @@
Recovery_available *bool
Vendor_available *bool
Product_available *bool
+ Ramdisk_available *bool
Host_supported *bool
Apex_available []string
Min_sdk_version *string
@@ -475,6 +479,7 @@
ccProps.Recovery_available = m.properties.Recovery_available
ccProps.Vendor_available = m.properties.Vendor_available
ccProps.Product_available = m.properties.Product_available
+ ccProps.Ramdisk_available = m.properties.Ramdisk_available
ccProps.Host_supported = m.properties.Host_supported
ccProps.Apex_available = m.ApexProperties.Apex_available
ccProps.Min_sdk_version = m.properties.Cpp.Min_sdk_version
diff --git a/ui/build/build.go b/ui/build/build.go
index 8f050d9..1187aa2 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -274,6 +274,11 @@
// Return early, if we're using Soong as solely the generator of BUILD files.
return
}
+
+ if config.bazelBuildMode() == generateJsonModuleGraph {
+ // Return early, if we're using Soong as solely the generator of the JSON module graph
+ return
+ }
}
if what&RunKati != 0 {
diff --git a/ui/build/config.go b/ui/build/config.go
index 220e734..4806721 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -108,6 +108,9 @@
// Only generate build files (in a subdirectory of the out directory) and exit.
generateBuildFiles
+ // Only generate the Soong json module graph for use with jq, and exit.
+ generateJsonModuleGraph
+
// Generate synthetic build files and incorporate these files into a build which
// partially uses Bazel. Build metadata may come from Android.bp or BUILD files.
mixedBuild
@@ -573,6 +576,9 @@
} else if arg == "--skip-ninja" {
c.skipNinja = true
} else if arg == "--skip-make" {
+ // TODO(ccross): deprecate this, it has confusing behaviors. It doesn't run kati,
+ // but it does run a Kati ninja file if the .kati_enabled marker file was created
+ // by a previous build.
c.skipConfig = true
c.skipKati = true
} else if arg == "--skip-kati" {
@@ -581,6 +587,8 @@
} else if arg == "--soong-only" {
c.skipKati = true
c.skipKatiNinja = true
+ } else if arg == "--skip-config" {
+ c.skipConfig = true
} else if arg == "--skip-soong-tests" {
c.skipSoongTests = true
} else if len(arg) > 0 && arg[0] == '-' {
@@ -931,6 +939,8 @@
return mixedBuild
} else if c.Environment().IsEnvTrue("GENERATE_BAZEL_FILES") {
return generateBuildFiles
+ } else if v, ok := c.Environment().Get("SOONG_DUMP_JSON_MODULE_GRAPH"); ok && v != "" {
+ return generateJsonModuleGraph
} else {
return noBazel
}
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 7128414..a40457f 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()
@@ -313,8 +333,9 @@
}
func shouldCollectBuildSoongMetrics(config Config) bool {
- // Do not collect metrics protobuf if the soong_build binary ran as the bp2build converter.
- return config.bazelBuildMode() != generateBuildFiles
+ // Do not collect metrics protobuf if the soong_build binary ran as the
+ // bp2build converter or the JSON graph dump.
+ return config.bazelBuildMode() != generateBuildFiles && config.bazelBuildMode() != generateJsonModuleGraph
}
func loadSoongBuildMetrics(ctx Context, config Config) *soong_metrics_proto.SoongBuildMetrics {