Merge "support errorprone javacflags"
diff --git a/Android.bp b/Android.bp
index 380a388..42a8e5c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -61,6 +61,9 @@
linux_bionic: {
enabled: true,
},
+ linux_musl: {
+ enabled: false,
+ },
linux_glibc: {
enabled: false,
},
@@ -82,6 +85,9 @@
linux_bionic: {
enabled: true,
},
+ linux_musl: {
+ enabled: false,
+ },
linux_glibc: {
enabled: false,
},
diff --git a/android/bazel.go b/android/bazel.go
index fafb68b..e3fb0a6 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -570,12 +570,9 @@
"art-script", // depends on unconverted modules: dalvikvm, dex2oat
"dex2oat-script", // depends on unconverted modules: dex2oat
- "prebuilt_car-ui-androidx-core-common", // b/224773339, genrule dependency creates an .aar, not a .jar
- "prebuilt_art-module-host-exports_okhttp-norepackage@current", // aosp/1999250, needs Jars (arch variant)
- "prebuilt_conscrypt-unbundled", // aosp/1999250, needs Jars (arch variant)
- "prebuilt_conscrypt-module-host-exports_conscrypt-unbundled@current", // aosp/1999250, needs Jars (arch variant)
- "prebuilt_platform-robolectric-4.4-prebuilt", // aosp/1999250, needs .aar support in Jars
- "prebuilt_platform-robolectric-4.5.1-prebuilt", // aosp/1999250, needs .aar support in Jars
+ "prebuilt_car-ui-androidx-core-common", // b/224773339, genrule dependency creates an .aar, not a .jar
+ "prebuilt_platform-robolectric-4.4-prebuilt", // aosp/1999250, needs .aar support in Jars
+ "prebuilt_platform-robolectric-4.5.1-prebuilt", // aosp/1999250, needs .aar support in Jars
}
// Per-module denylist of cc_library modules to only generate the static
diff --git a/android/neverallow.go b/android/neverallow.go
index 6f9ae58..f87cebb 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -249,7 +249,7 @@
continue
}
- if !n.appliesToProperties(ctx, properties) {
+ if !n.appliesToProperties(properties) {
continue
}
@@ -261,20 +261,12 @@
continue
}
- if !n.appliesToBootclasspathJar(ctx) {
- continue
- }
-
ctx.ModuleErrorf("violates " + n.String())
}
}
-type ValueMatcherContext interface {
- Config() Config
-}
-
type ValueMatcher interface {
- Test(ValueMatcherContext, string) bool
+ Test(string) bool
String() string
}
@@ -282,7 +274,7 @@
expected string
}
-func (m *equalMatcher) Test(ctx ValueMatcherContext, value string) bool {
+func (m *equalMatcher) Test(value string) bool {
return m.expected == value
}
@@ -293,7 +285,7 @@
type anyMatcher struct {
}
-func (m *anyMatcher) Test(ctx ValueMatcherContext, value string) bool {
+func (m *anyMatcher) Test(value string) bool {
return true
}
@@ -307,7 +299,7 @@
prefix string
}
-func (m *startsWithMatcher) Test(ctx ValueMatcherContext, value string) bool {
+func (m *startsWithMatcher) Test(value string) bool {
return strings.HasPrefix(value, m.prefix)
}
@@ -319,7 +311,7 @@
re *regexp.Regexp
}
-func (m *regexMatcher) Test(ctx ValueMatcherContext, value string) bool {
+func (m *regexMatcher) Test(value string) bool {
return m.re.MatchString(value)
}
@@ -331,7 +323,7 @@
allowed []string
}
-func (m *notInListMatcher) Test(ctx ValueMatcherContext, value string) bool {
+func (m *notInListMatcher) Test(value string) bool {
return !InList(value, m.allowed)
}
@@ -341,7 +333,7 @@
type isSetMatcher struct{}
-func (m *isSetMatcher) Test(ctx ValueMatcherContext, value string) bool {
+func (m *isSetMatcher) Test(value string) bool {
return value != ""
}
@@ -351,19 +343,6 @@
var isSetMatcherInstance = &isSetMatcher{}
-type sdkVersionMatcher struct {
- condition func(ctx ValueMatcherContext, spec SdkSpec) bool
- description string
-}
-
-func (m *sdkVersionMatcher) Test(ctx ValueMatcherContext, value string) bool {
- return m.condition(ctx, SdkSpecFromWithConfig(ctx.Config(), value))
-}
-
-func (m *sdkVersionMatcher) String() string {
- return ".sdk-version(" + m.description + ")"
-}
-
type ruleProperty struct {
fields []string // e.x.: Vndk.Enabled
matcher ValueMatcher
@@ -397,8 +376,6 @@
NotModuleType(types ...string) Rule
- BootclasspathJar() Rule
-
With(properties, value string) Rule
WithMatcher(properties string, matcher ValueMatcher) Rule
@@ -514,12 +491,6 @@
return r
}
-// BootclasspathJar whether this rule only applies to Jars in the Bootclasspath
-func (r *rule) BootclasspathJar() Rule {
- r.onlyBootclasspathJar = true
- return r
-}
-
func (r *rule) String() string {
s := []string{"neverallow requirements. Not allowed:"}
if len(r.paths) > 0 {
@@ -537,9 +508,6 @@
if len(r.osClasses) > 0 {
s = append(s, fmt.Sprintf("os class(es): %q", r.osClasses))
}
- if r.onlyBootclasspathJar {
- s = append(s, "in bootclasspath jar")
- }
if len(r.unlessPaths) > 0 {
s = append(s, fmt.Sprintf("EXCEPT in dirs: %q", r.unlessPaths))
}
@@ -580,14 +548,6 @@
return matches
}
-func (r *rule) appliesToBootclasspathJar(ctx BottomUpMutatorContext) bool {
- if !r.onlyBootclasspathJar {
- return true
- }
-
- return InList(ctx.ModuleName(), ctx.Config().BootJars())
-}
-
func (r *rule) appliesToOsClass(osClass OsClass) bool {
if len(r.osClasses) == 0 {
return true
@@ -606,10 +566,9 @@
return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
}
-func (r *rule) appliesToProperties(ctx ValueMatcherContext,
- properties []interface{}) bool {
- includeProps := hasAllProperties(ctx, properties, r.props)
- excludeProps := hasAnyProperty(ctx, properties, r.unlessProps)
+func (r *rule) appliesToProperties(properties []interface{}) bool {
+ includeProps := hasAllProperties(properties, r.props)
+ excludeProps := hasAnyProperty(properties, r.unlessProps)
return includeProps && !excludeProps
}
@@ -629,16 +588,6 @@
return ¬InListMatcher{allowed}
}
-func LessThanSdkVersion(sdk string) ValueMatcher {
- return &sdkVersionMatcher{
- condition: func(ctx ValueMatcherContext, spec SdkSpec) bool {
- return spec.ApiLevel.LessThan(
- SdkSpecFromWithConfig(ctx.Config(), sdk).ApiLevel)
- },
- description: "lessThan=" + sdk,
- }
-}
-
// assorted utils
func cleanPaths(paths []string) []string {
@@ -657,28 +606,25 @@
return names
}
-func hasAnyProperty(ctx ValueMatcherContext, properties []interface{},
- props []ruleProperty) bool {
+func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
for _, v := range props {
- if hasProperty(ctx, properties, v) {
+ if hasProperty(properties, v) {
return true
}
}
return false
}
-func hasAllProperties(ctx ValueMatcherContext, properties []interface{},
- props []ruleProperty) bool {
+func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
for _, v := range props {
- if !hasProperty(ctx, properties, v) {
+ if !hasProperty(properties, v) {
return false
}
}
return true
}
-func hasProperty(ctx ValueMatcherContext, properties []interface{},
- prop ruleProperty) bool {
+func hasProperty(properties []interface{}, prop ruleProperty) bool {
for _, propertyStruct := range properties {
propertiesValue := reflect.ValueOf(propertyStruct).Elem()
for _, v := range prop.fields {
@@ -692,7 +638,7 @@
}
check := func(value string) bool {
- return prop.matcher.Test(ctx, value)
+ return prop.matcher.Test(value)
}
if matchValue(propertiesValue, check) {
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 59016d4..8afe9e0 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -327,48 +327,6 @@
"Only boot images may be imported as a makefile goal.",
},
},
- {
- name: "min_sdk too low",
- fs: map[string][]byte{
- "Android.bp": []byte(`
- java_library {
- name: "min_sdk_too_low",
- min_sdk_version: "30",
- }`),
- },
- rules: []Rule{
- NeverAllow().WithMatcher("min_sdk_version", LessThanSdkVersion("31")),
- },
- expectedErrors: []string{
- "module \"min_sdk_too_low\": violates neverallow",
- },
- },
- {
- name: "min_sdk high enough",
- fs: map[string][]byte{
- "Android.bp": []byte(`
- java_library {
- name: "min_sdk_high_enough",
- min_sdk_version: "31",
- }`),
- },
- rules: []Rule{
- NeverAllow().WithMatcher("min_sdk_version", LessThanSdkVersion("31")),
- },
- },
- {
- name: "current min_sdk high enough",
- fs: map[string][]byte{
- "Android.bp": []byte(`
- java_library {
- name: "current_min_sdk_high_enough",
- min_sdk_version: "current",
- }`),
- },
- rules: []Rule{
- NeverAllow().WithMatcher("min_sdk_version", LessThanSdkVersion("31")),
- },
- },
}
var prepareForNeverAllowTest = GroupFixturePreparers(
@@ -452,10 +410,9 @@
}
type mockJavaLibraryProperties struct {
- Libs []string
- Min_sdk_version *string
- Sdk_version *string
- Uncompress_dex *bool
+ Libs []string
+ Sdk_version *string
+ Uncompress_dex *bool
}
type mockJavaLibraryModule struct {
diff --git a/android/notices.go b/android/notices.go
index 194a734..2a4c17c 100644
--- a/android/notices.go
+++ b/android/notices.go
@@ -15,93 +15,9 @@
package android
import (
- "path/filepath"
"strings"
-
- "github.com/google/blueprint"
)
-func init() {
- pctx.SourcePathVariable("merge_notices", "build/soong/scripts/mergenotice.py")
- pctx.SourcePathVariable("generate_notice", "build/soong/scripts/generate-notice-files.py")
-
- pctx.HostBinToolVariable("minigzip", "minigzip")
-}
-
-type NoticeOutputs struct {
- Merged OptionalPath
- TxtOutput OptionalPath
- HtmlOutput OptionalPath
- HtmlGzOutput OptionalPath
-}
-
-var (
- mergeNoticesRule = pctx.AndroidStaticRule("mergeNoticesRule", blueprint.RuleParams{
- Command: `${merge_notices} --output $out $in`,
- CommandDeps: []string{"${merge_notices}"},
- Description: "merge notice files into $out",
- })
-
- generateNoticeRule = pctx.AndroidStaticRule("generateNoticeRule", blueprint.RuleParams{
- Command: `rm -rf $$(dirname $txtOut) $$(dirname $htmlOut) $$(dirname $out) && ` +
- `mkdir -p $$(dirname $txtOut) $$(dirname $htmlOut) $$(dirname $out) && ` +
- `${generate_notice} --text-output $txtOut --html-output $htmlOut -t "$title" -s $inputDir && ` +
- `${minigzip} -c $htmlOut > $out`,
- CommandDeps: []string{"${generate_notice}", "${minigzip}"},
- Description: "produce notice file $out",
- }, "txtOut", "htmlOut", "title", "inputDir")
-)
-
-func MergeNotices(ctx ModuleContext, mergedNotice WritablePath, noticePaths []Path) {
- ctx.Build(pctx, BuildParams{
- Rule: mergeNoticesRule,
- Description: "merge notices",
- Inputs: noticePaths,
- Output: mergedNotice,
- })
-}
-
-func BuildNoticeOutput(ctx ModuleContext, installPath InstallPath, installFilename string,
- noticePaths []Path) NoticeOutputs {
- // Merge all NOTICE files into one.
- // TODO(jungjw): We should just produce a well-formatted NOTICE.html file in a single pass.
- //
- // generate-notice-files.py, which processes the merged NOTICE file, has somewhat strict rules
- // about input NOTICE file paths.
- // 1. Their relative paths to the src root become their NOTICE index titles. We want to use
- // on-device paths as titles, and so output the merged NOTICE file the corresponding location.
- // 2. They must end with .txt extension. Otherwise, they're ignored.
- noticeRelPath := InstallPathToOnDevicePath(ctx, installPath.Join(ctx, installFilename+".txt"))
- mergedNotice := PathForModuleOut(ctx, filepath.Join("NOTICE_FILES/src", noticeRelPath))
- MergeNotices(ctx, mergedNotice, noticePaths)
-
- // Transform the merged NOTICE file into a gzipped HTML file.
- txtOuptut := PathForModuleOut(ctx, "NOTICE_txt", "NOTICE.txt")
- htmlOutput := PathForModuleOut(ctx, "NOTICE_html", "NOTICE.html")
- htmlGzOutput := PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
- title := "Notices for " + ctx.ModuleName()
- ctx.Build(pctx, BuildParams{
- Rule: generateNoticeRule,
- Description: "generate notice output",
- Input: mergedNotice,
- Output: htmlGzOutput,
- ImplicitOutputs: WritablePaths{txtOuptut, htmlOutput},
- Args: map[string]string{
- "txtOut": txtOuptut.String(),
- "htmlOut": htmlOutput.String(),
- "title": title,
- "inputDir": PathForModuleOut(ctx, "NOTICE_FILES/src").String(),
- },
- })
-
- return NoticeOutputs{
- Merged: OptionalPathForPath(mergedNotice),
- TxtOutput: OptionalPathForPath(txtOuptut),
- HtmlOutput: OptionalPathForPath(htmlOutput),
- HtmlGzOutput: OptionalPathForPath(htmlGzOutput),
- }
-}
-
// BuildNoticeTextOutputFromLicenseMetadata writes out a notice text file based on the module's
// generated license metadata file.
func BuildNoticeTextOutputFromLicenseMetadata(ctx ModuleContext, outputFile WritablePath) {
@@ -112,5 +28,18 @@
FlagWithOutput("-o ", outputFile).
FlagWithDepFile("-d ", depsFile).
Input(ctx.Module().base().licenseMetadataFile)
- rule.Build("container_notice", "container notice file")
+ rule.Build("text_notice", "container notice file")
+}
+
+// BuildNoticeHtmlOutputFromLicenseMetadata writes out a notice text file based on the module's
+// generated license metadata file.
+func BuildNoticeHtmlOutputFromLicenseMetadata(ctx ModuleContext, outputFile WritablePath) {
+ depsFile := outputFile.ReplaceExtension(ctx, strings.TrimPrefix(outputFile.Ext()+".d", "."))
+ rule := NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ BuiltTool("htmlnotice").
+ FlagWithOutput("-o ", outputFile).
+ FlagWithDepFile("-d ", depsFile).
+ Input(ctx.Module().base().licenseMetadataFile)
+ rule.Build("html_notice", "container notice file")
}
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 059b4d7..e094a12 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -396,10 +396,6 @@
}
a.writeRequiredModules(w, moduleNames)
- if a.mergedNotices.Merged.Valid() {
- fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", a.mergedNotices.Merged.Path().String())
- }
-
fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
if apexType == imageApex {
diff --git a/apex/apex.go b/apex/apex.go
index 6d8a67a..2fe17da 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -414,8 +414,8 @@
// Processed file_contexts files
fileContexts android.WritablePath
- // Struct holding the merged notice file paths in different formats
- mergedNotices android.NoticeOutputs
+ // Path to notice file in html.gz format.
+ htmlGzNotice android.WritablePath
// The built APEX file. This is the main product.
// Could be .apex or .capex
@@ -487,11 +487,10 @@
// for each of the files in case when the APEX is flattened.
type apexFile struct {
// buildFile is put in the installDir inside the APEX.
- builtFile android.Path
- noticeFiles android.Paths
- installDir string
- customStem string
- symlinks []string // additional symlinks
+ builtFile android.Path
+ installDir string
+ customStem string
+ symlinks []string // additional symlinks
// Info for Android.mk Module name of `module` in AndroidMk. Note the generated AndroidMk
// module for apexFile is named something like <AndroidMk module name>.<apex name>[<apex
@@ -528,7 +527,6 @@
module: module,
}
if module != nil {
- ret.noticeFiles = module.NoticeFiles()
ret.moduleDir = ctx.OtherModuleDir(module)
ret.requiredModuleNames = module.RequiredModuleNames()
ret.targetRequiredModuleNames = module.TargetRequiredModuleNames()
@@ -3280,21 +3278,19 @@
}
func init() {
- android.AddNeverAllowRules(createApexPermittedPackagesRules(qModulesPackages())...)
- android.AddNeverAllowRules(createApexPermittedPackagesRules(rModulesPackages())...)
+ android.AddNeverAllowRules(createBcpPermittedPackagesRules(qBcpPackages())...)
+ android.AddNeverAllowRules(createBcpPermittedPackagesRules(rBcpPackages())...)
}
-func createApexPermittedPackagesRules(modules_packages map[string][]string) []android.Rule {
- rules := make([]android.Rule, 0, len(modules_packages))
- for module_name, module_packages := range modules_packages {
+func createBcpPermittedPackagesRules(bcpPermittedPackages map[string][]string) []android.Rule {
+ rules := make([]android.Rule, 0, len(bcpPermittedPackages))
+ for jar, permittedPackages := range bcpPermittedPackages {
permittedPackagesRule := android.NeverAllow().
- BootclasspathJar().
- With("apex_available", module_name).
- WithMatcher("permitted_packages", android.NotInList(module_packages)).
- WithMatcher("min_sdk_version", android.LessThanSdkVersion("Tiramisu")).
- Because("jars that are part of the " + module_name +
- " module may only use these package prefixes: " + strings.Join(module_packages, ",") +
- " with min_sdk < T. Please consider the following alternatives:\n" +
+ With("name", jar).
+ WithMatcher("permitted_packages", android.NotInList(permittedPackages)).
+ Because(jar +
+ " bootjar may only use these package prefixes: " + strings.Join(permittedPackages, ",") +
+ ". Please consider the following alternatives:\n" +
" 1. If the offending code is from a statically linked library, consider " +
"removing that dependency and using an alternative already in the " +
"bootclasspath, or perhaps a shared library." +
@@ -3302,55 +3298,56 @@
" 3. Jarjar the offending code. Please be mindful of the potential system " +
"health implications of bundling that code, particularly if the offending jar " +
"is part of the bootclasspath.")
+
rules = append(rules, permittedPackagesRule)
}
return rules
}
-// DO NOT EDIT! These are the package prefixes that are exempted from being AOT'ed by ART on Q/R/S.
+// DO NOT EDIT! These are the package prefixes that are exempted from being AOT'ed by ART.
// Adding code to the bootclasspath in new packages will cause issues on module update.
-func qModulesPackages() map[string][]string {
+func qBcpPackages() map[string][]string {
return map[string][]string{
- "com.android.conscrypt": []string{
+ "conscrypt": []string{
"android.net.ssl",
"com.android.org.conscrypt",
},
- "com.android.media": []string{
+ "updatable-media": []string{
"android.media",
},
}
}
-// DO NOT EDIT! These are the package prefixes that are exempted from being AOT'ed by ART on R/S.
+// DO NOT EDIT! These are the package prefixes that are exempted from being AOT'ed by ART.
// Adding code to the bootclasspath in new packages will cause issues on module update.
-func rModulesPackages() map[string][]string {
+func rBcpPackages() map[string][]string {
return map[string][]string{
- "com.android.mediaprovider": []string{
+ "framework-mediaprovider": []string{
"android.provider",
},
- "com.android.permission": []string{
+ "framework-permission": []string{
"android.permission",
"android.app.role",
"com.android.permission",
"com.android.role",
},
- "com.android.sdkext": []string{
+ "framework-sdkextensions": []string{
"android.os.ext",
},
- "com.android.os.statsd": []string{
+ "framework-statsd": []string{
"android.app",
"android.os",
"android.util",
"com.android.internal.statsd",
"com.android.server.stats",
},
- "com.android.wifi": []string{
+ "framework-wifi": []string{
"com.android.server.wifi",
"com.android.wifi.x",
"android.hardware.wifi",
"android.net.wifi",
},
- "com.android.tethering": []string{
+ "framework-tethering": []string{
"android.net",
},
}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 85bd595..5706a2c 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -591,15 +591,6 @@
t.Errorf("Could not find all expected symlinks! foo: %t, foo_link_64: %t. Command was %s", found_foo, found_foo_link_64, copyCmds)
}
- mergeNoticesRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("mergeNoticesRule")
- noticeInputs := mergeNoticesRule.Inputs.Strings()
- if len(noticeInputs) != 3 {
- t.Errorf("number of input notice files: expected = 3, actual = %q", len(noticeInputs))
- }
- ensureListContains(t, noticeInputs, "NOTICE")
- ensureListContains(t, noticeInputs, "custom_notice")
- ensureListContains(t, noticeInputs, "custom_notice_for_static_lib")
-
fullDepsInfo := strings.Split(ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("depsinfo/fulllist.txt").Args["content"], "\\n")
ensureListContains(t, fullDepsInfo, " myjar(minSdkVersion:(no version)) <- myapex")
ensureListContains(t, fullDepsInfo, " mylib2(minSdkVersion:(no version)) <- mylib")
@@ -7589,7 +7580,7 @@
})
}
-func testApexPermittedPackagesRules(t *testing.T, errmsg, bp string, bootJars []string, rules []android.Rule) {
+func testBootJarPermittedPackagesRules(t *testing.T, errmsg, bp string, bootJars []string, rules []android.Rule) {
t.Helper()
bp += `
apex_key {
@@ -7628,11 +7619,11 @@
func TestApexPermittedPackagesRules(t *testing.T) {
testcases := []struct {
- name string
- expectedError string
- bp string
- bootJars []string
- modulesPackages map[string][]string
+ name string
+ expectedError string
+ bp string
+ bootJars []string
+ bcpPermittedPackages map[string][]string
}{
{
@@ -7646,7 +7637,6 @@
apex_available: ["myapex"],
sdk_version: "none",
system_modules: "none",
- min_sdk_version: "30",
}
java_library {
name: "nonbcp_lib2",
@@ -7655,25 +7645,23 @@
permitted_packages: ["a.b"],
sdk_version: "none",
system_modules: "none",
- min_sdk_version: "30",
}
apex {
name: "myapex",
- min_sdk_version: "30",
key: "myapex.key",
java_libs: ["bcp_lib1", "nonbcp_lib2"],
updatable: false,
}`,
bootJars: []string{"bcp_lib1"},
- modulesPackages: map[string][]string{
- "myapex": []string{
+ bcpPermittedPackages: map[string][]string{
+ "bcp_lib1": []string{
"foo.bar",
},
},
},
{
- name: "Bootclasspath apex jar not satisfying allowed module packages on Q.",
- expectedError: `(?s)module "bcp_lib2" .* which is restricted because jars that are part of the myapex module may only use these package prefixes: foo.bar with min_sdk < T. Please consider the following alternatives:\n 1. If the offending code is from a statically linked library, consider removing that dependency and using an alternative already in the bootclasspath, or perhaps a shared library. 2. Move the offending code into an allowed package.\n 3. Jarjar the offending code. Please be mindful of the potential system health implications of bundling that code, particularly if the offending jar is part of the bootclasspath.`,
+ name: "Bootclasspath apex jar not satisfying allowed module packages.",
+ expectedError: `(?s)module "bcp_lib2" .* which is restricted because bcp_lib2 bootjar may only use these package prefixes: foo.bar. Please consider the following alternatives:\n 1. If the offending code is from a statically linked library, consider removing that dependency and using an alternative already in the bootclasspath, or perhaps a shared library. 2. Move the offending code into an allowed package.\n 3. Jarjar the offending code. Please be mindful of the potential system health implications of bundling that code, particularly if the offending jar is part of the bootclasspath.`,
bp: `
java_library {
name: "bcp_lib1",
@@ -7682,7 +7670,6 @@
permitted_packages: ["foo.bar"],
sdk_version: "none",
system_modules: "none",
- min_sdk_version: "29",
}
java_library {
name: "bcp_lib2",
@@ -7691,102 +7678,67 @@
permitted_packages: ["foo.bar", "bar.baz"],
sdk_version: "none",
system_modules: "none",
- min_sdk_version: "29",
}
apex {
name: "myapex",
- min_sdk_version: "29",
key: "myapex.key",
java_libs: ["bcp_lib1", "bcp_lib2"],
updatable: false,
}
`,
bootJars: []string{"bcp_lib1", "bcp_lib2"},
- modulesPackages: map[string][]string{
- "myapex": []string{
+ bcpPermittedPackages: map[string][]string{
+ "bcp_lib1": []string{
+ "foo.bar",
+ },
+ "bcp_lib2": []string{
"foo.bar",
},
},
},
{
- name: "Bootclasspath apex jar not satisfying allowed module packages on R.",
- expectedError: `(?s)module "bcp_lib2" .* which is restricted because jars that are part of the myapex module may only use these package prefixes: foo.bar with min_sdk < T. Please consider the following alternatives:\n 1. If the offending code is from a statically linked library, consider removing that dependency and using an alternative already in the bootclasspath, or perhaps a shared library. 2. Move the offending code into an allowed package.\n 3. Jarjar the offending code. Please be mindful of the potential system health implications of bundling that code, particularly if the offending jar is part of the bootclasspath.`,
- bp: `
- java_library {
- name: "bcp_lib1",
- srcs: ["lib1/src/*.java"],
- apex_available: ["myapex"],
- permitted_packages: ["foo.bar"],
- sdk_version: "none",
- system_modules: "none",
- min_sdk_version: "30",
- }
- java_library {
- name: "bcp_lib2",
- srcs: ["lib2/src/*.java"],
- apex_available: ["myapex"],
- permitted_packages: ["foo.bar", "bar.baz"],
- sdk_version: "none",
- system_modules: "none",
- min_sdk_version: "30",
- }
- apex {
- name: "myapex",
- min_sdk_version: "30",
- key: "myapex.key",
- java_libs: ["bcp_lib1", "bcp_lib2"],
- updatable: false,
- }
- `,
- bootJars: []string{"bcp_lib1", "bcp_lib2"},
- modulesPackages: map[string][]string{
- "myapex": []string{
- "foo.bar",
- },
- },
- },
- {
- name: "Bootclasspath apex jar >= T not satisfying Q/R/S allowed module packages.",
+ name: "Updateable Bootclasspath apex jar not satisfying allowed module packages.",
expectedError: "",
bp: `
java_library {
- name: "bcp_lib1",
+ name: "bcp_lib_restricted",
srcs: ["lib1/src/*.java"],
apex_available: ["myapex"],
permitted_packages: ["foo.bar"],
sdk_version: "none",
+ min_sdk_version: "29",
system_modules: "none",
- min_sdk_version: "current",
}
java_library {
- name: "bcp_lib2",
+ name: "bcp_lib_unrestricted",
srcs: ["lib2/src/*.java"],
apex_available: ["myapex"],
permitted_packages: ["foo.bar", "bar.baz"],
sdk_version: "none",
+ min_sdk_version: "29",
system_modules: "none",
- min_sdk_version: "current",
}
apex {
name: "myapex",
- min_sdk_version: "current",
key: "myapex.key",
- java_libs: ["bcp_lib1", "bcp_lib2"],
- updatable: false,
+ java_libs: ["bcp_lib_restricted", "bcp_lib_unrestricted"],
+ updatable: true,
+ min_sdk_version: "29",
}
`,
bootJars: []string{"bcp_lib1", "bcp_lib2"},
- modulesPackages: map[string][]string{
- "myapex": []string{
+ bcpPermittedPackages: map[string][]string{
+ "bcp_lib1_non_updateable": []string{
"foo.bar",
},
+ // bcp_lib2_updateable has no entry here since updateable bcp can contain new packages - tracking via an allowlist is not necessary
},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
- rules := createApexPermittedPackagesRules(tc.modulesPackages)
- testApexPermittedPackagesRules(t, tc.expectedError, tc.bp, tc.bootJars, rules)
+ rules := createBcpPermittedPackagesRules(tc.bcpPermittedPackages)
+ testBootJarPermittedPackagesRules(t, tc.expectedError, tc.bp, tc.bootJars, rules)
})
}
}
diff --git a/apex/builder.go b/apex/builder.go
index 8c5f99b..ea61e1a 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -305,32 +305,6 @@
return output.OutputPath
}
-// buildNoticeFiles creates a buile rule for aggregating notice files from the modules that
-// contributes to this APEX. The notice files are merged into a big notice file.
-func (a *apexBundle) buildNoticeFiles(ctx android.ModuleContext, apexFileName string) android.NoticeOutputs {
- var noticeFiles android.Paths
-
- a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
- if externalDep {
- // As soon as the dependency graph crosses the APEX boundary, don't go further.
- return false
- }
- noticeFiles = append(noticeFiles, to.NoticeFiles()...)
- return true
- })
-
- // TODO(jiyong): why do we need this? WalkPayloadDeps should have already covered this.
- for _, fi := range a.filesInfo {
- noticeFiles = append(noticeFiles, fi.noticeFiles...)
- }
-
- if len(noticeFiles) == 0 {
- return android.NoticeOutputs{}
- }
-
- return android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.SortedUniquePaths(noticeFiles))
-}
-
// buildInstalledFilesFile creates a build rule for the installed-files.txt file where the list of
// files included in this APEX is shown. The text file is dist'ed so that people can see what's
// included in the APEX without actually downloading and extracting it.
@@ -642,12 +616,17 @@
optFlags = append(optFlags, "--logging_parent ", a.overridableProperties.Logging_parent)
}
- a.mergedNotices = a.buildNoticeFiles(ctx, a.Name()+suffix)
- if a.mergedNotices.HtmlGzOutput.Valid() {
- // If there's a NOTICE file, embed it as an asset file in the APEX.
- implicitInputs = append(implicitInputs, a.mergedNotices.HtmlGzOutput.Path())
- optFlags = append(optFlags, "--assets_dir "+filepath.Dir(a.mergedNotices.HtmlGzOutput.String()))
- }
+ // Create a NOTICE file, and embed it as an asset file in the APEX.
+ a.htmlGzNotice = android.PathForModuleOut(ctx, "NOTICE.html.gz")
+ android.BuildNoticeHtmlOutputFromLicenseMetadata(ctx, a.htmlGzNotice)
+ noticeAssetPath := android.PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
+ builder := android.NewRuleBuilder(pctx, ctx)
+ builder.Command().Text("cp").
+ Input(a.htmlGzNotice).
+ Output(noticeAssetPath)
+ builder.Build("notice_dir", "Building notice dir")
+ implicitInputs = append(implicitInputs, noticeAssetPath)
+ optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeAssetPath.String()))
if (moduleMinSdkVersion.GreaterThan(android.SdkVersion_Android10) && !a.shouldGenerateHashtree()) && !compressionEnabled {
// Apexes which are supposed to be installed in builtin dirs(/system, etc)
diff --git a/bp2build/java_import_conversion_test.go b/bp2build/java_import_conversion_test.go
index 2f7211c..0b3191c 100644
--- a/bp2build/java_import_conversion_test.go
+++ b/bp2build/java_import_conversion_test.go
@@ -29,7 +29,7 @@
func registerJavaImportModuleTypes(ctx android.RegistrationContext) {
}
-func TestMinimalJavaImport(t *testing.T) {
+func TestJavaImportMinimal(t *testing.T) {
runJavaImportTestCase(t, bp2buildTestCase{
description: "Java import - simple example",
moduleTypeUnderTest: "java_import",
@@ -50,3 +50,36 @@
}),
}})
}
+
+func TestJavaImportArchVariant(t *testing.T) {
+ runJavaImportTestCase(t, bp2buildTestCase{
+ description: "Java import - simple example",
+ moduleTypeUnderTest: "java_import",
+ moduleTypeUnderTestFactory: java.ImportFactory,
+ filesystem: map[string]string{
+ "import.jar": "",
+ },
+ blueprint: `
+java_import {
+ name: "example_import",
+ target: {
+ android: {
+ jars: ["android.jar"],
+ },
+ linux_glibc: {
+ jars: ["linux.jar"],
+ },
+ },
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("java_import", "example_import", attrNameToString{
+ "jars": `select({
+ "//build/bazel/platforms/os:android": ["android.jar"],
+ "//build/bazel/platforms/os:linux": ["linux.jar"],
+ "//conditions:default": [],
+ })`,
+ }),
+ }})
+}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 318cd7c..ff5ba45 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -331,6 +331,14 @@
})
}
+func (test *testDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ if len(test.InstallerProperties.Test_suites) > 0 {
+ entries.AddCompatibilityTestSuites(test.InstallerProperties.Test_suites...)
+ }
+ })
+}
+
func (binary *binaryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
ctx.subAndroidMk(entries, binary.baseInstaller)
@@ -379,14 +387,13 @@
func (test *testBinary) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
ctx.subAndroidMk(entries, test.binaryDecorator)
+ ctx.subAndroidMk(entries, test.testDecorator)
+
entries.Class = "NATIVE_TESTS"
if Bool(test.Properties.Test_per_src) {
entries.SubName = "_" + String(test.binaryDecorator.Properties.Stem)
}
entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- if len(test.Properties.Test_suites) > 0 {
- entries.AddCompatibilityTestSuites(test.Properties.Test_suites...)
- }
if test.testConfig != nil {
entries.SetString("LOCAL_FULL_TEST_CONFIG", test.testConfig.String())
}
@@ -445,6 +452,7 @@
func (test *testLibrary) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
ctx.subAndroidMk(entries, test.libraryDecorator)
+ ctx.subAndroidMk(entries, test.testDecorator)
}
func (installer *baseInstaller) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
diff --git a/cc/cc.go b/cc/cc.go
index 58ab28c..ac6da05 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -3595,7 +3595,8 @@
&SharedProperties{},
&FlagExporterProperties{},
&BinaryLinkerProperties{},
- &TestProperties{},
+ &TestLinkerProperties{},
+ &TestInstallerProperties{},
&TestBinaryProperties{},
&BenchmarkProperties{},
&fuzz.FuzzProperties{},
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 278efa1..09cc352 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -779,6 +779,68 @@
}
}
+func TestTestBinaryTestSuites(t *testing.T) {
+ bp := `
+ cc_test {
+ name: "main_test",
+ srcs: ["main_test.cpp"],
+ test_suites: [
+ "suite_1",
+ "suite_2",
+ ],
+ gtest: false,
+ }
+ `
+
+ ctx := prepareForCcTest.RunTestWithBp(t, bp).TestContext
+ module := ctx.ModuleForTests("main_test", "android_arm_armv7-a-neon").Module()
+
+ entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
+ compatEntries := entries.EntryMap["LOCAL_COMPATIBILITY_SUITE"]
+ if len(compatEntries) != 2 {
+ t.Errorf("expected two elements in LOCAL_COMPATIBILITY_SUITE. got %d", len(compatEntries))
+ }
+ if compatEntries[0] != "suite_1" {
+ t.Errorf("expected LOCAL_COMPATIBILITY_SUITE to be`suite_1`,"+
+ " but was '%s'", compatEntries[0])
+ }
+ if compatEntries[1] != "suite_2" {
+ t.Errorf("expected LOCAL_COMPATIBILITY_SUITE to be`suite_2`,"+
+ " but was '%s'", compatEntries[1])
+ }
+}
+
+func TestTestLibraryTestSuites(t *testing.T) {
+ bp := `
+ cc_test_library {
+ name: "main_test_lib",
+ srcs: ["main_test_lib.cpp"],
+ test_suites: [
+ "suite_1",
+ "suite_2",
+ ],
+ gtest: false,
+ }
+ `
+
+ ctx := prepareForCcTest.RunTestWithBp(t, bp).TestContext
+ module := ctx.ModuleForTests("main_test_lib", "android_arm_armv7-a-neon_shared").Module()
+
+ entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
+ compatEntries := entries.EntryMap["LOCAL_COMPATIBILITY_SUITE"]
+ if len(compatEntries) != 2 {
+ t.Errorf("expected two elements in LOCAL_COMPATIBILITY_SUITE. got %d", len(compatEntries))
+ }
+ if compatEntries[0] != "suite_1" {
+ t.Errorf("expected LOCAL_COMPATIBILITY_SUITE to be`suite_1`,"+
+ " but was '%s'", compatEntries[0])
+ }
+ if compatEntries[1] != "suite_2" {
+ t.Errorf("expected LOCAL_COMPATIBILITY_SUITE to be`suite_2`,"+
+ " but was '%s'", compatEntries[1])
+ }
+}
+
func TestVndkWhenVndkVersionIsNotSet(t *testing.T) {
ctx := testCcNoVndk(t, `
cc_library {
diff --git a/cc/config/global.go b/cc/config/global.go
index 8dda537..fad675a 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -214,7 +214,6 @@
// http://b/145211066
"-Wno-implicit-int-float-conversion",
// New warnings to be fixed after clang-r377782.
- "-Wno-int-in-bool-context", // http://b/148287349
"-Wno-sizeof-array-div", // http://b/148815709
"-Wno-tautological-overlap-compare", // http://b/148815696
// New warnings to be fixed after clang-r383902.
@@ -287,8 +286,8 @@
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-r450784"
- ClangDefaultShortVersion = "14.0.3"
+ ClangDefaultVersion = "clang-r450784b"
+ ClangDefaultShortVersion = "14.0.4"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
diff --git a/cc/test.go b/cc/test.go
index d8b7833..ead7877 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -25,7 +25,8 @@
"android/soong/tradefed"
)
-type TestProperties struct {
+// TestLinkerProperties properties to be registered via the linker
+type TestLinkerProperties struct {
// if set, build against the gtest library. Defaults to true.
Gtest *bool
@@ -33,6 +34,12 @@
Isolated *bool
}
+// TestInstallerProperties properties to be registered via the installer
+type TestInstallerProperties struct {
+ // list of compatibility suites (for example "cts", "vts") that the module should be installed into.
+ Test_suites []string `android:"arch_variant"`
+}
+
// Test option struct.
type TestOptions struct {
// The UID that you want to run the test as on a device.
@@ -83,10 +90,6 @@
// list of binary modules that should be installed alongside the test
Data_bins []string `android:"arch_variant"`
- // list of compatibility suites (for example "cts", "vts") that the module should be
- // installed into.
- Test_suites []string `android:"arch_variant"`
-
// the name of the test configuration (for example "AndroidTest.xml") that should be
// installed with the module.
Test_config *string `android:"path,arch_variant"`
@@ -243,12 +246,14 @@
}
type testDecorator struct {
- Properties TestProperties
- linker *baseLinker
+ LinkerProperties TestLinkerProperties
+ InstallerProperties TestInstallerProperties
+ installer *baseInstaller
+ linker *baseLinker
}
func (test *testDecorator) gtest() bool {
- return BoolDefault(test.Properties.Gtest, true)
+ return BoolDefault(test.LinkerProperties.Gtest, true)
}
func (test *testDecorator) testBinary() bool {
@@ -283,7 +288,7 @@
if test.gtest() {
if ctx.useSdk() && ctx.Device() {
deps.StaticLibs = append(deps.StaticLibs, "libgtest_main_ndk_c++", "libgtest_ndk_c++")
- } else if BoolDefault(test.Properties.Isolated, false) {
+ } else if BoolDefault(test.LinkerProperties.Isolated, false) {
deps.StaticLibs = append(deps.StaticLibs, "libgtest_isolated_main")
// The isolated library requires liblog, but adding it
// as a static library means unit tests cannot override
@@ -316,7 +321,11 @@
}
func (test *testDecorator) linkerProps() []interface{} {
- return []interface{}{&test.Properties}
+ return []interface{}{&test.LinkerProperties}
+}
+
+func (test *testDecorator) installerProps() []interface{} {
+ return []interface{}{&test.InstallerProperties}
}
func NewTestInstaller() *baseInstaller {
@@ -324,7 +333,7 @@
}
type testBinary struct {
- testDecorator
+ *testDecorator
*binaryDecorator
*baseCompiler
Properties TestBinaryProperties
@@ -358,6 +367,10 @@
return flags
}
+func (test *testBinary) installerProps() []interface{} {
+ return append(test.baseInstaller.installerProps(), test.testDecorator.installerProps()...)
+}
+
func (test *testBinary) install(ctx ModuleContext, file android.Path) {
// TODO: (b/167308193) Switch to /data/local/tests/unrestricted as the default install base.
testInstallBase := "/data/local/tmp"
@@ -411,7 +424,7 @@
var options []tradefed.Option
configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.StopServicesSetup", options})
}
- if Bool(test.testDecorator.Properties.Isolated) {
+ if Bool(test.testDecorator.LinkerProperties.Isolated) {
configs = append(configs, tradefed.Option{Name: "not-shardable", Value: "true"})
}
if test.Properties.Test_options.Run_test_as != nil {
@@ -441,7 +454,7 @@
}
test.testConfig = tradefed.AutoGenNativeTestConfig(ctx, test.Properties.Test_config,
- test.Properties.Test_config_template, test.Properties.Test_suites, configs, test.Properties.Auto_gen_config, testInstallBase)
+ test.Properties.Test_config_template, test.testDecorator.InstallerProperties.Test_suites, configs, test.Properties.Auto_gen_config, testInstallBase)
test.extraTestConfigs = android.PathsForModuleSrc(ctx, test.Properties.Test_options.Extra_test_configs)
@@ -466,8 +479,9 @@
binary.baseInstaller = NewTestInstaller()
test := &testBinary{
- testDecorator: testDecorator{
- linker: binary.baseLinker,
+ testDecorator: &testDecorator{
+ linker: binary.baseLinker,
+ installer: binary.baseInstaller,
},
binaryDecorator: binary,
baseCompiler: NewBaseCompiler(),
@@ -479,12 +493,14 @@
}
type testLibrary struct {
- testDecorator
+ *testDecorator
*libraryDecorator
}
func (test *testLibrary) linkerProps() []interface{} {
- return append(test.testDecorator.linkerProps(), test.libraryDecorator.linkerProps()...)
+ var props []interface{}
+ props = append(props, test.testDecorator.linkerProps()...)
+ return append(props, test.libraryDecorator.linkerProps()...)
}
func (test *testLibrary) linkerInit(ctx BaseModuleContext) {
@@ -504,16 +520,22 @@
return flags
}
+func (test *testLibrary) installerProps() []interface{} {
+ return append(test.baseInstaller.installerProps(), test.testDecorator.installerProps()...)
+}
+
func NewTestLibrary(hod android.HostOrDeviceSupported) *Module {
module, library := NewLibrary(android.HostAndDeviceSupported)
library.baseInstaller = NewTestInstaller()
test := &testLibrary{
- testDecorator: testDecorator{
- linker: library.baseLinker,
+ testDecorator: &testDecorator{
+ linker: library.baseLinker,
+ installer: library.baseInstaller,
},
libraryDecorator: library,
}
module.linker = test
+ module.installer = test
return module
}
diff --git a/cmd/symbols_map/Android.bp b/cmd/symbols_map/Android.bp
new file mode 100644
index 0000000..0ba3b07
--- /dev/null
+++ b/cmd/symbols_map/Android.bp
@@ -0,0 +1,34 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+ name: "symbols_map",
+ srcs: [
+ "elf.go",
+ "r8.go",
+ "symbols_map.go",
+ ],
+ testSrcs: [
+ "elf_test.go",
+ "r8_test.go",
+ ],
+ deps: [
+ "blueprint-pathtools",
+ "golang-protobuf-encoding-prototext",
+ "soong-response",
+ "symbols_map_proto",
+ ],
+}
+
+bootstrap_go_package {
+ name: "symbols_map_proto",
+ pkgPath: "android/soong/cmd/symbols_map/symbols_map_proto",
+ deps: [
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ ],
+ srcs: [
+ "symbols_map_proto/symbols_map.pb.go",
+ ],
+}
diff --git a/cmd/symbols_map/elf.go b/cmd/symbols_map/elf.go
new file mode 100644
index 0000000..b38896a
--- /dev/null
+++ b/cmd/symbols_map/elf.go
@@ -0,0 +1,95 @@
+// Copyright 2022 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 main
+
+import (
+ "debug/elf"
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+ "io"
+)
+
+const gnuBuildID = "GNU\x00"
+
+// elfIdentifier extracts the elf build ID from an elf file. If allowMissing is true it returns
+// an empty identifier if the file exists but the build ID note does not.
+func elfIdentifier(filename string, allowMissing bool) (string, error) {
+ f, err := elf.Open(filename)
+ if err != nil {
+ return "", fmt.Errorf("failed to open %s: %w", filename, err)
+ }
+ defer f.Close()
+
+ buildIDNote := f.Section(".note.gnu.build-id")
+ if buildIDNote == nil {
+ if allowMissing {
+ return "", nil
+ }
+ return "", fmt.Errorf("failed to find .note.gnu.build-id in %s", filename)
+ }
+
+ buildIDs, err := readNote(buildIDNote.Open(), f.ByteOrder)
+ if err != nil {
+ return "", fmt.Errorf("failed to read .note.gnu.build-id: %w", err)
+ }
+
+ for name, desc := range buildIDs {
+ if name == gnuBuildID {
+ return hex.EncodeToString(desc), nil
+ }
+ }
+
+ return "", nil
+}
+
+// readNote reads the contents of a note section, returning it as a map from name to descriptor.
+func readNote(note io.Reader, byteOrder binary.ByteOrder) (map[string][]byte, error) {
+ var noteHeader struct {
+ Namesz uint32
+ Descsz uint32
+ Type uint32
+ }
+
+ notes := make(map[string][]byte)
+ for {
+ err := binary.Read(note, byteOrder, ¬eHeader)
+ if err != nil {
+ if err == io.EOF {
+ return notes, nil
+ }
+ return nil, fmt.Errorf("failed to read note header: %w", err)
+ }
+
+ nameBuf := make([]byte, align4(noteHeader.Namesz))
+ err = binary.Read(note, byteOrder, &nameBuf)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read note name: %w", err)
+ }
+ name := string(nameBuf[:noteHeader.Namesz])
+
+ descBuf := make([]byte, align4(noteHeader.Descsz))
+ err = binary.Read(note, byteOrder, &descBuf)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read note desc: %w", err)
+ }
+ notes[name] = descBuf[:noteHeader.Descsz]
+ }
+}
+
+// align4 rounds the input up to the next multiple of 4.
+func align4(i uint32) uint32 {
+ return (i + 3) &^ 3
+}
diff --git a/cmd/symbols_map/elf_test.go b/cmd/symbols_map/elf_test.go
new file mode 100644
index 0000000..e616228
--- /dev/null
+++ b/cmd/symbols_map/elf_test.go
@@ -0,0 +1,45 @@
+// Copyright 2022 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 main
+
+import (
+ "bytes"
+ "encoding/binary"
+ "reflect"
+ "testing"
+)
+
+func Test_readNote(t *testing.T) {
+ note := []byte{
+ 0x04, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00,
+ 0x47, 0x4e, 0x55, 0x00,
+ 0xca, 0xaf, 0x44, 0xd2, 0x82, 0x78, 0x68, 0xfe, 0xc0, 0x90, 0xa3, 0x43, 0x85, 0x36, 0x6c, 0xc7,
+ }
+
+ descs, err := readNote(bytes.NewBuffer(note), binary.LittleEndian)
+ if err != nil {
+ t.Fatalf("unexpected error in readNote: %s", err)
+ }
+
+ expectedDescs := map[string][]byte{
+ "GNU\x00": []byte{0xca, 0xaf, 0x44, 0xd2, 0x82, 0x78, 0x68, 0xfe, 0xc0, 0x90, 0xa3, 0x43, 0x85, 0x36, 0x6c, 0xc7},
+ }
+
+ if !reflect.DeepEqual(descs, expectedDescs) {
+ t.Errorf("incorrect return, want %#v got %#v", expectedDescs, descs)
+ }
+}
diff --git a/cmd/symbols_map/r8.go b/cmd/symbols_map/r8.go
new file mode 100644
index 0000000..6f73e09
--- /dev/null
+++ b/cmd/symbols_map/r8.go
@@ -0,0 +1,56 @@
+// Copyright 2022 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 main
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+ "strings"
+)
+
+const hashPrefix = "# pg_map_hash: "
+const hashTypePrefix = "SHA-256 "
+const commentPrefix = "#"
+
+// r8Identifier extracts the hash from the comments of a dictionary produced by R8. It returns
+// an empty identifier if no matching comment was found before the first non-comment line.
+func r8Identifier(filename string) (string, error) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return "", fmt.Errorf("failed to open %s: %w", filename, err)
+ }
+ defer f.Close()
+
+ return extractR8CompilerHash(f)
+}
+
+func extractR8CompilerHash(r io.Reader) (string, error) {
+ s := bufio.NewScanner(r)
+ for s.Scan() {
+ line := s.Text()
+ if strings.HasPrefix(line, hashPrefix) {
+ hash := strings.TrimPrefix(line, hashPrefix)
+ if !strings.HasPrefix(hash, hashTypePrefix) {
+ return "", fmt.Errorf("invalid hash type found in %q", line)
+ }
+ return strings.TrimPrefix(hash, hashTypePrefix), nil
+ } else if !strings.HasPrefix(line, commentPrefix) {
+ break
+ }
+ }
+ return "", nil
+}
diff --git a/cmd/symbols_map/r8_test.go b/cmd/symbols_map/r8_test.go
new file mode 100644
index 0000000..5712da9
--- /dev/null
+++ b/cmd/symbols_map/r8_test.go
@@ -0,0 +1,91 @@
+// Copyright 2022 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 main
+
+import (
+ "bytes"
+ "strings"
+ "testing"
+)
+
+func Test_extractR8CompilerHash(t *testing.T) {
+ testCases := []struct {
+ name string
+ data string
+
+ hash string
+ err string
+ }{
+ {
+ name: "simple",
+ data: `# compiler: R8
+# compiler_version: 3.3.18-dev
+# min_api: 10000
+# compiler_hash: bab44c1a04a2201b55fe10394f477994205c34e0
+# common_typos_disable
+# {"id":"com.android.tools.r8.mapping","version":"2.0"}
+# pg_map_id: 7fe8b95
+# pg_map_hash: SHA-256 7fe8b95ae71f179f63d2a585356fb9cf2c8fb94df9c9dd50621ffa6d9e9e88da
+android.car.userlib.UserHelper -> android.car.userlib.UserHelper:
+`,
+ hash: "7fe8b95ae71f179f63d2a585356fb9cf2c8fb94df9c9dd50621ffa6d9e9e88da",
+ },
+ {
+ name: "empty",
+ data: ``,
+ hash: "",
+ },
+ {
+ name: "non comment line",
+ data: `# compiler: R8
+# compiler_version: 3.3.18-dev
+# min_api: 10000
+# compiler_hash: bab44c1a04a2201b55fe10394f477994205c34e0
+# common_typos_disable
+# {"id":"com.android.tools.r8.mapping","version":"2.0"}
+# pg_map_id: 7fe8b95
+android.car.userlib.UserHelper -> android.car.userlib.UserHelper:
+# pg_map_hash: SHA-256 7fe8b95ae71f179f63d2a585356fb9cf2c8fb94df9c9dd50621ffa6d9e9e88da
+`,
+ hash: "",
+ },
+ {
+ name: "invalid hash",
+ data: `# pg_map_hash: foobar 7fe8b95ae71f179f63d2a585356fb9cf2c8fb94df9c9dd50621ffa6d9e9e88da`,
+ err: "invalid hash type",
+ },
+ }
+
+ for _, tt := range testCases {
+ t.Run(tt.name, func(t *testing.T) {
+ hash, err := extractR8CompilerHash(bytes.NewBufferString(tt.data))
+ if err != nil {
+ if tt.err != "" {
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Fatalf("incorrect error in extractR8CompilerHash, want %s got %s", tt.err, err)
+ }
+ } else {
+ t.Fatalf("unexpected error in extractR8CompilerHash: %s", err)
+ }
+ } else if tt.err != "" {
+ t.Fatalf("missing error in extractR8CompilerHash, want %s", tt.err)
+ }
+
+ if g, w := hash, tt.hash; g != w {
+ t.Errorf("incorrect hash, want %q got %q", w, g)
+ }
+ })
+ }
+}
diff --git a/cmd/symbols_map/symbols_map.go b/cmd/symbols_map/symbols_map.go
new file mode 100644
index 0000000..938446d
--- /dev/null
+++ b/cmd/symbols_map/symbols_map.go
@@ -0,0 +1,202 @@
+// Copyright 2022 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 main
+
+import (
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strings"
+
+ "android/soong/cmd/symbols_map/symbols_map_proto"
+ "android/soong/response"
+
+ "github.com/google/blueprint/pathtools"
+ "google.golang.org/protobuf/encoding/prototext"
+ "google.golang.org/protobuf/proto"
+)
+
+// This tool is used to extract a hash from an elf file or an r8 dictionary and store it as a
+// textproto, or to merge multiple textprotos together.
+
+func main() {
+ var expandedArgs []string
+ for _, arg := range os.Args[1:] {
+ if strings.HasPrefix(arg, "@") {
+ f, err := os.Open(strings.TrimPrefix(arg, "@"))
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+
+ respArgs, err := response.ReadRspFile(f)
+ f.Close()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+ expandedArgs = append(expandedArgs, respArgs...)
+ } else {
+ expandedArgs = append(expandedArgs, arg)
+ }
+ }
+
+ flags := flag.NewFlagSet("flags", flag.ExitOnError)
+
+ // Hide the flag package to prevent accidental references to flag instead of flags.
+ flag := struct{}{}
+ _ = flag
+
+ flags.Usage = func() {
+ fmt.Fprintf(flags.Output(), "Usage of %s:\n", os.Args[0])
+ fmt.Fprintf(flags.Output(), " %s -elf|-r8 <input file> [-write_if_changed] <output file>\n", os.Args[0])
+ fmt.Fprintf(flags.Output(), " %s -merge <output file> [-write_if_changed] [-ignore_missing_files] [-strip_prefix <prefix>] [<input file>...]\n", os.Args[0])
+ fmt.Fprintln(flags.Output())
+
+ flags.PrintDefaults()
+ }
+
+ elfFile := flags.String("elf", "", "extract identifier from an elf file")
+ r8File := flags.String("r8", "", "extract identifier from an r8 dictionary")
+ merge := flags.String("merge", "", "merge multiple identifier protos")
+
+ writeIfChanged := flags.Bool("write_if_changed", false, "only write output file if it is modified")
+ ignoreMissingFiles := flags.Bool("ignore_missing_files", false, "ignore missing input files in merge mode")
+ stripPrefix := flags.String("strip_prefix", "", "prefix to strip off of the location field in merge mode")
+
+ flags.Parse(expandedArgs)
+
+ if *merge != "" {
+ // If merge mode was requested perform the merge and exit early.
+ err := mergeProtos(*merge, flags.Args(), *stripPrefix, *writeIfChanged, *ignoreMissingFiles)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "failed to merge protos: %s", err)
+ os.Exit(1)
+ }
+ os.Exit(0)
+ }
+
+ if *elfFile == "" && *r8File == "" {
+ fmt.Fprintf(os.Stderr, "-elf or -r8 argument is required\n")
+ flags.Usage()
+ os.Exit(1)
+ }
+
+ if *elfFile != "" && *r8File != "" {
+ fmt.Fprintf(os.Stderr, "only one of -elf or -r8 argument is allowed\n")
+ flags.Usage()
+ os.Exit(1)
+ }
+
+ if flags.NArg() != 1 {
+ flags.Usage()
+ os.Exit(1)
+ }
+
+ output := flags.Arg(0)
+
+ var identifier string
+ var location string
+ var typ symbols_map_proto.Mapping_Type
+ var err error
+
+ if *elfFile != "" {
+ typ = symbols_map_proto.Mapping_ELF
+ location = *elfFile
+ identifier, err = elfIdentifier(*elfFile, true)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error reading elf identifier: %s\n", err)
+ os.Exit(1)
+ }
+ } else if *r8File != "" {
+ typ = symbols_map_proto.Mapping_R8
+ identifier, err = r8Identifier(*r8File)
+ location = *r8File
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error reading r8 identifier: %s\n", err)
+ os.Exit(1)
+ }
+ } else {
+ panic("shouldn't get here")
+ }
+
+ mapping := symbols_map_proto.Mapping{
+ Identifier: proto.String(identifier),
+ Location: proto.String(location),
+ Type: typ.Enum(),
+ }
+
+ err = writeTextProto(output, &mapping, *writeIfChanged)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error writing output: %s\n", err)
+ os.Exit(1)
+ }
+}
+
+// writeTextProto writes a proto to an output file as a textproto, optionally leaving the file
+// unmodified if it was already up to date.
+func writeTextProto(output string, message proto.Message, writeIfChanged bool) error {
+ marshaller := prototext.MarshalOptions{Multiline: true}
+ data, err := marshaller.Marshal(message)
+ if err != nil {
+ return fmt.Errorf("error marshalling textproto: %w", err)
+ }
+
+ if writeIfChanged {
+ err = pathtools.WriteFileIfChanged(output, data, 0666)
+ } else {
+ err = ioutil.WriteFile(output, data, 0666)
+ }
+
+ if err != nil {
+ return fmt.Errorf("error writing to %s: %w\n", output, err)
+ }
+
+ return nil
+}
+
+// mergeProtos merges a list of textproto files containing Mapping messages into a single textproto
+// containing a Mappings message.
+func mergeProtos(output string, inputs []string, stripPrefix string, writeIfChanged bool, ignoreMissingFiles bool) error {
+ mappings := symbols_map_proto.Mappings{}
+ for _, input := range inputs {
+ mapping := symbols_map_proto.Mapping{}
+ data, err := ioutil.ReadFile(input)
+ if err != nil {
+ if ignoreMissingFiles && os.IsNotExist(err) {
+ // Merge mode is used on a list of files in the packaging directory. If multiple
+ // goals are included on the build command line, for example `dist` and `tests`,
+ // then the symbols packaging rule for `dist` can run while a dependency of `tests`
+ // is modifying the symbols packaging directory. That can result in a file that
+ // existed when the file list was generated being deleted as part of updating it,
+ // resulting in sporadic ENOENT errors. Ignore them if -ignore_missing_files
+ // was passed on the command line.
+ continue
+ }
+ return fmt.Errorf("failed to read %s: %w", input, err)
+ }
+ err = prototext.Unmarshal(data, &mapping)
+ if err != nil {
+ return fmt.Errorf("failed to parse textproto %s: %w", input, err)
+ }
+ if stripPrefix != "" && mapping.Location != nil {
+ mapping.Location = proto.String(strings.TrimPrefix(*mapping.Location, stripPrefix))
+ }
+ mappings.Mappings = append(mappings.Mappings, &mapping)
+ }
+
+ return writeTextProto(output, &mappings, writeIfChanged)
+}
diff --git a/cmd/symbols_map/symbols_map_proto/symbols_map.pb.go b/cmd/symbols_map/symbols_map_proto/symbols_map.pb.go
new file mode 100644
index 0000000..f9c0ce5
--- /dev/null
+++ b/cmd/symbols_map/symbols_map_proto/symbols_map.pb.go
@@ -0,0 +1,315 @@
+// Copyright 2022 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.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.27.1
+// protoc v3.9.1
+// source: symbols_map.proto
+
+package symbols_map_proto
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// Type is the valid types of a mapping.
+type Mapping_Type int32
+
+const (
+ // ELF denotes a mapping from an elf build ID to an unstripped elf file.
+ Mapping_ELF Mapping_Type = 0
+ // R8 denotes a mapping from an R8 dictionary hash to an R8 dictionary.
+ Mapping_R8 Mapping_Type = 1
+)
+
+// Enum value maps for Mapping_Type.
+var (
+ Mapping_Type_name = map[int32]string{
+ 0: "ELF",
+ 1: "R8",
+ }
+ Mapping_Type_value = map[string]int32{
+ "ELF": 0,
+ "R8": 1,
+ }
+)
+
+func (x Mapping_Type) Enum() *Mapping_Type {
+ p := new(Mapping_Type)
+ *p = x
+ return p
+}
+
+func (x Mapping_Type) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (Mapping_Type) Descriptor() protoreflect.EnumDescriptor {
+ return file_symbols_map_proto_enumTypes[0].Descriptor()
+}
+
+func (Mapping_Type) Type() protoreflect.EnumType {
+ return &file_symbols_map_proto_enumTypes[0]
+}
+
+func (x Mapping_Type) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *Mapping_Type) UnmarshalJSON(b []byte) error {
+ num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+ if err != nil {
+ return err
+ }
+ *x = Mapping_Type(num)
+ return nil
+}
+
+// Deprecated: Use Mapping_Type.Descriptor instead.
+func (Mapping_Type) EnumDescriptor() ([]byte, []int) {
+ return file_symbols_map_proto_rawDescGZIP(), []int{0, 0}
+}
+
+type Mapping struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // identifier is a unique identifier of a location, generally the hash of the file. For an
+ // elf file it is the elf build ID, for an R8 dictionary it is the hash from the comments in the
+ // top of the file. It may be empty if no hash could be extracted from the file.
+ Identifier *string `protobuf:"bytes,1,opt,name=identifier" json:"identifier,omitempty"`
+ // location is the path to the file with the given identifier. The location should be valid
+ // both on the local disk and in the distributed symbols.zip or proguard_dict.zip files.
+ Location *string `protobuf:"bytes,2,opt,name=location" json:"location,omitempty"`
+ // type is the type of the mapping, either ELF or R8.
+ Type *Mapping_Type `protobuf:"varint,3,opt,name=type,enum=symbols_map.Mapping_Type" json:"type,omitempty"`
+}
+
+func (x *Mapping) Reset() {
+ *x = Mapping{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_symbols_map_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Mapping) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Mapping) ProtoMessage() {}
+
+func (x *Mapping) ProtoReflect() protoreflect.Message {
+ mi := &file_symbols_map_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Mapping.ProtoReflect.Descriptor instead.
+func (*Mapping) Descriptor() ([]byte, []int) {
+ return file_symbols_map_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Mapping) GetIdentifier() string {
+ if x != nil && x.Identifier != nil {
+ return *x.Identifier
+ }
+ return ""
+}
+
+func (x *Mapping) GetLocation() string {
+ if x != nil && x.Location != nil {
+ return *x.Location
+ }
+ return ""
+}
+
+func (x *Mapping) GetType() Mapping_Type {
+ if x != nil && x.Type != nil {
+ return *x.Type
+ }
+ return Mapping_ELF
+}
+
+type Mappings struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Mappings []*Mapping `protobuf:"bytes,4,rep,name=mappings" json:"mappings,omitempty"`
+}
+
+func (x *Mappings) Reset() {
+ *x = Mappings{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_symbols_map_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Mappings) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Mappings) ProtoMessage() {}
+
+func (x *Mappings) ProtoReflect() protoreflect.Message {
+ mi := &file_symbols_map_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Mappings.ProtoReflect.Descriptor instead.
+func (*Mappings) Descriptor() ([]byte, []int) {
+ return file_symbols_map_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *Mappings) GetMappings() []*Mapping {
+ if x != nil {
+ return x.Mappings
+ }
+ return nil
+}
+
+var File_symbols_map_proto protoreflect.FileDescriptor
+
+var file_symbols_map_proto_rawDesc = []byte{
+ 0x0a, 0x11, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70,
+ 0x22, 0x8d, 0x01, 0x0a, 0x07, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x1e, 0x0a, 0x0a,
+ 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08,
+ 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
+ 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
+ 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73,
+ 0x5f, 0x6d, 0x61, 0x70, 0x2e, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x54, 0x79, 0x70,
+ 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x17, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,
+ 0x07, 0x0a, 0x03, 0x45, 0x4c, 0x46, 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x52, 0x38, 0x10, 0x01,
+ 0x22, 0x3c, 0x0a, 0x08, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x30, 0x0a, 0x08,
+ 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14,
+ 0x2e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x2e, 0x4d, 0x61, 0x70,
+ 0x70, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x31,
+ 0x5a, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f,
+ 0x63, 0x6d, 0x64, 0x2f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x2f,
+ 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f,
+}
+
+var (
+ file_symbols_map_proto_rawDescOnce sync.Once
+ file_symbols_map_proto_rawDescData = file_symbols_map_proto_rawDesc
+)
+
+func file_symbols_map_proto_rawDescGZIP() []byte {
+ file_symbols_map_proto_rawDescOnce.Do(func() {
+ file_symbols_map_proto_rawDescData = protoimpl.X.CompressGZIP(file_symbols_map_proto_rawDescData)
+ })
+ return file_symbols_map_proto_rawDescData
+}
+
+var file_symbols_map_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_symbols_map_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_symbols_map_proto_goTypes = []interface{}{
+ (Mapping_Type)(0), // 0: symbols_map.Mapping.Type
+ (*Mapping)(nil), // 1: symbols_map.Mapping
+ (*Mappings)(nil), // 2: symbols_map.Mappings
+}
+var file_symbols_map_proto_depIdxs = []int32{
+ 0, // 0: symbols_map.Mapping.type:type_name -> symbols_map.Mapping.Type
+ 1, // 1: symbols_map.Mappings.mappings:type_name -> symbols_map.Mapping
+ 2, // [2:2] is the sub-list for method output_type
+ 2, // [2:2] is the sub-list for method input_type
+ 2, // [2:2] is the sub-list for extension type_name
+ 2, // [2:2] is the sub-list for extension extendee
+ 0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_symbols_map_proto_init() }
+func file_symbols_map_proto_init() {
+ if File_symbols_map_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_symbols_map_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Mapping); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_symbols_map_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Mappings); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_symbols_map_proto_rawDesc,
+ NumEnums: 1,
+ NumMessages: 2,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_symbols_map_proto_goTypes,
+ DependencyIndexes: file_symbols_map_proto_depIdxs,
+ EnumInfos: file_symbols_map_proto_enumTypes,
+ MessageInfos: file_symbols_map_proto_msgTypes,
+ }.Build()
+ File_symbols_map_proto = out.File
+ file_symbols_map_proto_rawDesc = nil
+ file_symbols_map_proto_goTypes = nil
+ file_symbols_map_proto_depIdxs = nil
+}
diff --git a/cmd/symbols_map/symbols_map_proto/symbols_map.proto b/cmd/symbols_map/symbols_map_proto/symbols_map.proto
new file mode 100644
index 0000000..693fe3e
--- /dev/null
+++ b/cmd/symbols_map/symbols_map_proto/symbols_map.proto
@@ -0,0 +1,44 @@
+// Copyright 2022 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.
+
+syntax = "proto2";
+
+package symbols_map;
+option go_package = "android/soong/cmd/symbols_map/symbols_map_proto";
+
+message Mapping {
+ // identifier is a unique identifier of a location, generally the hash of the file. For an
+ // elf file it is the elf build ID, for an R8 dictionary it is the hash from the comments in the
+ // top of the file. It may be empty if no hash could be extracted from the file.
+ optional string identifier = 1;
+
+ // location is the path to the file with the given identifier. The location should be valid
+ // both on the local disk and in the distributed symbols.zip or proguard_dict.zip files.
+ optional string location = 2;
+
+ // Type is the valid types of a mapping.
+ enum Type {
+ // ELF denotes a mapping from an elf build ID to an unstripped elf file.
+ ELF = 0;
+ // R8 denotes a mapping from an R8 dictionary hash to an R8 dictionary.
+ R8 = 1;
+ }
+
+ // type is the type of the mapping, either ELF or R8.
+ optional Type type = 3;
+}
+
+message Mappings {
+ repeated Mapping mappings = 4;
+}
\ No newline at end of file
diff --git a/cmd/symbols_map/symbols_map_test.go b/cmd/symbols_map/symbols_map_test.go
new file mode 100644
index 0000000..754b7ef
--- /dev/null
+++ b/cmd/symbols_map/symbols_map_test.go
@@ -0,0 +1,217 @@
+// Copyright 2022 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 main
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "android/soong/cmd/symbols_map/symbols_map_proto"
+
+ "google.golang.org/protobuf/encoding/prototext"
+ "google.golang.org/protobuf/proto"
+)
+
+func Test_mergeProtos(t *testing.T) {
+ type testFile struct {
+ filename string
+ contents *symbols_map_proto.Mapping
+ missing bool
+ }
+
+ tests := []struct {
+ name string
+ inputs []testFile
+ stripPrefix string
+ writeIfChanged bool
+ ignoreMissingFiles bool
+
+ error string
+ output *symbols_map_proto.Mappings
+ }{
+ {
+ name: "empty",
+ output: &symbols_map_proto.Mappings{},
+ },
+ {
+ name: "merge",
+ inputs: []testFile{
+ {
+ filename: "foo",
+ contents: &symbols_map_proto.Mapping{
+ Identifier: proto.String("foo"),
+ Location: proto.String("symbols/foo"),
+ Type: symbols_map_proto.Mapping_ELF.Enum(),
+ },
+ },
+ {
+ filename: "bar",
+ contents: &symbols_map_proto.Mapping{
+ Identifier: proto.String("bar"),
+ Location: proto.String("symbols/bar"),
+ Type: symbols_map_proto.Mapping_R8.Enum(),
+ },
+ },
+ },
+ output: &symbols_map_proto.Mappings{
+ Mappings: []*symbols_map_proto.Mapping{
+ {
+ Identifier: proto.String("foo"),
+ Location: proto.String("symbols/foo"),
+ Type: symbols_map_proto.Mapping_ELF.Enum(),
+ },
+ {
+ Identifier: proto.String("bar"),
+ Location: proto.String("symbols/bar"),
+ Type: symbols_map_proto.Mapping_R8.Enum(),
+ },
+ },
+ },
+ },
+ {
+ name: "strip prefix",
+ inputs: []testFile{
+ {
+ filename: "foo",
+ contents: &symbols_map_proto.Mapping{
+ Identifier: proto.String("foo"),
+ Location: proto.String("symbols/foo"),
+ Type: symbols_map_proto.Mapping_ELF.Enum(),
+ },
+ },
+ {
+ filename: "bar",
+ contents: &symbols_map_proto.Mapping{
+ Identifier: proto.String("bar"),
+ Location: proto.String("symbols/bar"),
+ Type: symbols_map_proto.Mapping_R8.Enum(),
+ },
+ },
+ },
+ stripPrefix: "symbols/",
+ output: &symbols_map_proto.Mappings{
+ Mappings: []*symbols_map_proto.Mapping{
+ {
+ Identifier: proto.String("foo"),
+ Location: proto.String("foo"),
+ Type: symbols_map_proto.Mapping_ELF.Enum(),
+ },
+ {
+ Identifier: proto.String("bar"),
+ Location: proto.String("bar"),
+ Type: symbols_map_proto.Mapping_R8.Enum(),
+ },
+ },
+ },
+ },
+ {
+ name: "missing",
+ inputs: []testFile{
+ {
+ filename: "foo",
+ contents: &symbols_map_proto.Mapping{
+ Identifier: proto.String("foo"),
+ Location: proto.String("symbols/foo"),
+ Type: symbols_map_proto.Mapping_ELF.Enum(),
+ },
+ },
+ {
+ filename: "bar",
+ missing: true,
+ },
+ },
+ error: "no such file or directory",
+ },
+ {
+ name: "ignore missing",
+ inputs: []testFile{
+ {
+ filename: "foo",
+ contents: &symbols_map_proto.Mapping{
+ Identifier: proto.String("foo"),
+ Location: proto.String("symbols/foo"),
+ Type: symbols_map_proto.Mapping_ELF.Enum(),
+ },
+ },
+ {
+ filename: "bar",
+ missing: true,
+ },
+ },
+ ignoreMissingFiles: true,
+ output: &symbols_map_proto.Mappings{
+ Mappings: []*symbols_map_proto.Mapping{
+ {
+ Identifier: proto.String("foo"),
+ Location: proto.String("symbols/foo"),
+ Type: symbols_map_proto.Mapping_ELF.Enum(),
+ },
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ dir, err := os.MkdirTemp("", "test_mergeProtos")
+ if err != nil {
+ t.Fatalf("failed to create temporary directory: %s", err)
+ }
+ defer os.RemoveAll(dir)
+
+ var inputs []string
+ for _, in := range tt.inputs {
+ path := filepath.Join(dir, in.filename)
+ inputs = append(inputs, path)
+ if !in.missing {
+ err := writeTextProto(path, in.contents, false)
+ if err != nil {
+ t.Fatalf("failed to create input file %s: %s", path, err)
+ }
+ }
+ }
+ output := filepath.Join(dir, "out")
+
+ err = mergeProtos(output, inputs, tt.stripPrefix, tt.writeIfChanged, tt.ignoreMissingFiles)
+ if err != nil {
+ if tt.error != "" {
+ if !strings.Contains(err.Error(), tt.error) {
+ t.Fatalf("expected error %q, got %s", tt.error, err.Error())
+ }
+ } else {
+ t.Fatalf("unexpected error %q", err)
+ }
+ } else if tt.error != "" {
+ t.Fatalf("missing error %q", tt.error)
+ } else {
+ data, err := ioutil.ReadFile(output)
+ if err != nil {
+ t.Fatalf("failed to read output file %s: %s", output, err)
+ }
+ var got symbols_map_proto.Mappings
+ err = prototext.Unmarshal(data, &got)
+ if err != nil {
+ t.Fatalf("failed to unmarshal textproto %s: %s", output, err)
+ }
+
+ if !proto.Equal(tt.output, &got) {
+ t.Fatalf("expected output %q, got %q", tt.output.String(), got.String())
+ }
+ }
+ })
+ }
+}
diff --git a/java/androidmk.go b/java/androidmk.go
index b930441..80b828d 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -409,22 +409,6 @@
entries.SetOptionalPaths("LOCAL_SOONG_LINT_REPORTS", app.linter.reports)
},
},
- ExtraFooters: []android.AndroidMkExtraFootersFunc{
- func(w io.Writer, name, prefix, moduleDir string) {
- if app.noticeOutputs.Merged.Valid() {
- fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
- app.installApkName, app.noticeOutputs.Merged.String(), app.installApkName+"_NOTICE")
- }
- if app.noticeOutputs.TxtOutput.Valid() {
- fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
- app.installApkName, app.noticeOutputs.TxtOutput.String(), app.installApkName+"_NOTICE.txt")
- }
- if app.noticeOutputs.HtmlOutput.Valid() {
- fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
- app.installApkName, app.noticeOutputs.HtmlOutput.String(), app.installApkName+"_NOTICE.html")
- }
- },
- },
}}
}
diff --git a/java/app.go b/java/app.go
index 8728df6..21ee34e 100755
--- a/java/app.go
+++ b/java/app.go
@@ -19,7 +19,6 @@
import (
"path/filepath"
- "sort"
"strings"
"github.com/google/blueprint"
@@ -164,8 +163,6 @@
additionalAaptFlags []string
- noticeOutputs android.NoticeOutputs
-
overriddenManifestPackageName string
android.ApexBundleDepsInfo
@@ -523,53 +520,6 @@
return jniSymbols
}
-func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext) {
- // Collect NOTICE files from all dependencies.
- seenModules := make(map[android.Module]bool)
- noticePathSet := make(map[android.Path]bool)
-
- ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
- // Have we already seen this?
- if _, ok := seenModules[child]; ok {
- return false
- }
- seenModules[child] = true
-
- // Skip host modules.
- if child.Target().Os.Class == android.Host {
- return false
- }
-
- paths := child.(android.Module).NoticeFiles()
- if len(paths) > 0 {
- for _, path := range paths {
- noticePathSet[path] = true
- }
- }
- return true
- })
-
- // If the app has one, add it too.
- if len(a.NoticeFiles()) > 0 {
- for _, path := range a.NoticeFiles() {
- noticePathSet[path] = true
- }
- }
-
- if len(noticePathSet) == 0 {
- return
- }
- var noticePaths []android.Path
- for path := range noticePathSet {
- noticePaths = append(noticePaths, path)
- }
- sort.Slice(noticePaths, func(i, j int) bool {
- return noticePaths[i].String() < noticePaths[j].String()
- })
-
- a.noticeOutputs = android.BuildNoticeOutput(ctx, a.installDir, a.installApkName+".apk", noticePaths)
-}
-
// Reads and prepends a main cert from the default cert dir if it hasn't been set already, i.e. it
// isn't a cert module reference. Also checks and enforces system cert restriction if applicable.
func processMainCert(m android.ModuleBase, certPropValue string, certificates []Certificate, ctx android.ModuleContext) []Certificate {
@@ -636,9 +586,16 @@
}
a.onDeviceDir = android.InstallPathToOnDevicePath(ctx, a.installDir)
- a.noticeBuildActions(ctx)
if Bool(a.appProperties.Embed_notices) || ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") {
- a.aapt.noticeFile = a.noticeOutputs.HtmlGzOutput
+ noticeFile := android.PathForModuleOut(ctx, "NOTICE.html.gz")
+ android.BuildNoticeHtmlOutputFromLicenseMetadata(ctx, noticeFile)
+ noticeAssetPath := android.PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
+ builder := android.NewRuleBuilder(pctx, ctx)
+ builder.Command().Text("cp").
+ Input(noticeFile).
+ Output(noticeAssetPath)
+ builder.Build("notice_dir", "Building notice dir")
+ a.aapt.noticeFile = android.OptionalPathForPath(noticeAssetPath)
}
a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
diff --git a/java/app_test.go b/java/app_test.go
index 16bbec1..08baf54 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -27,7 +27,6 @@
"android/soong/android"
"android/soong/cc"
"android/soong/dexpreopt"
- "android/soong/genrule"
)
// testApp runs tests using the prepareForJavaTest
@@ -2722,116 +2721,6 @@
}
}
-func TestEmbedNotice(t *testing.T) {
- result := android.GroupFixturePreparers(
- PrepareForTestWithJavaDefaultModules,
- cc.PrepareForTestWithCcDefaultModules,
- genrule.PrepareForTestWithGenRuleBuildComponents,
- android.MockFS{
- "APP_NOTICE": nil,
- "GENRULE_NOTICE": nil,
- "LIB_NOTICE": nil,
- "TOOL_NOTICE": nil,
- }.AddToFixture(),
- ).RunTestWithBp(t, `
- android_app {
- name: "foo",
- srcs: ["a.java"],
- static_libs: ["javalib"],
- jni_libs: ["libjni"],
- notice: "APP_NOTICE",
- embed_notices: true,
- sdk_version: "current",
- }
-
- // No embed_notice flag
- android_app {
- name: "bar",
- srcs: ["a.java"],
- jni_libs: ["libjni"],
- notice: "APP_NOTICE",
- sdk_version: "current",
- }
-
- // No NOTICE files
- android_app {
- name: "baz",
- srcs: ["a.java"],
- embed_notices: true,
- sdk_version: "current",
- }
-
- cc_library {
- name: "libjni",
- system_shared_libs: [],
- stl: "none",
- notice: "LIB_NOTICE",
- sdk_version: "current",
- }
-
- java_library {
- name: "javalib",
- srcs: [
- ":gen",
- ],
- sdk_version: "current",
- }
-
- genrule {
- name: "gen",
- tools: ["gentool"],
- out: ["gen.java"],
- notice: "GENRULE_NOTICE",
- }
-
- java_binary_host {
- name: "gentool",
- srcs: ["b.java"],
- notice: "TOOL_NOTICE",
- }
- `)
-
- // foo has NOTICE files to process, and embed_notices is true.
- foo := result.ModuleForTests("foo", "android_common")
- // verify merge notices rule.
- mergeNotices := foo.Rule("mergeNoticesRule")
- noticeInputs := mergeNotices.Inputs.Strings()
- // TOOL_NOTICE should be excluded as it's a host module.
- if len(mergeNotices.Inputs) != 3 {
- t.Errorf("number of input notice files: expected = 3, actual = %q", noticeInputs)
- }
- if !inList("APP_NOTICE", noticeInputs) {
- t.Errorf("APP_NOTICE is missing from notice files, %q", noticeInputs)
- }
- if !inList("LIB_NOTICE", noticeInputs) {
- t.Errorf("LIB_NOTICE is missing from notice files, %q", noticeInputs)
- }
- if !inList("GENRULE_NOTICE", noticeInputs) {
- t.Errorf("GENRULE_NOTICE is missing from notice files, %q", noticeInputs)
- }
- // aapt2 flags should include -A <NOTICE dir> so that its contents are put in the APK's /assets.
- res := foo.Output("package-res.apk")
- aapt2Flags := res.Args["flags"]
- e := "-A out/soong/.intermediates/foo/android_common/NOTICE"
- android.AssertStringDoesContain(t, "expected.apkPath", aapt2Flags, e)
-
- // bar has NOTICE files to process, but embed_notices is not set.
- bar := result.ModuleForTests("bar", "android_common")
- res = bar.Output("package-res.apk")
- aapt2Flags = res.Args["flags"]
- e = "-A out/soong/.intermediates/bar/android_common/NOTICE"
- android.AssertStringDoesNotContain(t, "bar shouldn't have the asset dir flag for NOTICE", aapt2Flags, e)
-
- // baz's embed_notice is true, but it doesn't have any NOTICE files.
- baz := result.ModuleForTests("baz", "android_common")
- res = baz.Output("package-res.apk")
- aapt2Flags = res.Args["flags"]
- e = "-A out/soong/.intermediates/baz/android_common/NOTICE"
- if strings.Contains(aapt2Flags, e) {
- t.Errorf("baz shouldn't have the asset dir flag for NOTICE: %q", e)
- }
-}
-
func TestUncompressDex(t *testing.T) {
testCases := []struct {
name string
diff --git a/java/config/config.go b/java/config/config.go
index 05dfde6..262c531 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -50,11 +50,6 @@
"core-icu4j",
"core-oj",
"core-libart",
- // TODO: Could this be all updatable bootclasspath jars?
- "updatable-media",
- "framework-mediaprovider",
- "framework-sdkextensions",
- "android.net.ipsec.ike",
}
)
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 2921c3e..e7aeeb8 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -477,7 +477,7 @@
Flag("--format=v2").
FlagWithArg("--repeat-errors-max ", "10").
FlagWithArg("--hide ", "UnresolvedImport").
- FlagWithArg("--hide ", "InvalidNullability").
+ FlagWithArg("--hide ", "InvalidNullabilityOverride").
// b/223382732
FlagWithArg("--hide ", "ChangedDefault")
diff --git a/java/java.go b/java/java.go
index 5afa2db..713fe94 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2225,8 +2225,16 @@
// java_import bp2Build converter.
func (i *Import) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
- //TODO(b/209577426): Support multiple arch variants
- jars := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, i.properties.Jars, []string(nil)))
+ var jars bazel.LabelListAttribute
+ archVariantProps := i.GetArchVariantProperties(ctx, &ImportProperties{})
+ for axis, configToProps := range archVariantProps {
+ for config, _props := range configToProps {
+ if archProps, ok := _props.(*ImportProperties); ok {
+ archJars := android.BazelLabelForModuleSrcExcludes(ctx, archProps.Jars, []string(nil))
+ jars.SetSelectValue(axis, config, archJars)
+ }
+ }
+ }
attrs := &bazelJavaImportAttributes{
Jars: jars,
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index c881751..950a1e5 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -77,7 +77,7 @@
"addprefix": &simpleCallParser{name: baseName + ".addprefix", returnType: starlarkTypeList},
"addsuffix": &simpleCallParser{name: baseName + ".addsuffix", returnType: starlarkTypeList},
"copy-files": &simpleCallParser{name: baseName + ".copy_files", returnType: starlarkTypeList},
- "dir": &simpleCallParser{name: baseName + ".dir", returnType: starlarkTypeList},
+ "dir": &simpleCallParser{name: baseName + ".dir", returnType: starlarkTypeString},
"dist-for-goals": &simpleCallParser{name: baseName + ".mkdist_for_goals", returnType: starlarkTypeVoid, addGlobals: true},
"enforce-product-packages-exist": &simpleCallParser{name: baseName + ".enforce_product_packages_exist", returnType: starlarkTypeVoid},
"error": &makeControlFuncParser{name: baseName + ".mkerror"},
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index 31739fa..2b447e3 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -793,7 +793,7 @@
PRODUCT_COPY_FILES := $(addsuffix .sff, a b c)
PRODUCT_NAME := $(word 1, $(subst ., ,$(TARGET_BOARD_PLATFORM)))
$(info $(patsubst %.pub,$(PRODUCT_NAME)%,$(PRODUCT_ADB_KEYS)))
-$(info $(dir foo/bar))
+$(info $$(dir foo/bar): $(dir foo/bar))
$(info $(firstword $(PRODUCT_COPY_FILES)))
$(info $(lastword $(PRODUCT_COPY_FILES)))
$(info $(dir $(lastword $(MAKEFILE_LIST))))
@@ -816,7 +816,7 @@
cfg["PRODUCT_COPY_FILES"] = rblf.addsuffix(".sff", "a b c")
cfg["PRODUCT_NAME"] = ((g.get("TARGET_BOARD_PLATFORM", "")).replace(".", " ")).split()[0]
rblf.mkinfo("product.mk", rblf.mkpatsubst("%.pub", "%s%%" % cfg["PRODUCT_NAME"], g.get("PRODUCT_ADB_KEYS", "")))
- rblf.mkinfo("product.mk", rblf.dir("foo/bar"))
+ rblf.mkinfo("product.mk", "$(dir foo/bar): %s" % rblf.dir("foo/bar"))
rblf.mkinfo("product.mk", cfg["PRODUCT_COPY_FILES"][0])
rblf.mkinfo("product.mk", cfg["PRODUCT_COPY_FILES"][-1])
rblf.mkinfo("product.mk", rblf.dir("product.mk"))
diff --git a/scripts/generate-notice-files.py b/scripts/generate-notice-files.py
deleted file mode 100755
index 1b4acfa..0000000
--- a/scripts/generate-notice-files.py
+++ /dev/null
@@ -1,272 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2012 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.
-"""
-Usage: generate-notice-files --text-output [plain text output file] \
- --html-output [html output file] \
- --xml-output [xml output file] \
- -t [file title] -s [directory of notices]
-
-Generate the Android notice files, including both text and html files.
-
--h to display this usage message and exit.
-"""
-from collections import defaultdict
-import argparse
-import hashlib
-import itertools
-import os
-import os.path
-import re
-import struct
-import sys
-
-MD5_BLOCKSIZE = 1024 * 1024
-HTML_ESCAPE_TABLE = {
- b"&": b"&",
- b'"': b""",
- b"'": b"'",
- b">": b">",
- b"<": b"<",
- }
-
-def md5sum(filename):
- """Calculate an MD5 of the file given by FILENAME,
- and return hex digest as a string.
- Output should be compatible with md5sum command"""
-
- f = open(filename, "rb")
- sum = hashlib.md5()
- while 1:
- block = f.read(MD5_BLOCKSIZE)
- if not block:
- break
- sum.update(block)
- f.close()
- return sum.hexdigest()
-
-
-def html_escape(text):
- """Produce entities within text."""
- # Using for i in text doesn't work since i will be an int, not a byte.
- # There are multiple ways to solve this, but the most performant way
- # to iterate over a byte array is to use unpack. Using the
- # for i in range(len(text)) and using that to get a byte using array
- # slices is twice as slow as this method.
- return b"".join(HTML_ESCAPE_TABLE.get(i,i) for i in struct.unpack(str(len(text)) + 'c', text))
-
-HTML_OUTPUT_CSS=b"""
-<style type="text/css">
-body { padding: 0; font-family: sans-serif; }
-.same-license { background-color: #eeeeee; border-top: 20px solid white; padding: 10px; }
-.label { font-weight: bold; }
-.file-list { margin-left: 1em; color: blue; }
-</style>
-
-"""
-
-def combine_notice_files_html(file_hash, input_dir, output_filename):
- """Combine notice files in FILE_HASH and output a HTML version to OUTPUT_FILENAME."""
-
- SRC_DIR_STRIP_RE = re.compile(input_dir + "(/.*).txt")
-
- # Set up a filename to row id table (anchors inside tables don't work in
- # most browsers, but href's to table row ids do)
- id_table = {}
- id_count = 0
- for value in file_hash:
- for filename in value:
- id_table[filename] = id_count
- id_count += 1
-
- # Open the output file, and output the header pieces
- output_file = open(output_filename, "wb")
-
- output_file.write(b"<html><head>\n")
- output_file.write(HTML_OUTPUT_CSS)
- output_file.write(b'</head><body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">\n')
-
- # Output our table of contents
- output_file.write(b'<div class="toc">\n')
- output_file.write(b"<ul>\n")
-
- # Flatten the list of lists into a single list of filenames
- sorted_filenames = sorted(itertools.chain.from_iterable(file_hash))
-
- # Print out a nice table of contents
- for filename in sorted_filenames:
- stripped_filename = SRC_DIR_STRIP_RE.sub(r"\1", filename)
- output_file.write(('<li><a href="#id%d">%s</a></li>\n' % (id_table.get(filename), stripped_filename)).encode())
-
- output_file.write(b"</ul>\n")
- output_file.write(b"</div><!-- table of contents -->\n")
- # Output the individual notice file lists
- output_file.write(b'<table cellpadding="0" cellspacing="0" border="0">\n')
- for value in file_hash:
- output_file.write(('<tr id="id%d"><td class="same-license">\n' % id_table.get(value[0])).encode())
- output_file.write(b'<div class="label">Notices for file(s):</div>\n')
- output_file.write(b'<div class="file-list">\n')
- for filename in value:
- output_file.write(("%s <br/>\n" % (SRC_DIR_STRIP_RE.sub(r"\1", filename))).encode())
- output_file.write(b"</div><!-- file-list -->\n\n")
- output_file.write(b'<pre class="license-text">\n')
- with open(value[0], "rb") as notice_file:
- output_file.write(html_escape(notice_file.read()))
- output_file.write(b"\n</pre><!-- license-text -->\n")
- output_file.write(b"</td></tr><!-- same-license -->\n\n\n\n")
-
- # Finish off the file output
- output_file.write(b"</table>\n")
- output_file.write(b"</body></html>\n")
- output_file.close()
-
-def combine_notice_files_text(file_hash, input_dir, output_filename, file_title):
- """Combine notice files in FILE_HASH and output a text version to OUTPUT_FILENAME."""
-
- SRC_DIR_STRIP_RE = re.compile(input_dir + "(/.*).txt")
- output_file = open(output_filename, "wb")
- output_file.write(file_title.encode())
- output_file.write(b"\n")
- for value in file_hash:
- output_file.write(b"============================================================\n")
- output_file.write(b"Notices for file(s):\n")
- for filename in value:
- output_file.write(SRC_DIR_STRIP_RE.sub(r"\1", filename).encode())
- output_file.write(b"\n")
- output_file.write(b"------------------------------------------------------------\n")
- with open(value[0], "rb") as notice_file:
- output_file.write(notice_file.read())
- output_file.write(b"\n")
- output_file.close()
-
-def combine_notice_files_xml(files_with_same_hash, input_dir, output_filename):
- """Combine notice files in FILE_HASH and output a XML version to OUTPUT_FILENAME."""
-
- SRC_DIR_STRIP_RE = re.compile(input_dir + "(/.*).txt")
-
- # Set up a filename to row id table (anchors inside tables don't work in
- # most browsers, but href's to table row ids do)
- id_table = {}
- for file_key, files in files_with_same_hash.items():
- for filename in files:
- id_table[filename] = file_key
-
- # Open the output file, and output the header pieces
- output_file = open(output_filename, "wb")
-
- output_file.write(b'<?xml version="1.0" encoding="utf-8"?>\n')
- output_file.write(b"<licenses>\n")
-
- # Flatten the list of lists into a single list of filenames
- sorted_filenames = sorted(list(id_table))
-
- # Print out a nice table of contents
- for filename in sorted_filenames:
- stripped_filename = SRC_DIR_STRIP_RE.sub(r"\1", filename)
- output_file.write(('<file-name contentId="%s">%s</file-name>\n' % (id_table.get(filename), stripped_filename)).encode())
- output_file.write(b"\n\n")
-
- processed_file_keys = []
- # Output the individual notice file lists
- for filename in sorted_filenames:
- file_key = id_table.get(filename)
- if file_key in processed_file_keys:
- continue
- processed_file_keys.append(file_key)
-
- output_file.write(('<file-content contentId="%s"><![CDATA[' % file_key).encode())
- with open(filename, "rb") as notice_file:
- output_file.write(html_escape(notice_file.read()))
- output_file.write(b"]]></file-content>\n\n")
-
- # Finish off the file output
- output_file.write(b"</licenses>\n")
- output_file.close()
-
-def get_args():
- parser = argparse.ArgumentParser()
- parser.add_argument(
- '--text-output', required=True,
- help='The text output file path.')
- parser.add_argument(
- '--html-output',
- help='The html output file path.')
- parser.add_argument(
- '--xml-output',
- help='The xml output file path.')
- parser.add_argument(
- '-t', '--title', required=True,
- help='The file title.')
- parser.add_argument(
- '-s', '--source-dir', required=True,
- help='The directory containing notices.')
- parser.add_argument(
- '-i', '--included-subdirs', action='append',
- help='The sub directories which should be included.')
- parser.add_argument(
- '-e', '--excluded-subdirs', action='append',
- help='The sub directories which should be excluded.')
- return parser.parse_args()
-
-def main(argv):
- args = get_args()
-
- txt_output_file = args.text_output
- html_output_file = args.html_output
- xml_output_file = args.xml_output
- file_title = args.title
- included_subdirs = []
- excluded_subdirs = []
- if args.included_subdirs is not None:
- included_subdirs = args.included_subdirs
- if args.excluded_subdirs is not None:
- excluded_subdirs = args.excluded_subdirs
-
- # Find all the notice files and md5 them
- input_dir = os.path.normpath(args.source_dir)
- files_with_same_hash = defaultdict(list)
- for root, dir, files in os.walk(input_dir):
- for file in files:
- matched = True
- if len(included_subdirs) > 0:
- matched = False
- for subdir in included_subdirs:
- if (root == (input_dir + '/' + subdir) or
- root.startswith(input_dir + '/' + subdir + '/')):
- matched = True
- break
- elif len(excluded_subdirs) > 0:
- for subdir in excluded_subdirs:
- if (root == (input_dir + '/' + subdir) or
- root.startswith(input_dir + '/' + subdir + '/')):
- matched = False
- break
- if matched and file.endswith(".txt"):
- filename = os.path.join(root, file)
- file_md5sum = md5sum(filename)
- files_with_same_hash[file_md5sum].append(filename)
-
- filesets = [sorted(files_with_same_hash[md5]) for md5 in sorted(list(files_with_same_hash))]
-
- combine_notice_files_text(filesets, input_dir, txt_output_file, file_title)
-
- if html_output_file is not None:
- combine_notice_files_html(filesets, input_dir, html_output_file)
-
- if xml_output_file is not None:
- combine_notice_files_xml(files_with_same_hash, input_dir, xml_output_file)
-
-if __name__ == "__main__":
- main(sys.argv)
diff --git a/scripts/hiddenapi/analyze_bcpf.py b/scripts/hiddenapi/analyze_bcpf.py
index 1ad8d07..595343b 100644
--- a/scripts/hiddenapi/analyze_bcpf.py
+++ b/scripts/hiddenapi/analyze_bcpf.py
@@ -319,12 +319,48 @@
@dataclasses.dataclass()
+class PackagePropertyReason:
+ """Provides the reasons why a package was added to a specific property.
+
+ A split package is one that contains classes from the bootclasspath_fragment
+ and other bootclasspath modules. So, for a split package this contains the
+ corresponding lists of classes.
+
+ A single package is one that contains classes sub-packages from the
+ For a split package this contains a list of classes in that package that are
+ provided by the bootclasspath_fragment and a list of classes
+ """
+
+ # The list of classes/sub-packages that is provided by the
+ # bootclasspath_fragment.
+ bcpf: typing.List[str]
+
+ # The list of classes/sub-packages that is provided by other modules on the
+ # bootclasspath.
+ other: typing.List[str]
+
+
+@dataclasses.dataclass()
class Result:
"""Encapsulates the result of the analysis."""
# The diffs in the flags.
diffs: typing.Optional[FlagDiffs] = None
+ # A map from package name to the reason why it belongs in the
+ # split_packages property.
+ split_packages: typing.Dict[str, PackagePropertyReason] = dataclasses.field(
+ default_factory=dict)
+
+ # A map from package name to the reason why it belongs in the
+ # single_packages property.
+ single_packages: typing.Dict[str,
+ PackagePropertyReason] = dataclasses.field(
+ default_factory=dict)
+
+ # The list of packages to add to the package_prefixes property.
+ package_prefixes: typing.List[str] = dataclasses.field(default_factory=list)
+
# The bootclasspath_fragment hidden API properties changes.
property_changes: typing.List[HiddenApiPropertyChange] = dataclasses.field(
default_factory=list)
@@ -394,13 +430,17 @@
def reformat_report_test(text):
return re.sub(r"(.)\n([^\s])", r"\1 \2", text)
- def report(self, text, **kwargs):
+ def report(self, text="", **kwargs):
# Concatenate lines that are not separated by a blank line together to
# eliminate formatting applied to the supplied text to adhere to python
# line length limitations.
text = self.reformat_report_test(text)
logging.info("%s", text, **kwargs)
+ def report_dedent(self, text, **kwargs):
+ text = textwrap.dedent(text)
+ self.report(text, **kwargs)
+
def run_command(self, cmd, *args, **kwargs):
cmd_line = " ".join(cmd)
logging.debug("Running %s", cmd_line)
@@ -442,9 +482,7 @@
def load_module_info(self):
module_info_file = os.path.join(self.product_out_dir,
"module-info.json")
- self.report(f"""
-Making sure that {module_info_file} is up to date.
-""")
+ self.report(f"\nMaking sure that {module_info_file} is up to date.\n")
output = self.build_file_read_output(module_info_file)
lines = output.lines()
for line in lines:
@@ -496,61 +534,62 @@
optimizations that can be applied.
"""
self.report(f"Analyzing bootclasspath_fragment module {self.bcpf}")
- self.report(f"""
-Run this tool to help initialize a bootclasspath_fragment module. Before you
-start make sure that:
+ self.report_dedent(f"""
+ Run this tool to help initialize a bootclasspath_fragment module.
+ Before you start make sure that:
-1. The current checkout is up to date.
+ 1. The current checkout is up to date.
-2. The environment has been initialized using lunch, e.g.
- lunch aosp_arm64-userdebug
+ 2. The environment has been initialized using lunch, e.g.
+ lunch aosp_arm64-userdebug
-3. You have added a bootclasspath_fragment module to the appropriate Android.bp
-file. Something like this:
+ 3. You have added a bootclasspath_fragment module to the appropriate
+ Android.bp file. Something like this:
- bootclasspath_fragment {{
- name: "{self.bcpf}",
- contents: [
- "...",
- ],
+ bootclasspath_fragment {{
+ name: "{self.bcpf}",
+ contents: [
+ "...",
+ ],
+
+ // The bootclasspath_fragments that provide APIs on which this
+ // depends.
+ fragments: [
+ {{
+ apex: "com.android.art",
+ module: "art-bootclasspath-fragment",
+ }},
+ ],
+ }}
+
+ 4. You have added it to the platform_bootclasspath module in
+ frameworks/base/boot/Android.bp. Something like this:
- // The bootclasspath_fragments that provide APIs on which this depends.
- fragments: [
- {{
- apex: "com.android.art",
- module: "art-bootclasspath-fragment",
- }},
- ],
- }}
+ platform_bootclasspath {{
+ name: "platform-bootclasspath",
+ fragments: [
+ ...
+ {{
+ apex: "{self.apex}",
+ module: "{self.bcpf}",
+ }},
+ ],
+ }}
-4. You have added it to the platform_bootclasspath module in
-frameworks/base/boot/Android.bp. Something like this:
+ 5. You have added an sdk module. Something like this:
- platform_bootclasspath {{
- name: "platform-bootclasspath",
- fragments: [
- ...
- {{
- apex: "{self.apex}",
- module: "{self.bcpf}",
- }},
- ],
- }}
-
-5. You have added an sdk module. Something like this:
-
- sdk {{
- name: "{self.sdk}",
- bootclasspath_fragments: ["{self.bcpf}"],
- }}
-""")
+ sdk {{
+ name: "{self.sdk}",
+ bootclasspath_fragments: ["{self.bcpf}"],
+ }}
+ """)
# Make sure that the module-info.json file is up to date.
self.load_module_info()
- self.report("""
-Cleaning potentially stale files.
-""")
+ self.report_dedent("""
+ Cleaning potentially stale files.
+ """)
# Remove the out/soong/hiddenapi files.
shutil.rmtree(f"{self.out_dir}/soong/hiddenapi", ignore_errors=True)
@@ -605,26 +644,26 @@
if result.file_changes:
if self.fix:
- file_change_message = """
-The following files were modified by this script:"""
+ file_change_message = textwrap.dedent("""
+ The following files were modified by this script:
+ """)
else:
- file_change_message = """
-The following modifications need to be made:"""
+ file_change_message = textwrap.dedent("""
+ The following modifications need to be made:
+ """)
- self.report(f"""
-{file_change_message}""")
+ self.report(file_change_message)
result.file_changes.sort()
for file_change in result.file_changes:
- self.report(f"""
- {file_change.path}
- {file_change.description}
-""".lstrip("\n"))
+ self.report(f" {file_change.path}")
+ self.report(f" {file_change.description}")
+ self.report()
if not self.fix:
- self.report("""
-Run the command again with the --fix option to automatically make the above
-changes.
-""".lstrip())
+ self.report_dedent("""
+ Run the command again with the --fix option to automatically
+ make the above changes.
+ """.lstrip("\n"))
def new_file_change(self, file, description):
return FileChange(
@@ -635,11 +674,10 @@
if not (module_line.startswith("< ") and
monolithic_line.startswith("> ") and not separator_line):
# Something went wrong.
- self.report(f"""Invalid build output detected:
- module_line: "{module_line}"
- monolithic_line: "{monolithic_line}"
- separator_line: "{separator_line}"
-""")
+ self.report("Invalid build output detected:")
+ self.report(f" module_line: '{module_line}'")
+ self.report(f" monolithic_line: '{monolithic_line}'")
+ self.report(f" separator_line: '{separator_line}'")
sys.exit(1)
if significant:
@@ -698,10 +736,9 @@
if module_signature != monolithic_signature:
# Something went wrong.
- self.report(f"""Inconsistent signatures detected:
- module_signature: "{module_signature}"
- monolithic_signature: "{monolithic_signature}"
-""")
+ self.report("Inconsistent signatures detected:")
+ self.report(f" module_signature: '{module_signature}'")
+ self.report(f" monolithic_signature: '{monolithic_signature}'")
sys.exit(1)
diffs[module_signature] = (module_flags, monolithic_flags)
@@ -749,85 +786,91 @@
return diffs
def build_monolithic_stubs_flags(self):
- self.report(f"""
-Attempting to build {_STUB_FLAGS_FILE} to verify that the
-bootclasspath_fragment has the correct API stubs available...
-""")
+ self.report_dedent(f"""
+ Attempting to build {_STUB_FLAGS_FILE} to verify that the
+ bootclasspath_fragment has the correct API stubs available...
+ """)
# Build the hiddenapi-stubs-flags.txt file.
diffs = self.build_hiddenapi_flags(_STUB_FLAGS_FILE)
if diffs:
- self.report(f"""
-There is a discrepancy between the stub API derived flags created by the
-bootclasspath_fragment and the platform_bootclasspath. See preceding error
-messages to see which flags are inconsistent. The inconsistencies can occur for
-a couple of reasons:
+ self.report_dedent(f"""
+ There is a discrepancy between the stub API derived flags
+ created by the bootclasspath_fragment and the
+ platform_bootclasspath. See preceding error messages to see
+ which flags are inconsistent. The inconsistencies can occur for
+ a couple of reasons:
-If you are building against prebuilts of the Android SDK, e.g. by using
-TARGET_BUILD_APPS then the prebuilt versions of the APIs this
-bootclasspath_fragment depends upon are out of date and need updating. See
-go/update-prebuilts for help.
+ If you are building against prebuilts of the Android SDK, e.g.
+ by using TARGET_BUILD_APPS then the prebuilt versions of the
+ APIs this bootclasspath_fragment depends upon are out of date
+ and need updating. See go/update-prebuilts for help.
-Otherwise, this is happening because there are some stub APIs that are either
-provided by or used by the contents of the bootclasspath_fragment but which are
-not available to it. There are 4 ways to handle this:
+ Otherwise, this is happening because there are some stub APIs
+ that are either provided by or used by the contents of the
+ bootclasspath_fragment but which are not available to it. There
+ are 4 ways to handle this:
-1. A java_sdk_library in the contents property will automatically make its stub
- APIs available to the bootclasspath_fragment so nothing needs to be done.
+ 1. A java_sdk_library in the contents property will
+ automatically make its stub APIs available to the
+ bootclasspath_fragment so nothing needs to be done.
-2. If the API provided by the bootclasspath_fragment is created by an api_only
- java_sdk_library (or a java_library that compiles files generated by a
- separate droidstubs module then it cannot be added to the contents and
- instead must be added to the api.stubs property, e.g.
+ 2. If the API provided by the bootclasspath_fragment is created
+ by an api_only java_sdk_library (or a java_library that compiles
+ files generated by a separate droidstubs module then it cannot
+ be added to the contents and instead must be added to the
+ api.stubs property, e.g.
- bootclasspath_fragment {{
- name: "{self.bcpf}",
- ...
- api: {{
- stubs: ["$MODULE-api-only"],"
- }},
- }}
+ bootclasspath_fragment {{
+ name: "{self.bcpf}",
+ ...
+ api: {{
+ stubs: ["$MODULE-api-only"],"
+ }},
+ }}
-3. If the contents use APIs provided by another bootclasspath_fragment then
- it needs to be added to the fragments property, e.g.
+ 3. If the contents use APIs provided by another
+ bootclasspath_fragment then it needs to be added to the
+ fragments property, e.g.
+
+ bootclasspath_fragment {{
+ name: "{self.bcpf}",
+ ...
+ // The bootclasspath_fragments that provide APIs on which this depends.
+ fragments: [
+ ...
+ {{
+ apex: "com.android.other",
+ module: "com.android.other-bootclasspath-fragment",
+ }},
+ ],
+ }}
+
+ 4. If the contents use APIs from a module that is not part of
+ another bootclasspath_fragment then it must be added to the
+ additional_stubs property, e.g.
- bootclasspath_fragment {{
- name: "{self.bcpf}",
- ...
- // The bootclasspath_fragments that provide APIs on which this depends.
- fragments: [
- ...
- {{
- apex: "com.android.other",
- module: "com.android.other-bootclasspath-fragment",
- }},
- ],
- }}
+ bootclasspath_fragment {{
+ name: "{self.bcpf}",
+ ...
+ additional_stubs: ["android-non-updatable"],
+ }}
-4. If the contents use APIs from a module that is not part of another
- bootclasspath_fragment then it must be added to the additional_stubs
- property, e.g.
+ Like the api.stubs property these are typically
+ java_sdk_library modules but can be java_library too.
- bootclasspath_fragment {{
- name: "{self.bcpf}",
- ...
- additional_stubs: ["android-non-updatable"],
- }}
-
- Like the api.stubs property these are typically java_sdk_library modules but
- can be java_library too.
-
- Note: The "android-non-updatable" is treated as if it was a java_sdk_library
- which it is not at the moment but will be in future.
-""")
+ Note: The "android-non-updatable" is treated as if it was a
+ java_sdk_library which it is not at the moment but will be in
+ future.
+ """)
return diffs
def build_monolithic_flags(self, result):
- self.report(f"""
-Attempting to build {_FLAGS_FILE} to verify that the
-bootclasspath_fragment has the correct hidden API flags...
-""")
+ self.report_dedent(f"""
+ Attempting to build {_FLAGS_FILE} to verify that the
+ bootclasspath_fragment has the correct hidden API flags...
+ """)
# Build the hiddenapi-flags.csv file and extract any differences in
# the flags between this bootclasspath_fragment and the monolithic
@@ -838,32 +881,34 @@
self.load_all_flags()
if result.diffs:
- self.report(f"""
-There is a discrepancy between the hidden API flags created by the
-bootclasspath_fragment and the platform_bootclasspath. See preceding error
-messages to see which flags are inconsistent. The inconsistencies can occur for
-a couple of reasons:
+ self.report_dedent(f"""
+ There is a discrepancy between the hidden API flags created by
+ the bootclasspath_fragment and the platform_bootclasspath. See
+ preceding error messages to see which flags are inconsistent.
+ The inconsistencies can occur for a couple of reasons:
-If you are building against prebuilts of this bootclasspath_fragment then the
-prebuilt version of the sdk snapshot (specifically the hidden API flag files)
-are inconsistent with the prebuilt version of the apex {self.apex}. Please
-ensure that they are both updated from the same build.
+ If you are building against prebuilts of this
+ bootclasspath_fragment then the prebuilt version of the sdk
+ snapshot (specifically the hidden API flag files) are
+ inconsistent with the prebuilt version of the apex {self.apex}.
+ Please ensure that they are both updated from the same build.
-1. There are custom hidden API flags specified in the one of the files in
- frameworks/base/boot/hiddenapi which apply to the bootclasspath_fragment but
- which are not supplied to the bootclasspath_fragment module.
+ 1. There are custom hidden API flags specified in the one of the
+ files in frameworks/base/boot/hiddenapi which apply to the
+ bootclasspath_fragment but which are not supplied to the
+ bootclasspath_fragment module.
-2. The bootclasspath_fragment specifies invalid "package_prefixes" or
- "split_packages" properties that match packages and classes that it does not
- provide.
-
-""")
+ 2. The bootclasspath_fragment specifies invalid
+ "split_packages", "single_packages" and/of "package_prefixes"
+ properties that match packages and classes that it does not
+ provide.
+ """)
# Check to see if there are any hiddenapi related properties that
# need to be added to the
- self.report("""
-Checking custom hidden API flags....
-""")
+ self.report_dedent("""
+ Checking custom hidden API flags....
+ """)
self.check_frameworks_base_boot_hidden_api_files(result)
def report_hidden_api_flag_file_changes(self, result, property_name,
@@ -1044,13 +1089,16 @@
""").strip("\n")
def analyze_hiddenapi_package_properties(self, result):
- split_packages, single_packages, package_prefixes = \
- self.compute_hiddenapi_package_properties()
+ self.compute_hiddenapi_package_properties(result)
+
+ def indent_lines(lines):
+ return "\n".join([f" {cls}" for cls in lines])
# TODO(b/202154151): Find those classes in split packages that are not
# part of an API, i.e. are an internal implementation class, and so
# can, and should, be safely moved out of the split packages.
+ split_packages = result.split_packages.keys()
result.property_changes.append(
HiddenApiPropertyChange(
property_name="split_packages",
@@ -1060,26 +1108,40 @@
))
if split_packages:
- self.report(f"""
-bootclasspath_fragment {self.bcpf} contains classes in packages that also
-contain classes provided by other sources, those packages are called split
-packages. Split packages should be avoided where possible but are often
-unavoidable when modularizing existing code.
+ self.report_dedent(f"""
+ bootclasspath_fragment {self.bcpf} contains classes in packages
+ that also contain classes provided by other bootclasspath
+ modules. Those packages are called split packages. Split
+ packages should be avoided where possible but are often
+ unavoidable when modularizing existing code.
-The hidden api processing needs to know which packages are split (and conversely
-which are not) so that it can optimize the hidden API flags to remove
-unnecessary implementation details.
+ The hidden api processing needs to know which packages are split
+ (and conversely which are not) so that it can optimize the
+ hidden API flags to remove unnecessary implementation details.
+
+ By default (for backwards compatibility) the
+ bootclasspath_fragment assumes that all packages are split
+ unless one of the package_prefixes or split_packages properties
+ are specified. While that is safe it is not optimal and can lead
+ to unnecessary implementation details leaking into the hidden
+ API flags. Adding an empty split_packages property allows the
+ flags to be optimized and remove any unnecessary implementation
+ details.
+ """)
+
+ for package in split_packages:
+ reason = result.split_packages[package]
+ self.report(f"""
+ Package {package} is split because while this bootclasspath_fragment
+ provides the following classes:
+{indent_lines(reason.bcpf)}
+
+ Other module(s) on the bootclasspath provides the following classes in
+ that package:
+{indent_lines(reason.other)}
""")
- self.report("""
-By default (for backwards compatibility) the bootclasspath_fragment assumes that
-all packages are split unless one of the package_prefixes or split_packages
-properties are specified. While that is safe it is not optimal and can lead to
-unnecessary implementation details leaking into the hidden API flags. Adding an
-empty split_packages property allows the flags to be optimized and remove any
-unnecessary implementation details.
-""")
-
+ single_packages = result.single_packages.keys()
if single_packages:
result.property_changes.append(
HiddenApiPropertyChange(
@@ -1091,10 +1153,34 @@
contain classes from other bootclasspath modules. Packages
should only be listed here when necessary for legacy
purposes, new packages should match a package prefix.
- """),
+ """),
action=PropertyChangeAction.REPLACE,
))
+ self.report_dedent(f"""
+ bootclasspath_fragment {self.bcpf} contains classes from
+ packages that has sub-packages which contain classes provided by
+ other bootclasspath modules. Those packages are called single
+ packages. Single packages should be avoided where possible but
+ are often unavoidable when modularizing existing code.
+
+ Because some sub-packages contains classes from other
+ bootclasspath modules it is not possible to use the package as a
+ package prefix as that treats the package and all its
+ sub-packages as being provided by this module.
+ """)
+ for package in single_packages:
+ reason = result.single_packages[package]
+ self.report(f"""
+ Package {package} is not a package prefix because while this
+ bootclasspath_fragment provides the following sub-packages:
+{indent_lines(reason.bcpf)}
+
+ Other module(s) on the bootclasspath provide the following sub-packages:
+{indent_lines(reason.other)}
+""")
+
+ package_prefixes = result.package_prefixes
if package_prefixes:
result.property_changes.append(
HiddenApiPropertyChange(
@@ -1111,38 +1197,40 @@
signature_patterns_files = signature_patterns_files.removeprefix(
self.top_dir)
- self.report(f"""
-The purpose of the hiddenapi split_packages and package_prefixes properties is
-to allow the removal of implementation details from the hidden API flags to
-reduce the coupling between sdk snapshots and the APEX runtime. It cannot
-eliminate that coupling completely though. Doing so may require changes to the
-code.
+ self.report_dedent(f"""
+ The purpose of the hiddenapi split_packages and package_prefixes
+ properties is to allow the removal of implementation details
+ from the hidden API flags to reduce the coupling between sdk
+ snapshots and the APEX runtime. It cannot eliminate that
+ coupling completely though. Doing so may require changes to the
+ code.
-This tool provides support for managing those properties but it cannot decide
-whether the set of package prefixes suggested is appropriate that needs the
-input of the developer.
+ This tool provides support for managing those properties but it
+ cannot decide whether the set of package prefixes suggested is
+ appropriate that needs the input of the developer.
-Please run the following command:
- m {signature_patterns_files}
+ Please run the following command:
+ m {signature_patterns_files}
-And then check the '{signature_patterns_files}' for any mention of
-implementation classes and packages (i.e. those classes/packages that do not
-contain any part of an API surface, including the hidden API). If they are
-found then the code should ideally be moved to a package unique to this module
-that is contained within a package that is part of an API surface.
+ And then check the '{signature_patterns_files}' for any mention
+ of implementation classes and packages (i.e. those
+ classes/packages that do not contain any part of an API surface,
+ including the hidden API). If they are found then the code
+ should ideally be moved to a package unique to this module that
+ is contained within a package that is part of an API surface.
-The format of the file is a list of patterns:
+ The format of the file is a list of patterns:
-* Patterns for split packages will list every class in that package.
+ * Patterns for split packages will list every class in that package.
-* Patterns for package prefixes will end with .../**.
+ * Patterns for package prefixes will end with .../**.
-* Patterns for packages which are not split but cannot use a package prefix
-because there are sub-packages which are provided by another module will end
-with .../*.
-""")
+ * Patterns for packages which are not split but cannot use a
+ package prefix because there are sub-packages which are provided
+ by another module will end with .../*.
+ """)
- def compute_hiddenapi_package_properties(self):
+ def compute_hiddenapi_package_properties(self, result):
trie = signature_trie()
# Populate the trie with the classes that are provided by the
# bootclasspath_fragment tagging them to make it clear where they
@@ -1151,6 +1239,7 @@
for class_name in sorted_classes:
trie.add(class_name + _FAKE_MEMBER, ClassProvider.BCPF)
+ # Now the same for monolithic classes.
monolithic_classes = set()
abs_flags_file = os.path.join(self.top_dir, _FLAGS_FILE)
with open(abs_flags_file, "r", encoding="utf8") as f:
@@ -1165,15 +1254,54 @@
only_if_matches=True)
monolithic_classes.add(class_name)
- split_packages = []
- single_packages = []
- package_prefixes = []
- self.recurse_hiddenapi_packages_trie(trie, split_packages,
- single_packages, package_prefixes)
- return split_packages, single_packages, package_prefixes
+ self.recurse_hiddenapi_packages_trie(trie, result)
- def recurse_hiddenapi_packages_trie(self, node, split_packages,
- single_packages, package_prefixes):
+ @staticmethod
+ def selector_to_java_reference(node):
+ return node.selector.replace("/", ".")
+
+ @staticmethod
+ def determine_reason_for_single_package(node):
+ bcpf_packages = []
+ other_packages = []
+
+ def recurse(n):
+ if n.type != "package":
+ return
+
+ providers = n.get_matching_rows("*")
+ package_ref = BcpfAnalyzer.selector_to_java_reference(n)
+ if ClassProvider.BCPF in providers:
+ bcpf_packages.append(package_ref)
+ else:
+ other_packages.append(package_ref)
+
+ children = n.child_nodes()
+ if children:
+ for child in children:
+ recurse(child)
+
+ recurse(node)
+ return PackagePropertyReason(bcpf=bcpf_packages, other=other_packages)
+
+ @staticmethod
+ def determine_reason_for_split_package(node):
+ bcpf_classes = []
+ other_classes = []
+ for child in node.child_nodes():
+ if child.type != "class":
+ continue
+
+ providers = child.values(lambda _: True)
+ class_ref = BcpfAnalyzer.selector_to_java_reference(child)
+ if ClassProvider.BCPF in providers:
+ bcpf_classes.append(class_ref)
+ else:
+ other_classes.append(class_ref)
+
+ return PackagePropertyReason(bcpf=bcpf_classes, other=other_classes)
+
+ def recurse_hiddenapi_packages_trie(self, node, result):
nodes = node.child_nodes()
if nodes:
for child in nodes:
@@ -1181,7 +1309,7 @@
if child.type != "package":
continue
- package = child.selector.replace("/", ".")
+ package = self.selector_to_java_reference(child)
providers = set(child.get_matching_rows("**"))
if not providers:
@@ -1192,7 +1320,7 @@
# The package and all its sub packages only contain
# classes provided by the bootclasspath_fragment.
logging.debug("Package '%s.**' is not split", package)
- package_prefixes.append(package)
+ result.package_prefixes.append(package)
# There is no point traversing into the sub packages.
continue
elif providers == {ClassProvider.OTHER}:
@@ -1217,8 +1345,17 @@
elif providers == {ClassProvider.BCPF}:
# The package only contains classes provided by the
# bootclasspath_fragment.
- logging.debug("Package '%s.*' is not split", package)
- single_packages.append(package)
+ logging.debug(
+ "Package '%s.*' is not split but does have "
+ "sub-packages from other modules", package)
+
+ # Partition the sub-packages into those that are provided by
+ # this bootclasspath_fragment and those provided by other
+ # modules. They can be used to explain the reason for the
+ # single package to developers.
+ reason = self.determine_reason_for_single_package(child)
+ result.single_packages[package] = reason
+
elif providers == {ClassProvider.OTHER}:
# The package contains no classes provided by the
# bootclasspath_fragment. Child nodes make contain such
@@ -1229,11 +1366,15 @@
# The package contains classes provided by both the
# bootclasspath_fragment and some other source.
logging.debug("Package '%s.*' is split", package)
- split_packages.append(package)
- self.recurse_hiddenapi_packages_trie(child, split_packages,
- single_packages,
- package_prefixes)
+ # Partition the classes in this split package into those
+ # that come from this bootclasspath_fragment and those that
+ # come from other modules. That can be used to explain the
+ # reason for the split package to developers.
+ reason = self.determine_reason_for_split_package(child)
+ result.split_packages[package] = reason
+
+ self.recurse_hiddenapi_packages_trie(child, result)
def newline_stripping_iter(iterator):
diff --git a/scripts/hiddenapi/analyze_bcpf_test.py b/scripts/hiddenapi/analyze_bcpf_test.py
index 650dd54..a32ffd0 100644
--- a/scripts/hiddenapi/analyze_bcpf_test.py
+++ b/scripts/hiddenapi/analyze_bcpf_test.py
@@ -377,6 +377,7 @@
La/b/c/D;->m()V
La/b/c/E;->m()V
La/b/c/d/E;->m()V
+La/b/c/d/e/F;->m()V
Lb/c/D;->m()V
Lb/c/E;->m()V
Lb/c/d/E;->m()V
@@ -385,11 +386,21 @@
analyzer = self.create_analyzer_for_test(fs)
analyzer.load_all_flags()
- split_packages, single_packages, package_prefixes = \
- analyzer.compute_hiddenapi_package_properties()
- self.assertEqual(["a.b"], split_packages)
- self.assertEqual(["a.b.c"], single_packages)
- self.assertEqual(["b"], package_prefixes)
+ result = ab.Result()
+ analyzer.compute_hiddenapi_package_properties(result)
+ self.assertEqual(["a.b"], list(result.split_packages.keys()))
+
+ reason = result.split_packages["a.b"]
+ self.assertEqual(["a.b.C"], reason.bcpf)
+ self.assertEqual(["a.b.D", "a.b.E"], reason.other)
+
+ self.assertEqual(["a.b.c"], list(result.single_packages.keys()))
+
+ reason = result.single_packages["a.b.c"]
+ self.assertEqual(["a.b.c"], reason.bcpf)
+ self.assertEqual(["a.b.c.d", "a.b.c.d.e"], reason.other)
+
+ self.assertEqual(["b"], result.package_prefixes)
class TestHiddenApiPropertyChange(unittest.TestCase):
diff --git a/scripts/hiddenapi/signature_trie.py b/scripts/hiddenapi/signature_trie.py
index e813a97..3650fa1 100644
--- a/scripts/hiddenapi/signature_trie.py
+++ b/scripts/hiddenapi/signature_trie.py
@@ -45,7 +45,9 @@
:return: A list of iterables of all the values associated with
this node and its children.
"""
- raise NotImplementedError("Please Implement this method")
+ values = []
+ self.append_values(values, selector)
+ return values
def append_values(self, values, selector):
"""Append the values associated with this node and its children.
@@ -313,12 +315,8 @@
node = node.nodes[element]
else:
return []
- return chain.from_iterable(node.values(selector))
- def values(self, selector):
- values = []
- self.append_values(values, selector)
- return values
+ return node.values(selector)
def append_values(self, values, selector):
for key, node in self.nodes.items():
@@ -336,11 +334,8 @@
# The value associated with this leaf.
value: typing.Any
- def values(self, selector):
- return [[self.value]]
-
def append_values(self, values, selector):
- values.append([self.value])
+ values.append(self.value)
def child_nodes(self):
return []
diff --git a/scripts/hiddenapi/signature_trie_test.py b/scripts/hiddenapi/signature_trie_test.py
index 1295691..6d4e660 100755
--- a/scripts/hiddenapi/signature_trie_test.py
+++ b/scripts/hiddenapi/signature_trie_test.py
@@ -150,6 +150,27 @@
str(context.exception))
+class TestValues(unittest.TestCase):
+ def test_add_then_get(self):
+ trie = signature_trie()
+ trie.add("La/b/C;->l()", 1)
+ trie.add("La/b/C$D;->m()", "A")
+ trie.add("La/b/C$D;->n()", {})
+
+ package_a_node = next(iter(trie.child_nodes()))
+ self.assertEqual("package", package_a_node.type)
+ self.assertEqual("a", package_a_node.selector)
+
+ package_b_node = next(iter(package_a_node.child_nodes()))
+ self.assertEqual("package", package_b_node.type)
+ self.assertEqual("a/b", package_b_node.selector)
+
+ class_c_node = next(iter(package_b_node.child_nodes()))
+ self.assertEqual("class", class_c_node.type)
+ self.assertEqual("a/b/C", class_c_node.selector)
+
+ self.assertEqual([1, "A", {}], class_c_node.values(lambda _: True))
+
class TestGetMatchingRows(unittest.TestCase):
extractInput = """
Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
diff --git a/scripts/mergenotice.py b/scripts/mergenotice.py
deleted file mode 100755
index fe99073..0000000
--- a/scripts/mergenotice.py
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2019 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.
-#
-"""
-Merges input notice files to the output file while ignoring duplicated files
-This script shouldn't be confused with build/soong/scripts/generate-notice-files.py
-which is responsible for creating the final notice file for all artifacts
-installed. This script has rather limited scope; it is meant to create a merged
-notice file for a set of modules that are packaged together, e.g. in an APEX.
-The merged notice file does not reveal the individual files in the package.
-"""
-
-import sys
-import argparse
-
-def get_args():
- parser = argparse.ArgumentParser(description='Merge notice files.')
- parser.add_argument('--output', help='output file path.')
- parser.add_argument('inputs', metavar='INPUT', nargs='+',
- help='input notice file')
- return parser.parse_args()
-
-def main(argv):
- args = get_args()
-
- processed = set()
- with open(args.output, 'w+') as output:
- for input in args.inputs:
- with open(input, 'r') as f:
- data = f.read().strip()
- if data not in processed:
- processed.add(data)
- output.write('%s\n\n' % data)
-
-if __name__ == '__main__':
- main(sys.argv)