diff options
-rw-r--r-- | README.md | 8 | ||||
-rw-r--r-- | android/config.go | 4 | ||||
-rw-r--r-- | android/mutator.go | 1 | ||||
-rw-r--r-- | android/variable.go | 2 | ||||
-rw-r--r-- | android/visibility.go | 174 | ||||
-rw-r--r-- | android/visibility_test.go | 361 | ||||
-rw-r--r-- | dexpreopt/config.go | 62 | ||||
-rw-r--r-- | dexpreopt/dexpreopt.go | 51 | ||||
-rw-r--r-- | dexpreopt/dexpreopt_test.go | 2 | ||||
-rw-r--r-- | java/app.go | 173 | ||||
-rw-r--r-- | java/app_builder.go | 11 | ||||
-rw-r--r-- | java/app_test.go | 80 | ||||
-rw-r--r-- | java/builder.go | 9 | ||||
-rw-r--r-- | java/config/config.go | 2 | ||||
-rw-r--r-- | java/config/makevars.go | 8 | ||||
-rw-r--r-- | java/dexpreopt.go | 15 | ||||
-rw-r--r-- | java/droiddoc.go | 7 | ||||
-rw-r--r-- | java/hiddenapi_singleton.go | 2 | ||||
-rw-r--r-- | java/jacoco.go | 2 | ||||
-rw-r--r-- | java/java.go | 9 | ||||
-rw-r--r-- | java/java_test.go | 2 | ||||
-rw-r--r-- | java/system_modules.go | 2 | ||||
-rw-r--r-- | java/testing.go | 27 | ||||
-rw-r--r-- | scripts/build_broken_logs.go | 5 | ||||
-rw-r--r-- | ui/build/dumpvars.go | 1 |
25 files changed, 835 insertions, 185 deletions
@@ -81,6 +81,8 @@ types are: Maps may values of any type, including nested maps. Lists and maps may have trailing commas after the last value. +Strings can contain double quotes using `\"`, for example `"cat \"a b\""`. + ### Operators Strings, lists of strings, and maps can be appended using the `+` operator. @@ -195,8 +197,10 @@ where `//project` is the module's package. e.g. using `[":__subpackages__"]` in * `["//visibility:legacy_public"]`: The default visibility, behaves as `//visibility:public` for now. It is an error if it is used in a module. -The visibility rules of `//visibility:public` and `//visibility:private` can -not be combined with any other visibility specifications. +The visibility rules of `//visibility:public` and `//visibility:private` can not +be combined with any other visibility specifications, except +`//visibility:public` is allowed to override visibility specifications imported +through the `defaults` property. Packages outside `vendor/` cannot make themselves visible to specific packages in `vendor/`, e.g. a module in `libcore` cannot declare that it is visible to diff --git a/android/config.go b/android/config.go index 1507c252a..b0d8b7fca 100644 --- a/android/config.go +++ b/android/config.go @@ -1103,6 +1103,10 @@ func (c *config) ProductCompatibleProperty() bool { return Bool(c.productVariables.ProductCompatibleProperty) } +func (c *config) MissingUsesLibraries() []string { + return c.productVariables.MissingUsesLibraries +} + func (c *deviceConfig) BoardVndkRuntimeDisable() bool { return Bool(c.config.productVariables.BoardVndkRuntimeDisable) } diff --git a/android/mutator.go b/android/mutator.go index 085c055e1..0e802493f 100644 --- a/android/mutator.go +++ b/android/mutator.go @@ -76,6 +76,7 @@ var preArch = []RegisterMutatorFunc{ registerLoadHookMutator, RegisterNamespaceMutator, RegisterPrebuiltsPreArchMutators, + registerVisibilityRuleChecker, RegisterDefaultsPreArchMutators, registerVisibilityRuleGatherer, } diff --git a/android/variable.go b/android/variable.go index ff3ebaf00..d039a1659 100644 --- a/android/variable.go +++ b/android/variable.go @@ -306,6 +306,8 @@ type productVariables struct { ProductCompatibleProperty *bool `json:",omitempty"` TargetFSConfigGen []string `json:",omitempty"` + + MissingUsesLibraries []string `json:",omitempty"` } func boolPtr(v bool) *bool { diff --git a/android/visibility.go b/android/visibility.go index 36b6f35f8..c7ef1dae6 100644 --- a/android/visibility.go +++ b/android/visibility.go @@ -71,7 +71,17 @@ type visibilityRule interface { String() string } -// A compositeRule is a visibility rule composed from other visibility rules. +// A compositeRule is a visibility rule composed from a list of atomic visibility rules. +// +// The list corresponds to the list of strings in the visibility property after defaults expansion. +// Even though //visibility:public is not allowed together with other rules in the visibility list +// of a single module, it is allowed here to permit a module to override an inherited visibility +// spec with public visibility. +// +// //visibility:private is not allowed in the same way, since we'd need to check for it during the +// defaults expansion to make that work. No non-private visibility rules are allowed in a +// compositeRule containing a privateRule. +// // This array will only be [] if all the rules are invalid and will behave as if visibility was // ["//visibility:private"]. type compositeRule []visibilityRule @@ -126,6 +136,28 @@ func (r subpackagesRule) String() string { return fmt.Sprintf("//%s:__subpackages__", r.pkgPrefix) } +// visibilityRule for //visibility:public +type publicRule struct{} + +func (r publicRule) matches(_ qualifiedModuleName) bool { + return true +} + +func (r publicRule) String() string { + return "//visibility:public" +} + +// visibilityRule for //visibility:private +type privateRule struct{} + +func (r privateRule) matches(_ qualifiedModuleName) bool { + return false +} + +func (r privateRule) String() string { + return "//visibility:private" +} + var visibilityRuleMap = NewOnceKey("visibilityRuleMap") // The map from qualifiedModuleName to visibilityRule. @@ -135,8 +167,15 @@ func moduleToVisibilityRuleMap(ctx BaseModuleContext) *sync.Map { }).(*sync.Map) } +// The rule checker needs to be registered before defaults expansion to correctly check that +// //visibility:xxx isn't combined with other packages in the same list in any one module. +func registerVisibilityRuleChecker(ctx RegisterMutatorsContext) { + ctx.BottomUp("visibilityRuleChecker", visibilityRuleChecker).Parallel() +} + // Visibility is not dependent on arch so this must be registered before the arch phase to avoid -// having to process multiple variants for each module. +// having to process multiple variants for each module. This goes after defaults expansion to gather +// the complete visibility lists from flat lists. func registerVisibilityRuleGatherer(ctx RegisterMutatorsContext) { ctx.BottomUp("visibilityRuleGatherer", visibilityRuleGatherer).Parallel() } @@ -146,39 +185,35 @@ func registerVisibilityRuleEnforcer(ctx RegisterMutatorsContext) { ctx.TopDown("visibilityRuleEnforcer", visibilityRuleEnforcer).Parallel() } -// Gathers the visibility rules, parses the visibility properties, stores them in a map by -// qualifiedModuleName for retrieval during enforcement. -// -// See ../README.md#Visibility for information on the format of the visibility rules. - -func visibilityRuleGatherer(ctx BottomUpMutatorContext) { - m, ok := ctx.Module().(Module) - if !ok { - return - } - +// Checks the per-module visibility rule lists before defaults expansion. +func visibilityRuleChecker(ctx BottomUpMutatorContext) { qualified := createQualifiedModuleName(ctx) - - visibility := m.base().commonProperties.Visibility - if visibility != nil { - rule := parseRules(ctx, qualified.pkg, visibility) - if rule != nil { - moduleToVisibilityRuleMap(ctx).Store(qualified, rule) + if d, ok := ctx.Module().(Defaults); ok { + // Defaults modules don't store the payload properties in m.base(). + for _, props := range d.properties() { + if cp, ok := props.(*commonProperties); ok { + if visibility := cp.Visibility; visibility != nil { + checkRules(ctx, qualified.pkg, visibility) + } + } + } + } else if m, ok := ctx.Module().(Module); ok { + if visibility := m.base().commonProperties.Visibility; visibility != nil { + checkRules(ctx, qualified.pkg, visibility) } } } -func parseRules(ctx BottomUpMutatorContext, currentPkg string, visibility []string) compositeRule { +func checkRules(ctx BottomUpMutatorContext, currentPkg string, visibility []string) { ruleCount := len(visibility) if ruleCount == 0 { // This prohibits an empty list as its meaning is unclear, e.g. it could mean no visibility and // it could mean public visibility. Requiring at least one rule makes the owner's intent // clearer. ctx.PropertyErrorf("visibility", "must contain at least one visibility rule") - return nil + return } - rules := make(compositeRule, 0, ruleCount) for _, v := range visibility { ok, pkg, name := splitRule(ctx, v, currentPkg) if !ok { @@ -192,23 +227,19 @@ func parseRules(ctx BottomUpMutatorContext, currentPkg string, visibility []stri } if pkg == "visibility" { - if ruleCount != 1 { - ctx.PropertyErrorf("visibility", "cannot mix %q with any other visibility rules", v) - continue - } switch name { - case "private": - rules = append(rules, packageRule{currentPkg}) - continue - case "public": - return nil + case "private", "public": case "legacy_public": ctx.PropertyErrorf("visibility", "//visibility:legacy_public must not be used") - return nil + continue default: ctx.PropertyErrorf("visibility", "unrecognized visibility rule %q", v) continue } + if ruleCount != 1 { + ctx.PropertyErrorf("visibility", "cannot mix %q with any other visibility rules", v) + continue + } } // If the current directory is not in the vendor tree then there are some additional @@ -221,22 +252,76 @@ func parseRules(ctx BottomUpMutatorContext, currentPkg string, visibility []stri continue } } + } +} - // Create the rule - var r visibilityRule - switch name { - case "__pkg__": - r = packageRule{pkg} - case "__subpackages__": - r = subpackagesRule{pkg} - default: - ctx.PropertyErrorf("visibility", "unrecognized visibility rule %q", v) +// Gathers the flattened visibility rules after defaults expansion, parses the visibility +// properties, stores them in a map by qualifiedModuleName for retrieval during enforcement. +// +// See ../README.md#Visibility for information on the format of the visibility rules. +func visibilityRuleGatherer(ctx BottomUpMutatorContext) { + m, ok := ctx.Module().(Module) + if !ok { + return + } + + qualified := createQualifiedModuleName(ctx) + + visibility := m.base().commonProperties.Visibility + if visibility != nil { + rule := parseRules(ctx, qualified.pkg, visibility) + if rule != nil { + moduleToVisibilityRuleMap(ctx).Store(qualified, rule) + } + } +} + +func parseRules(ctx BottomUpMutatorContext, currentPkg string, visibility []string) compositeRule { + rules := make(compositeRule, 0, len(visibility)) + hasPrivateRule := false + hasNonPrivateRule := false + for _, v := range visibility { + ok, pkg, name := splitRule(ctx, v, currentPkg) + if !ok { continue } + var r visibilityRule + isPrivateRule := false + if pkg == "visibility" { + switch name { + case "private": + r = privateRule{} + isPrivateRule = true + case "public": + r = publicRule{} + } + } else { + switch name { + case "__pkg__": + r = packageRule{pkg} + case "__subpackages__": + r = subpackagesRule{pkg} + default: + continue + } + } + + if isPrivateRule { + hasPrivateRule = true + } else { + hasNonPrivateRule = true + } + rules = append(rules, r) } + if hasPrivateRule && hasNonPrivateRule { + ctx.PropertyErrorf("visibility", + "cannot mix \"//visibility:private\" with any other visibility rules") + return compositeRule{privateRule{}} + } + return rules } @@ -274,8 +359,7 @@ func splitRule(ctx BaseModuleContext, ruleExpression string, currentPkg string) } func visibilityRuleEnforcer(ctx TopDownMutatorContext) { - _, ok := ctx.Module().(Module) - if !ok { + if _, ok := ctx.Module().(Module); !ok { return } @@ -297,9 +381,7 @@ func visibilityRuleEnforcer(ctx TopDownMutatorContext) { rule, ok := moduleToVisibilityRule.Load(depQualified) if ok { if !rule.(compositeRule).matches(qualified) { - ctx.ModuleErrorf( - "depends on %s which is not visible to this module; %s is only visible to %s", - depQualified, depQualified, rule) + ctx.ModuleErrorf("depends on %s which is not visible to this module", depQualified) } } }) diff --git a/android/visibility_test.go b/android/visibility_test.go index ea5316ced..09c5b1b34 100644 --- a/android/visibility_test.go +++ b/android/visibility_test.go @@ -91,7 +91,7 @@ var visibilityTests = []struct { expectedErrors: []string{`unrecognized visibility rule "//visibility:unknown"`}, }, { - name: "//visibility:public mixed", + name: "//visibility:xxx mixed", fs: map[string][]byte{ "top/Blueprints": []byte(` mock_library { @@ -105,10 +105,10 @@ var visibilityTests = []struct { }`), }, expectedErrors: []string{ - `module "libother" variant "android_common": visibility: cannot mix "//visibility:private"` + + `module "libother": visibility: cannot mix "//visibility:private"` + + ` with any other visibility rules`, + `module "libexample": visibility: cannot mix "//visibility:public"` + ` with any other visibility rules`, - `module "libexample" variant "android_common": visibility: cannot mix` + - ` "//visibility:public" with any other visibility rules`, }, }, { @@ -121,7 +121,7 @@ var visibilityTests = []struct { }`), }, expectedErrors: []string{ - `module "libexample" variant "android_common": visibility: //visibility:legacy_public must` + + `module "libexample": visibility: //visibility:legacy_public must` + ` not be used`, }, }, @@ -153,33 +153,6 @@ var visibilityTests = []struct { }, }, { - // Verify that //visibility:public will allow the module to be referenced from anywhere, e.g. - // the current directory, a nested directory and a directory in a separate tree. - name: "//visibility:public", - fs: map[string][]byte{ - "top/Blueprints": []byte(` - mock_library { - name: "libexample", - visibility: ["//visibility:public"], - } - - mock_library { - name: "libsamepackage", - deps: ["libexample"], - }`), - "top/nested/Blueprints": []byte(` - mock_library { - name: "libnested", - deps: ["libexample"], - }`), - "other/Blueprints": []byte(` - mock_library { - name: "libother", - deps: ["libexample"], - }`), - }, - }, - { // Verify that //visibility:private allows the module to be referenced from the current // directory only. name: "//visibility:private", @@ -207,9 +180,9 @@ var visibilityTests = []struct { }, expectedErrors: []string{ `module "libnested" variant "android_common": depends on //top:libexample which is not` + - ` visible to this module; //top:libexample is only visible to \[//top:__pkg__\]`, + ` visible to this module`, `module "libother" variant "android_common": depends on //top:libexample which is not` + - ` visible to this module; //top:libexample is only visible to \[//top:__pkg__\]`, + ` visible to this module`, }, }, { @@ -239,9 +212,9 @@ var visibilityTests = []struct { }, expectedErrors: []string{ `module "libnested" variant "android_common": depends on //top:libexample which is not` + - ` visible to this module; //top:libexample is only visible to \[//top:__pkg__\]`, + ` visible to this module`, `module "libother" variant "android_common": depends on //top:libexample which is not` + - ` visible to this module; //top:libexample is only visible to \[//top:__pkg__\]`, + ` visible to this module`, }, }, { @@ -277,9 +250,9 @@ var visibilityTests = []struct { }, expectedErrors: []string{ `module "libother" variant "android_common": depends on //top:libexample which is not` + - ` visible to this module; //top:libexample is only visible to \[//top/nested:__pkg__\]`, + ` visible to this module`, `module "libnestedagain" variant "android_common": depends on //top:libexample which is not` + - ` visible to this module; //top:libexample is only visible to \[//top/nested:__pkg__\]`, + ` visible to this module`, }, }, { @@ -310,7 +283,7 @@ var visibilityTests = []struct { }, expectedErrors: []string{ `module "libother" variant "android_common": depends on //top:libexample which is not` + - ` visible to this module; //top:libexample is only visible to \[//top:__subpackages__\]`, + ` visible to this module`, }, }, { @@ -341,8 +314,7 @@ var visibilityTests = []struct { }, expectedErrors: []string{ `module "libother" variant "android_common": depends on //top:libexample which is not` + - ` visible to this module; //top:libexample is only visible to` + - ` \[//top/nested:__subpackages__, //other:__pkg__\]`, + ` visible to this module`, }, }, { @@ -399,11 +371,295 @@ var visibilityTests = []struct { }`), }, expectedErrors: []string{ - `module "libsamepackage" variant "android_common": visibility: "//vendor/apps/AcmeSettings"` + + `module "libsamepackage": visibility: "//vendor/apps/AcmeSettings"` + ` is not allowed. Packages outside //vendor cannot make themselves visible to specific` + ` targets within //vendor, they can only use //vendor:__subpackages__.`, }, }, + + // Defaults propagation tests + { + // Check that visibility is the union of the defaults modules. + name: "defaults union, basic", + fs: map[string][]byte{ + "top/Blueprints": []byte(` + mock_defaults { + name: "libexample_defaults", + visibility: ["//other"], + } + mock_library { + name: "libexample", + visibility: ["//top/nested"], + defaults: ["libexample_defaults"], + } + mock_library { + name: "libsamepackage", + deps: ["libexample"], + }`), + "top/nested/Blueprints": []byte(` + mock_library { + name: "libnested", + deps: ["libexample"], + }`), + "other/Blueprints": []byte(` + mock_library { + name: "libother", + deps: ["libexample"], + }`), + "outsider/Blueprints": []byte(` + mock_library { + name: "liboutsider", + deps: ["libexample"], + }`), + }, + expectedErrors: []string{ + `module "liboutsider" variant "android_common": depends on //top:libexample which is not` + + ` visible to this module`, + }, + }, + { + name: "defaults union, multiple defaults", + fs: map[string][]byte{ + "top/Blueprints": []byte(` + mock_defaults { + name: "libexample_defaults_1", + visibility: ["//other"], + } + mock_defaults { + name: "libexample_defaults_2", + visibility: ["//top/nested"], + } + mock_library { + name: "libexample", + defaults: ["libexample_defaults_1", "libexample_defaults_2"], + } + mock_library { + name: "libsamepackage", + deps: ["libexample"], + }`), + "top/nested/Blueprints": []byte(` + mock_library { + name: "libnested", + deps: ["libexample"], + }`), + "other/Blueprints": []byte(` + mock_library { + name: "libother", + deps: ["libexample"], + }`), + "outsider/Blueprints": []byte(` + mock_library { + name: "liboutsider", + deps: ["libexample"], + }`), + }, + expectedErrors: []string{ + `module "liboutsider" variant "android_common": depends on //top:libexample which is not` + + ` visible to this module`, + }, + }, + { + name: "//visibility:public mixed with other in defaults", + fs: map[string][]byte{ + "top/Blueprints": []byte(` + mock_defaults { + name: "libexample_defaults", + visibility: ["//visibility:public", "//namespace"], + } + mock_library { + name: "libexample", + defaults: ["libexample_defaults"], + }`), + }, + expectedErrors: []string{ + `module "libexample_defaults": visibility: cannot mix "//visibility:public"` + + ` with any other visibility rules`, + }, + }, + { + name: "//visibility:public overriding defaults", + fs: map[string][]byte{ + "top/Blueprints": []byte(` + mock_defaults { + name: "libexample_defaults", + visibility: ["//namespace"], + } + mock_library { + name: "libexample", + visibility: ["//visibility:public"], + defaults: ["libexample_defaults"], + }`), + "outsider/Blueprints": []byte(` + mock_library { + name: "liboutsider", + deps: ["libexample"], + }`), + }, + }, + { + name: "//visibility:public mixed with other from different defaults 1", + fs: map[string][]byte{ + "top/Blueprints": []byte(` + mock_defaults { + name: "libexample_defaults_1", + visibility: ["//namespace"], + } + mock_defaults { + name: "libexample_defaults_2", + visibility: ["//visibility:public"], + } + mock_library { + name: "libexample", + defaults: ["libexample_defaults_1", "libexample_defaults_2"], + }`), + "outsider/Blueprints": []byte(` + mock_library { + name: "liboutsider", + deps: ["libexample"], + }`), + }, + }, + { + name: "//visibility:public mixed with other from different defaults 2", + fs: map[string][]byte{ + "top/Blueprints": []byte(` + mock_defaults { + name: "libexample_defaults_1", + visibility: ["//visibility:public"], + } + mock_defaults { + name: "libexample_defaults_2", + visibility: ["//namespace"], + } + mock_library { + name: "libexample", + defaults: ["libexample_defaults_1", "libexample_defaults_2"], + }`), + "outsider/Blueprints": []byte(` + mock_library { + name: "liboutsider", + deps: ["libexample"], + }`), + }, + }, + { + name: "//visibility:private in defaults", + fs: map[string][]byte{ + "top/Blueprints": []byte(` + mock_defaults { + name: "libexample_defaults", + visibility: ["//visibility:private"], + } + mock_library { + name: "libexample", + defaults: ["libexample_defaults"], + } + mock_library { + name: "libsamepackage", + deps: ["libexample"], + }`), + "top/nested/Blueprints": []byte(` + mock_library { + name: "libnested", + deps: ["libexample"], + }`), + "other/Blueprints": []byte(` + mock_library { + name: "libother", + deps: ["libexample"], + }`), + }, + expectedErrors: []string{ + `module "libnested" variant "android_common": depends on //top:libexample which is not` + + ` visible to this module`, + `module "libother" variant "android_common": depends on //top:libexample which is not` + + ` visible to this module`, + }, + }, + { + name: "//visibility:private mixed with other in defaults", + fs: map[string][]byte{ + "top/Blueprints": []byte(` + mock_defaults { + name: "libexample_defaults", + visibility: ["//visibility:private", "//namespace"], + } + mock_library { + name: "libexample", + defaults: ["libexample_defaults"], + }`), + }, + expectedErrors: []string{ + `module "libexample_defaults": visibility: cannot mix "//visibility:private"` + + ` with any other visibility rules`, + }, + }, + { + name: "//visibility:private overriding defaults", + fs: map[string][]byte{ + "top/Blueprints": []byte(` + mock_defaults { + name: "libexample_defaults", + visibility: ["//namespace"], + } + mock_library { + name: "libexample", + visibility: ["//visibility:private"], + defaults: ["libexample_defaults"], + }`), + }, + expectedErrors: []string{ + `module "libexample": visibility: cannot mix "//visibility:private"` + + ` with any other visibility rules`, + }, + }, + { + name: "//visibility:private in defaults overridden", + fs: map[string][]byte{ + "top/Blueprints": []byte(` + mock_defaults { + name: "libexample_defaults", + visibility: ["//visibility:private"], + } + mock_library { + name: "libexample", + visibility: ["//namespace"], + defaults: ["libexample_defaults"], + }`), + }, + expectedErrors: []string{ + `module "libexample": visibility: cannot mix "//visibility:private"` + + ` with any other visibility rules`, + }, + }, + { + name: "//visibility:private mixed with itself", + fs: map[string][]byte{ + "top/Blueprints": []byte(` + mock_defaults { + name: "libexample_defaults_1", + visibility: ["//visibility:private"], + } + mock_defaults { + name: "libexample_defaults_2", + visibility: ["//visibility:private"], + } + mock_library { + name: "libexample", + visibility: ["//visibility:private"], + defaults: ["libexample_defaults_1", "libexample_defaults_2"], + }`), + "outsider/Blueprints": []byte(` + mock_library { + name: "liboutsider", + deps: ["libexample"], + }`), + }, + expectedErrors: []string{ + `module "liboutsider" variant "android_common": depends on //top:libexample which is not` + + ` visible to this module`, + }, + }, } func TestVisibility(t *testing.T) { @@ -445,7 +701,10 @@ func testVisibility(buildDir string, fs map[string][]byte) (*TestContext, []erro ctx := NewTestArchContext() ctx.RegisterModuleType("mock_library", ModuleFactoryAdaptor(newMockLibraryModule)) - ctx.PreDepsMutators(registerVisibilityRuleGatherer) + ctx.RegisterModuleType("mock_defaults", ModuleFactoryAdaptor(defaultsFactory)) + ctx.PreArchMutators(registerVisibilityRuleChecker) + ctx.PreArchMutators(RegisterDefaultsPreArchMutators) + ctx.PreArchMutators(registerVisibilityRuleGatherer) ctx.PostDepsMutators(registerVisibilityRuleEnforcer) ctx.Register() @@ -466,6 +725,7 @@ type mockLibraryProperties struct { type mockLibraryModule struct { ModuleBase + DefaultableModuleBase properties mockLibraryProperties } @@ -473,6 +733,7 @@ func newMockLibraryModule() Module { m := &mockLibraryModule{} m.AddProperties(&m.properties) InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon) + InitDefaultableModule(m) return m } @@ -487,3 +748,17 @@ func (j *mockLibraryModule) DepsMutator(ctx BottomUpMutatorContext) { func (p *mockLibraryModule) GenerateAndroidBuildActions(ModuleContext) { } + +type mockDefaults struct { + ModuleBase + DefaultsModuleBase +} + +func defaultsFactory() Module { + m := &mockDefaults{} + InitDefaultsModule(m) + return m +} + +func (*mockDefaults) GenerateAndroidBuildActions(ctx ModuleContext) { +} diff --git a/dexpreopt/config.go b/dexpreopt/config.go index 1e0f862ed..5a1bd74a5 100644 --- a/dexpreopt/config.go +++ b/dexpreopt/config.go @@ -65,8 +65,6 @@ type GlobalConfig struct { AlwaysOtherDebugInfo bool // always generate mini debug info for non-system server modules (overrides NoDebugInfo=true) NeverOtherDebugInfo bool // never generate mini debug info for non-system server modules (overrides NoDebugInfo=true) - MissingUsesLibraries []string // libraries that may be listed in OptionalUsesLibraries but will not be installed by the product - IsEng bool // build is a eng variant SanitizeLite bool // build is the second phase of a SANITIZE_LITE build @@ -95,14 +93,14 @@ type GlobalConfig struct { // Tools contains paths to tools possibly used by the generated commands. If you add a new tool here you MUST add it // to the order-only dependency list in DEXPREOPT_GEN_DEPS. type Tools struct { - Profman android.Path - Dex2oat android.Path - Aapt android.Path - SoongZip android.Path - Zip2zip android.Path - - VerifyUsesLibraries android.Path - ConstructContext android.Path + Profman android.Path + Dex2oat android.Path + Aapt android.Path + SoongZip android.Path + Zip2zip android.Path + ManifestCheck android.Path + + ConstructContext android.Path } type ModuleConfig struct { @@ -110,6 +108,7 @@ type ModuleConfig struct { DexLocation string // dex location on device BuildPath android.OutputPath DexPath android.Path + ManifestPath android.Path UncompressedDex bool HasApkLibraries bool PreoptFlags []string @@ -117,10 +116,10 @@ type ModuleConfig struct { ProfileClassListing android.OptionalPath ProfileIsTextListing bool - EnforceUsesLibraries bool - OptionalUsesLibraries []string - UsesLibraries []string - LibraryPaths map[string]android.Path + EnforceUsesLibraries bool + PresentOptionalUsesLibraries []string + UsesLibraries []string + LibraryPaths map[string]android.Path Archs []android.ArchType DexPreoptImages []android.Path @@ -187,14 +186,14 @@ func LoadGlobalConfig(ctx android.PathContext, path string) (GlobalConfig, []byt BootImageProfiles []string Tools struct { - Profman string - Dex2oat string - Aapt string - SoongZip string - Zip2zip string - - VerifyUsesLibraries string - ConstructContext string + Profman string + Dex2oat string + Aapt string + SoongZip string + Zip2zip string + ManifestCheck string + + ConstructContext string } } @@ -214,7 +213,7 @@ func LoadGlobalConfig(ctx android.PathContext, path string) (GlobalConfig, []byt config.GlobalConfig.Tools.Aapt = constructPath(ctx, config.Tools.Aapt) config.GlobalConfig.Tools.SoongZip = constructPath(ctx, config.Tools.SoongZip) config.GlobalConfig.Tools.Zip2zip = constructPath(ctx, config.Tools.Zip2zip) - config.GlobalConfig.Tools.VerifyUsesLibraries = constructPath(ctx, config.Tools.VerifyUsesLibraries) + config.GlobalConfig.Tools.ManifestCheck = constructPath(ctx, config.Tools.ManifestCheck) config.GlobalConfig.Tools.ConstructContext = constructPath(ctx, config.Tools.ConstructContext) return config.GlobalConfig, data, nil @@ -231,6 +230,7 @@ func LoadModuleConfig(ctx android.PathContext, path string) (ModuleConfig, error // used to construct the real value manually below. BuildPath string DexPath string + ManifestPath string ProfileClassListing string LibraryPaths map[string]string DexPreoptImages []string @@ -249,6 +249,7 @@ func LoadModuleConfig(ctx android.PathContext, path string) (ModuleConfig, error // Construct paths that require a PathContext. config.ModuleConfig.BuildPath = constructPath(ctx, config.BuildPath).(android.OutputPath) config.ModuleConfig.DexPath = constructPath(ctx, config.DexPath) + config.ModuleConfig.ManifestPath = constructPath(ctx, config.ManifestPath) config.ModuleConfig.ProfileClassListing = android.OptionalPathForPath(constructPath(ctx, config.ProfileClassListing)) config.ModuleConfig.LibraryPaths = constructPathMap(ctx, config.LibraryPaths) config.ModuleConfig.DexPreoptImages = constructPaths(ctx, config.DexPreoptImages) @@ -307,7 +308,6 @@ func GlobalConfigForTests(ctx android.PathContext) GlobalConfig { NeverSystemServerDebugInfo: false, AlwaysOtherDebugInfo: false, NeverOtherDebugInfo: false, - MissingUsesLibraries: nil, IsEng: false, SanitizeLite: false, DefaultAppImages: false, @@ -324,13 +324,13 @@ func GlobalConfigForTests(ctx android.PathContext) GlobalConfig { Dex2oatImageXmx: "", Dex2oatImageXms: "", Tools: Tools{ - Profman: android.PathForTesting("profman"), - Dex2oat: android.PathForTesting("dex2oat"), - Aapt: android.PathForTesting("aapt"), - SoongZip: android.PathForTesting("soong_zip"), - Zip2zip: android.PathForTesting("zip2zip"), - VerifyUsesLibraries: android.PathForTesting("verify_uses_libraries.sh"), - ConstructContext: android.PathForTesting("construct_context.sh"), + Profman: android.PathForTesting("profman"), + Dex2oat: android.PathForTesting("dex2oat"), + Aapt: android.PathForTesting("aapt"), + SoongZip: android.PathForTesting("soong_zip"), + Zip2zip: android.PathForTesting("zip2zip"), + ManifestCheck: android.PathForTesting("manifest_check"), + ConstructContext: android.PathForTesting("construct_context.sh"), }, } } diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go index 5b658d989..0be37d0ff 100644 --- a/dexpreopt/dexpreopt.go +++ b/dexpreopt/dexpreopt.go @@ -226,15 +226,6 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul bootImageLocation = PathToLocation(bootImage, arch) } - // Lists of used and optional libraries from the build config to be verified against the manifest in the APK - var verifyUsesLibs []string - var verifyOptionalUsesLibs []string - - // Lists of used and optional libraries from the build config, with optional libraries that are known to not - // be present in the current product removed. - var filteredUsesLibs []string - var filteredOptionalUsesLibs []string - // The class loader context using paths in the build var classLoaderContextHost android.Paths @@ -252,14 +243,10 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul var classLoaderContextHostString string if module.EnforceUsesLibraries { - verifyUsesLibs = copyOf(module.UsesLibraries) - verifyOptionalUsesLibs = copyOf(module.OptionalUsesLibraries) - - filteredOptionalUsesLibs = filterOut(global.MissingUsesLibraries, module.OptionalUsesLibraries) - filteredUsesLibs = append(copyOf(module.UsesLibraries), filteredOptionalUsesLibs...) + usesLibs := append(copyOf(module.UsesLibraries), module.PresentOptionalUsesLibraries...) // Create class loader context for dex2oat from uses libraries and filtered optional libraries - for _, l := range filteredUsesLibs { + for _, l := range usesLibs { classLoaderContextHost = append(classLoaderContextHost, pathForLibrary(module, l)) @@ -270,11 +257,13 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul const httpLegacy = "org.apache.http.legacy" const httpLegacyImpl = "org.apache.http.legacy.impl" - // Fix up org.apache.http.legacy.impl since it should be org.apache.http.legacy in the manifest. - replace(verifyUsesLibs, httpLegacyImpl, httpLegacy) - replace(verifyOptionalUsesLibs, httpLegacyImpl, httpLegacy) + // org.apache.http.legacy contains classes that were in the default classpath until API 28. If the + // targetSdkVersion in the manifest or APK is < 28, and the module does not explicitly depend on + // org.apache.http.legacy, then implicitly add the classes to the classpath for dexpreopt. One the + // device the classes will be in a file called org.apache.http.legacy.impl.jar. + module.LibraryPaths[httpLegacyImpl] = module.LibraryPaths[httpLegacy] - if !contains(verifyUsesLibs, httpLegacy) && !contains(verifyOptionalUsesLibs, httpLegacy) { + if !contains(module.UsesLibraries, httpLegacy) && !contains(module.PresentOptionalUsesLibraries, httpLegacy) { conditionalClassLoaderContextHost28 = append(conditionalClassLoaderContextHost28, pathForLibrary(module, httpLegacyImpl)) conditionalClassLoaderContextTarget28 = append(conditionalClassLoaderContextTarget28, @@ -284,6 +273,9 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul const hidlBase = "android.hidl.base-V1.0-java" const hidlManager = "android.hidl.manager-V1.0-java" + // android.hidl.base-V1.0-java and android.hidl.manager-V1.0 contain classes that were in the default + // classpath until API 29. If the targetSdkVersion in the manifest or APK is < 29 then implicitly add + // the classes to the classpath for dexpreopt. conditionalClassLoaderContextHost29 = append(conditionalClassLoaderContextHost29, pathForLibrary(module, hidlManager)) conditionalClassLoaderContextTarget29 = append(conditionalClassLoaderContextTarget29, @@ -309,9 +301,21 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul rule.Command().Text(`stored_class_loader_context_arg=""`) if module.EnforceUsesLibraries { - rule.Command().Textf(`uses_library_names="%s"`, strings.Join(verifyUsesLibs, " ")) - rule.Command().Textf(`optional_uses_library_names="%s"`, strings.Join(verifyOptionalUsesLibs, " ")) - rule.Command().Textf(`aapt_binary="%s"`, global.Tools.Aapt) + if module.ManifestPath != nil { + rule.Command().Text(`target_sdk_version="$(`). + Tool(global.Tools.ManifestCheck). + Flag("--extract-target-sdk-version"). + Input(module.ManifestPath). + Text(`)"`) + } else { + // No manifest to extract targetSdkVersion from, hope that DexJar is an APK + rule.Command().Text(`target_sdk_version="$(`). + Tool(global.Tools.Aapt). + Flag("dump badging"). + Input(module.DexPath). + Text(`| grep "targetSdkVersion" | sed -n "s/targetSdkVersion:'\(.*\)'/\1/p"`). + Text(`)"`) + } rule.Command().Textf(`dex_preopt_host_libraries="%s"`, strings.Join(classLoaderContextHost.Strings(), " ")). Implicits(classLoaderContextHost) @@ -327,8 +331,7 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul Implicits(conditionalClassLoaderContextHost29) rule.Command().Textf(`conditional_target_libs_29="%s"`, strings.Join(conditionalClassLoaderContextTarget29, " ")) - rule.Command().Text("source").Tool(global.Tools.VerifyUsesLibraries).Input(module.DexPath) - rule.Command().Text("source").Tool(global.Tools.ConstructContext) + rule.Command().Text("source").Tool(global.Tools.ConstructContext).Input(module.DexPath) } // Devices that do not have a product partition use a symlink from /product to /system/product. diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go index 6dfa9d266..0402f8754 100644 --- a/dexpreopt/dexpreopt_test.go +++ b/dexpreopt/dexpreopt_test.go @@ -33,7 +33,7 @@ func testModuleConfig(ctx android.PathContext) ModuleConfig { ProfileClassListing: android.OptionalPath{}, ProfileIsTextListing: false, EnforceUsesLibraries: false, - OptionalUsesLibraries: nil, + PresentOptionalUsesLibraries: nil, UsesLibraries: nil, LibraryPaths: nil, Archs: []android.ArchType{android.Arm}, diff --git a/java/app.go b/java/app.go index f7f08a88d..2d817fe58 100644 --- a/java/app.go +++ b/java/app.go @@ -17,12 +17,13 @@ package java // This file contains the module types for compiling Android apps. import ( - "github.com/google/blueprint" - "github.com/google/blueprint/proptools" "path/filepath" "reflect" "strings" + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" + "android/soong/android" "android/soong/cc" "android/soong/tradefed" @@ -119,6 +120,8 @@ type AndroidApp struct { aapt android.OverridableModuleBase + usesLibrary usesLibrary + certificate Certificate appProperties appProperties @@ -176,6 +179,8 @@ func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) { } } } + + a.usesLibrary.deps(ctx, Bool(a.properties.No_framework_libs)) } func (a *AndroidApp) OverridablePropertiesDepsMutator(ctx android.BottomUpMutatorContext) { @@ -276,6 +281,7 @@ func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) { aaptLinkFlags = append(aaptLinkFlags, a.additionalAaptFlags...) a.aapt.splitNames = a.appProperties.Package_splits + a.aapt.sdkLibraries = a.exportedSdkLibs a.aapt.buildActions(ctx, sdkContext(a), aaptLinkFlags...) @@ -308,9 +314,17 @@ func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path { } else { installDir = filepath.Join("app", a.installApkName) } + a.dexpreopter.installPath = android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk") a.dexpreopter.isInstallable = Bool(a.properties.Installable) a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx) + + a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries() + a.dexpreopter.usesLibs = a.usesLibrary.usesLibraryProperties.Uses_libs + a.dexpreopter.optionalUsesLibs = a.usesLibrary.presentOptionalUsesLibs(ctx) + a.dexpreopter.libraryPaths = a.usesLibrary.usesLibraryPaths(ctx) + a.dexpreopter.manifestFile = a.mergedManifestFile + a.deviceProperties.UncompressDex = a.dexpreopter.uncompressedDex if ctx.ModuleName() != "framework-res" { @@ -368,12 +382,19 @@ func processMainCert(m android.ModuleBase, certPropValue string, certificates [] } func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { + var apkDeps android.Paths + // Check if the install APK name needs to be overridden. a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Name()) // Process all building blocks, from AAPT to certificates. a.aaptBuildActions(ctx) + if a.usesLibrary.enforceUsesLibraries() { + manifestCheckFile := a.usesLibrary.verifyUsesLibrariesManifest(ctx, a.mergedManifestFile) + apkDeps = append(apkDeps, manifestCheckFile) + } + a.proguardBuildActions(ctx) dexJarFile := a.dexBuildActions(ctx) @@ -391,13 +412,13 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { // Build a final signed app package. // TODO(jungjw): Consider changing this to installApkName. packageFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".apk") - CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates) + CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates, apkDeps) a.outputFile = packageFile for _, split := range a.aapt.splits { // Sign the split APKs packageFile := android.PathForModuleOut(ctx, ctx.ModuleName()+"_"+split.suffix+".apk") - CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates) + CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates, apkDeps) a.extraOutputFiles = append(a.extraOutputFiles, packageFile) } @@ -483,7 +504,8 @@ func AndroidAppFactory() android.Module { &module.Module.protoProperties, &module.aaptProperties, &module.appProperties, - &module.overridableAppProperties) + &module.overridableAppProperties, + &module.usesLibrary.usesLibraryProperties) module.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase, class android.OsClass) bool { return class == android.Device && ctx.Config().DevicePrefer32BitApps() @@ -559,6 +581,7 @@ func AndroidTestFactory() android.Module { &module.appProperties, &module.appTestProperties, &module.overridableAppProperties, + &module.usesLibrary.usesLibraryProperties, &module.testProperties) android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) @@ -599,7 +622,8 @@ func AndroidTestHelperAppFactory() android.Module { &module.aaptProperties, &module.appProperties, &module.appTestHelperAppProperties, - &module.overridableAppProperties) + &module.overridableAppProperties, + &module.usesLibrary.usesLibraryProperties) android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) android.InitDefaultableModule(module) @@ -666,6 +690,8 @@ type AndroidAppImport struct { certificate *Certificate dexpreopter + + usesLibrary usesLibrary } type AndroidAppImportProperties struct { @@ -753,6 +779,8 @@ func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) { if cert != "" { ctx.AddDependency(ctx.Module(), certificateTag, cert) } + + a.usesLibrary.deps(ctx, false) } func (a *AndroidAppImport) uncompressEmbeddedJniLibs( @@ -808,7 +836,12 @@ func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext // TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK // TODO: LOCAL_PACKAGE_SPLITS - srcApk := android.PathForModuleSrc(ctx, a.getSrcApkPath(ctx)) + var srcApk android.Path + srcApk = android.PathForModuleSrc(ctx, a.getSrcApkPath(ctx)) + + if a.usesLibrary.enforceUsesLibraries() { + srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk) + } // TODO: Install or embed JNI libraries @@ -821,6 +854,12 @@ func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext a.dexpreopter.isInstallable = true a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned) a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx) + + a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries() + a.dexpreopter.usesLibs = a.usesLibrary.usesLibraryProperties.Uses_libs + a.dexpreopter.optionalUsesLibs = a.usesLibrary.presentOptionalUsesLibs(ctx) + a.dexpreopter.libraryPaths = a.usesLibrary.usesLibraryPaths(ctx) + dexOutput := a.dexpreopter.dexpreopt(ctx, jnisUncompressed) if a.dexpreopter.uncompressedDex { dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk") @@ -866,9 +905,129 @@ func AndroidAppImportFactory() android.Module { module.properties.Dpi_variants = reflect.New(dpiVariantsStruct).Interface() module.AddProperties(&module.properties) module.AddProperties(&module.dexpreoptProperties) + module.AddProperties(&module.usesLibrary.usesLibraryProperties) InitJavaModule(module, android.DeviceSupported) android.InitSingleSourcePrebuiltModule(module, &module.properties.Apk) return module } + +type UsesLibraryProperties struct { + // A list of shared library modules that will be listed in uses-library tags in the AndroidManifest.xml file. + Uses_libs []string + + // A list of shared library modules that will be listed in uses-library tags in the AndroidManifest.xml file with + // required=false. + Optional_uses_libs []string + + // If true, the list of uses_libs and optional_uses_libs modules must match the AndroidManifest.xml file. Defaults + // to true if either uses_libs or optional_uses_libs is set. Will unconditionally default to true in the future. + Enforce_uses_libs *bool +} + +// usesLibrary provides properties and helper functions for AndroidApp and AndroidAppImport to verify that the +// <uses-library> tags that end up in the manifest of an APK match the ones known to the build system through the +// uses_libs and optional_uses_libs properties. The build system's values are used by dexpreopt to preopt apps +// with knowledge of their shared libraries. +type usesLibrary struct { + usesLibraryProperties UsesLibraryProperties +} + +func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, noFrameworkLibs bool) { + ctx.AddVariationDependencies(nil, usesLibTag, u.usesLibraryProperties.Uses_libs...) + ctx.AddVariationDependencies(nil, usesLibTag, u.presentOptionalUsesLibs(ctx)...) + if !noFrameworkLibs { + // dexpreopt/dexpreopt.go needs the paths to the dex jars of these libraries in case construct_context.sh needs + // to pass them to dex2oat. Add them as a dependency so we can determine the path to the dex jar of each + // library to dexpreopt. + ctx.AddVariationDependencies(nil, usesLibTag, + "org.apache.http.legacy", + "android.hidl.base-V1.0-java", + "android.hidl.manager-V1.0-java") + } +} + +// presentOptionalUsesLibs returns optional_uses_libs after filtering out MissingUsesLibraries, which don't exist in the +// build. +func (u *usesLibrary) presentOptionalUsesLibs(ctx android.BaseModuleContext) []string { + optionalUsesLibs, _ := android.FilterList(u.usesLibraryProperties.Optional_uses_libs, ctx.Config().MissingUsesLibraries()) + return optionalUsesLibs +} + +// usesLibraryPaths returns a map of module names of shared library dependencies to the paths to their dex jars. +func (u *usesLibrary) usesLibraryPaths(ctx android.ModuleContext) map[string]android.Path { + usesLibPaths := make(map[string]android.Path) + + if !ctx.Config().UnbundledBuild() { + ctx.VisitDirectDepsWithTag(usesLibTag, func(m android.Module) { + if lib, ok := m.(Dependency); ok { + if dexJar := lib.DexJar(); dexJar != nil { + usesLibPaths[ctx.OtherModuleName(m)] = dexJar + } else { + ctx.ModuleErrorf("module %q in uses_libs or optional_uses_libs must produce a dex jar, does it have installable: true?", + ctx.OtherModuleName(m)) + } + } else if ctx.Config().AllowMissingDependencies() { + ctx.AddMissingDependencies([]string{ctx.OtherModuleName(m)}) + } else { + ctx.ModuleErrorf("module %q in uses_libs or optional_uses_libs must be a java library", + ctx.OtherModuleName(m)) + } + }) + } + + return usesLibPaths +} + +// enforceUsesLibraries returns true of <uses-library> tags should be checked against uses_libs and optional_uses_libs +// properties. Defaults to true if either of uses_libs or optional_uses_libs is specified. Will default to true +// unconditionally in the future. +func (u *usesLibrary) enforceUsesLibraries() bool { + defaultEnforceUsesLibs := len(u.usesLibraryProperties.Uses_libs) > 0 || + len(u.usesLibraryProperties.Optional_uses_libs) > 0 + return BoolDefault(u.usesLibraryProperties.Enforce_uses_libs, defaultEnforceUsesLibs) +} + +// verifyUsesLibrariesManifest checks the <uses-library> tags in an AndroidManifest.xml against the ones specified +// in the uses_libs and optional_uses_libs properties. It returns the path to a copy of the manifest. +func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path) android.Path { + outputFile := android.PathForModuleOut(ctx, "manifest_check", "AndroidManifest.xml") + + rule := android.NewRuleBuilder() + cmd := rule.Command().Tool(ctx.Config().HostToolPath(ctx, "manifest_check")). + Flag("--enforce-uses-libraries"). + Input(manifest). + FlagWithOutput("-o ", outputFile) + + for _, lib := range u.usesLibraryProperties.Uses_libs { + cmd.FlagWithArg("--uses-library ", lib) + } + + for _, lib := range u.usesLibraryProperties.Optional_uses_libs { + cmd.FlagWithArg("--optional-uses-library ", lib) + } + + rule.Build(pctx, ctx, "verify_uses_libraries", "verify <uses-library>") + + return outputFile +} + +// verifyUsesLibrariesAPK checks the <uses-library> tags in the manifest of an APK against the ones specified +// in the uses_libs and optional_uses_libs properties. It returns the path to a copy of the APK. +func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk android.Path) android.Path { + outputFile := android.PathForModuleOut(ctx, "verify_uses_libraries", apk.Base()) + + rule := android.NewRuleBuilder() + aapt := ctx.Config().HostToolPath(ctx, "aapt") + rule.Command(). + Textf("aapt_binary=%s", aapt.String()).Implicit(aapt). + Textf(`uses_library_names="%s"`, strings.Join(u.usesLibraryProperties.Uses_libs, " ")). + Textf(`optional_uses_library_names="%s"`, strings.Join(u.usesLibraryProperties.Optional_uses_libs, " ")). + Tool(android.PathForSource(ctx, "build/make/core/verify_uses_libraries.sh")).Input(apk) + rule.Command().Text("cp -f").Input(apk).Output(outputFile) + + rule.Build(pctx, ctx, "verify_uses_libraries", "verify <uses-library>") + + return outputFile +} diff --git a/java/app_builder.go b/java/app_builder.go index 82a390f64..348c8b4bb 100644 --- a/java/app_builder.go +++ b/java/app_builder.go @@ -31,7 +31,7 @@ import ( var ( Signapk = pctx.AndroidStaticRule("signapk", blueprint.RuleParams{ - Command: `${config.JavaCmd} -Djava.library.path=$$(dirname $signapkJniLibrary) ` + + Command: `${config.JavaCmd} ${config.JavaVmFlags} -Djava.library.path=$$(dirname $signapkJniLibrary) ` + `-jar $signapkCmd $flags $certificates $in $out`, CommandDeps: []string{"$signapkCmd", "$signapkJniLibrary"}, }, @@ -63,7 +63,7 @@ var combineApk = pctx.AndroidStaticRule("combineApk", }) func CreateAndSignAppPackage(ctx android.ModuleContext, outputFile android.WritablePath, - packageFile, jniJarFile, dexJarFile android.Path, certificates []Certificate) { + packageFile, jniJarFile, dexJarFile android.Path, certificates []Certificate, deps android.Paths) { unsignedApkName := strings.TrimSuffix(outputFile.Base(), ".apk") + "-unsigned.apk" unsignedApk := android.PathForModuleOut(ctx, unsignedApkName) @@ -78,9 +78,10 @@ func CreateAndSignAppPackage(ctx android.ModuleContext, outputFile android.Writa } ctx.Build(pctx, android.BuildParams{ - Rule: combineApk, - Inputs: inputs, - Output: unsignedApk, + Rule: combineApk, + Inputs: inputs, + Output: unsignedApk, + Implicits: deps, }) SignAppPackage(ctx, outputFile, unsignedApk, certificates) diff --git a/java/app_test.go b/java/app_test.go index 40a64af8c..559afcc7d 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -1239,3 +1239,83 @@ func TestStl(t *testing.T) { }) } } + +func TestUsesLibraries(t *testing.T) { + bp := ` + java_sdk_library { + name: "foo", + srcs: ["a.java"], + api_packages: ["foo"], + } + + java_sdk_library { + name: "bar", + srcs: ["a.java"], + api_packages: ["bar"], + } + + android_app { + name: "app", + srcs: ["a.java"], + uses_libs: ["foo"], + optional_uses_libs: [ + "bar", + "baz", + ], + } + + android_app_import { + name: "prebuilt", + apk: "prebuilts/apk/app.apk", + certificate: "platform", + uses_libs: ["foo"], + optional_uses_libs: [ + "bar", + "baz", + ], + } + ` + + config := testConfig(nil) + config.TestProductVariables.MissingUsesLibraries = []string{"baz"} + + ctx := testAppContext(config, bp, nil) + + run(t, ctx, config) + + app := ctx.ModuleForTests("app", "android_common") + prebuilt := ctx.ModuleForTests("prebuilt", "android_common") + + // Test that all libraries are verified + cmd := app.Rule("verify_uses_libraries").RuleParams.Command + if w := "--uses-library foo"; !strings.Contains(cmd, w) { + t.Errorf("wanted %q in %q", w, cmd) + } + + if w := "--optional-uses-library bar --optional-uses-library baz"; !strings.Contains(cmd, w) { + t.Errorf("wanted %q in %q", w, cmd) + } + + cmd = prebuilt.Rule("verify_uses_libraries").RuleParams.Command + + if w := `uses_library_names="foo"`; !strings.Contains(cmd, w) { + t.Errorf("wanted %q in %q", w, cmd) + } + + if w := `optional_uses_library_names="bar baz"`; !strings.Contains(cmd, w) { + t.Errorf("wanted %q in %q", w, cmd) + } + + // Test that only present libraries are preopted + cmd = app.Rule("dexpreopt").RuleParams.Command + + if w := `dex_preopt_target_libraries="/system/framework/foo.jar /system/framework/bar.jar"`; !strings.Contains(cmd, w) { + t.Errorf("wanted %q in %q", w, cmd) + } + + cmd = prebuilt.Rule("dexpreopt").RuleParams.Command + + if w := `dex_preopt_target_libraries="/system/framework/foo.jar /system/framework/bar.jar"`; !strings.Contains(cmd, w) { + t.Errorf("wanted %q in %q", w, cmd) + } +} diff --git a/java/builder.go b/java/builder.go index d257d1da1..e1a912b2f 100644 --- a/java/builder.go +++ b/java/builder.go @@ -43,7 +43,8 @@ var ( Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` + `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` + `(if [ -s $srcJarDir/list ] || [ -s $out.rsp ] ; then ` + - `${config.SoongJavacWrapper} ${config.JavacWrapper}${config.JavacCmd} ${config.JavacHeapFlags} ${config.CommonJdkFlags} ` + + `${config.SoongJavacWrapper} ${config.JavacWrapper}${config.JavacCmd} ` + + `${config.JavacHeapFlags} ${config.JavacVmFlags} ${config.CommonJdkFlags} ` + `$processorpath $processor $javacFlags $bootClasspath $classpath ` + `-source $javaVersion -target $javaVersion ` + `-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list ; fi ) && ` + @@ -64,7 +65,7 @@ var ( turbine = pctx.AndroidStaticRule("turbine", blueprint.RuleParams{ Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` + - `${config.JavaCmd} -jar ${config.TurbineJar} --output $out.tmp ` + + `${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.TurbineJar} --output $out.tmp ` + `--temp_dir "$outDir" --sources @$out.rsp --source_jars $srcJars ` + `--javacopts ${config.CommonJdkFlags} ` + `$javacFlags -source $javaVersion -target $javaVersion -- $bootClasspath $classpath && ` + @@ -108,7 +109,7 @@ var ( jarjar = pctx.AndroidStaticRule("jarjar", blueprint.RuleParams{ - Command: "${config.JavaCmd} -jar ${config.JarjarCmd} process $rulesFile $in $out", + Command: "${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.JarjarCmd} process $rulesFile $in $out", CommandDeps: []string{"${config.JavaCmd}", "${config.JarjarCmd}", "$rulesFile"}, }, "rulesFile") @@ -124,7 +125,7 @@ var ( jetifier = pctx.AndroidStaticRule("jetifier", blueprint.RuleParams{ - Command: "${config.JavaCmd} -jar ${config.JetifierJar} -l error -o $out -i $in", + Command: "${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.JetifierJar} -l error -o $out -i $in", CommandDeps: []string{"${config.JavaCmd}", "${config.JetifierJar}"}, }, ) diff --git a/java/config/config.go b/java/config/config.go index f9552d5a1..529f1e61c 100644 --- a/java/config/config.go +++ b/java/config/config.go @@ -69,6 +69,8 @@ func init() { // b/65004097: prevent using java.lang.invoke.StringConcatFactory when using -target 1.9 `-XDstringConcat=inline`, }, " ")) + pctx.StaticVariable("JavaVmFlags", "-XX:OnError=\"cat hs_err_pid%p.log\"") + pctx.StaticVariable("JavacVmFlags", "-J-XX:OnError=\"cat hs_err_pid%p.log\"") pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS) diff --git a/java/config/makevars.go b/java/config/makevars.go index 9c7851174..ead298acd 100644 --- a/java/config/makevars.go +++ b/java/config/makevars.go @@ -39,8 +39,8 @@ func makeVarsProvider(ctx android.MakeVarsContext) { ctx.Strict("ANDROID_JAVA8_HOME", "prebuilts/jdk/jdk8/${hostPrebuiltTag}") ctx.Strict("ANDROID_JAVA9_HOME", "prebuilts/jdk/jdk9/${hostPrebuiltTag}") ctx.Strict("ANDROID_JAVA_TOOLCHAIN", "${JavaToolchain}") - ctx.Strict("JAVA", "${JavaCmd}") - ctx.Strict("JAVAC", "${JavacCmd}") + ctx.Strict("JAVA", "${JavaCmd} ${JavaVmFlags}") + ctx.Strict("JAVAC", "${JavacCmd} ${JavacVmFlags}") ctx.Strict("JAR", "${JarCmd}") ctx.Strict("JAR_ARGS", "${JarArgsCmd}") ctx.Strict("JAVADOC", "${JavadocCmd}") @@ -58,8 +58,8 @@ func makeVarsProvider(ctx android.MakeVarsContext) { ctx.Strict("ERROR_PRONE_CHECKS", "${ErrorProneChecks}") } - ctx.Strict("TARGET_JAVAC", "${JavacCmd} ${CommonJdkFlags}") - ctx.Strict("HOST_JAVAC", "${JavacCmd} ${CommonJdkFlags}") + ctx.Strict("TARGET_JAVAC", "${JavacCmd} ${JavacVmFlags} ${CommonJdkFlags}") + ctx.Strict("HOST_JAVAC", "${JavacCmd} ${JavacVmFlags} ${CommonJdkFlags}") ctx.Strict("JLINK", "${JlinkCmd}") ctx.Strict("JMOD", "${JmodCmd}") diff --git a/java/dexpreopt.go b/java/dexpreopt.go index 08fd06ed6..23d2aa6e3 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -29,6 +29,12 @@ type dexpreopter struct { isInstallable bool isPresignedPrebuilt bool + manifestFile android.Path + usesLibs []string + optionalUsesLibs []string + enforceUsesLibs bool + libraryPaths map[string]android.Path + builtInstalled string } @@ -154,6 +160,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo DexLocation: dexLocation, BuildPath: android.PathForModuleOut(ctx, "dexpreopt", ctx.ModuleName()+".jar").OutputPath, DexPath: dexJarFile, + ManifestPath: d.manifestFile, UncompressedDex: d.uncompressedDex, HasApkLibraries: false, PreoptFlags: nil, @@ -161,10 +168,10 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo ProfileClassListing: profileClassListing, ProfileIsTextListing: profileIsTextListing, - EnforceUsesLibraries: false, - OptionalUsesLibraries: nil, - UsesLibraries: nil, - LibraryPaths: nil, + EnforceUsesLibraries: d.enforceUsesLibs, + PresentOptionalUsesLibraries: d.optionalUsesLibs, + UsesLibraries: d.usesLibs, + LibraryPaths: d.libraryPaths, Archs: archs, DexPreoptImages: images, diff --git a/java/droiddoc.go b/java/droiddoc.go index fd7e2a48a..b41825ea4 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -73,7 +73,7 @@ var ( Command: `rm -rf "$outDir" "$srcJarDir" "$stubsDir" && ` + `mkdir -p "$outDir" "$srcJarDir" "$stubsDir" && ` + `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` + - `${config.JavaCmd} -jar ${config.MetalavaJar} -encoding UTF-8 -source $javaVersion @$out.rsp @$srcJarDir/list ` + + `${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.MetalavaJar} -encoding UTF-8 -source $javaVersion @$out.rsp @$srcJarDir/list ` + `$bootclasspathArgs $classpathArgs $sourcepathArgs --no-banner --color --quiet --format=v2 ` + `$opts && ` + `${config.SoongZipCmd} -write_if_changed -jar -o $out -C $stubsDir -D $stubsDir && ` + @@ -95,7 +95,7 @@ var ( blueprint.RuleParams{ Command: `( rm -rf "$srcJarDir" && mkdir -p "$srcJarDir" && ` + `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` + - `${config.JavaCmd} -jar ${config.MetalavaJar} -encoding UTF-8 -source $javaVersion @$out.rsp @$srcJarDir/list ` + + `${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.MetalavaJar} -encoding UTF-8 -source $javaVersion @$out.rsp @$srcJarDir/list ` + `$bootclasspathArgs $classpathArgs $sourcepathArgs --no-banner --color --quiet --format=v2 ` + `$opts && touch $out && rm -rf "$srcJarDir") || ` + `( echo -e "$msg" ; exit 38 )`, @@ -120,7 +120,7 @@ var ( Command: `rm -rf "$outDir" "$srcJarDir" "$stubsDir" && ` + `mkdir -p "$outDir" "$srcJarDir" "$stubsDir" && ` + `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` + - `${config.JavaCmd} -jar ${config.DokkaJar} $srcJarDir ` + + `${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.DokkaJar} $srcJarDir ` + `$classpathArgs -format dac -dacRoot /reference/kotlin -output $outDir $opts && ` + `${config.SoongZipCmd} -write_if_changed -d -o $docZip -C $outDir -D $outDir && ` + `${config.SoongZipCmd} -write_if_changed -jar -o $out -C $stubsDir -D $stubsDir && ` + @@ -211,6 +211,7 @@ type JavadocProperties struct { // Available variables for substitution: // // $(location <label>): the path to the arg_files with name <label> + // $$: a literal $ Args *string // names of the output files used in args that will be generated diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go index 9627dc64b..b1ddab48d 100644 --- a/java/hiddenapi_singleton.go +++ b/java/hiddenapi_singleton.go @@ -61,7 +61,7 @@ func (h *hiddenAPISingleton) GenerateBuildActions(ctx android.SingletonContext) stubFlagsRule(ctx) // These rules depend on files located in frameworks/base, skip them if running in a tree that doesn't have them. - if ctx.Config().FrameworksBaseDirExists(ctx) { + if ctx.Config().FrameworksBaseDirExists(ctx) && !ctx.Config().UnbundledBuild() { h.flags = flagsRule(ctx) h.metadata = metadataRule(ctx) } else { diff --git a/java/jacoco.go b/java/jacoco.go index 8b6d4ac87..bce9822f4 100644 --- a/java/jacoco.go +++ b/java/jacoco.go @@ -31,7 +31,7 @@ var ( jacoco = pctx.AndroidStaticRule("jacoco", blueprint.RuleParams{ Command: `rm -rf $tmpDir && mkdir -p $tmpDir && ` + `${config.Zip2ZipCmd} -i $in -o $strippedJar $stripSpec && ` + - `${config.JavaCmd} -jar ${config.JacocoCLIJar} ` + + `${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.JacocoCLIJar} ` + ` instrument --quiet --dest $tmpDir $strippedJar && ` + `${config.Ziptime} $tmpJar && ` + `${config.MergeZipsCmd} --ignore-duplicates -j $out $tmpJar $in`, diff --git a/java/java.go b/java/java.go index 44830839d..84518a12e 100644 --- a/java/java.go +++ b/java/java.go @@ -420,6 +420,7 @@ var ( proguardRaiseTag = dependencyTag{name: "proguard-raise"} certificateTag = dependencyTag{name: "certificate"} instrumentationForTag = dependencyTag{name: "instrumentation_for"} + usesLibTag = dependencyTag{name: "uses-library"} ) type sdkDep struct { @@ -1301,9 +1302,11 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { return } - // Hidden API CSV generation and dex encoding - dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, dexOutputFile, j.implementationJarFile, - j.deviceProperties.UncompressDex) + if !ctx.Config().UnbundledBuild() { + // Hidden API CSV generation and dex encoding + dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, dexOutputFile, j.implementationJarFile, + j.deviceProperties.UncompressDex) + } // merge dex jar with resources if necessary if j.resourceJar != nil { diff --git a/java/java_test.go b/java/java_test.go index 50b1c34ce..3a7ed4e3f 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -174,6 +174,8 @@ func testContext(config android.Config, bp string, "build/soong/scripts/jar-wrapper.sh": nil, + "build/make/core/verify_uses_libraries.sh": nil, + "build/make/core/proguard.flags": nil, "build/make/core/proguard_basic_keeps.flags": nil, diff --git a/java/system_modules.go b/java/system_modules.go index 9ee0307ca..8005360a2 100644 --- a/java/system_modules.go +++ b/java/system_modules.go @@ -87,11 +87,13 @@ func SystemModulesFactory() android.Module { module := &SystemModules{} module.AddProperties(&module.properties) android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon) + android.InitDefaultableModule(module) return module } type SystemModules struct { android.ModuleBase + android.DefaultableModuleBase properties SystemModulesProperties diff --git a/java/testing.go b/java/testing.go index 22831c922..fc7842dd2 100644 --- a/java/testing.go +++ b/java/testing.go @@ -77,6 +77,33 @@ func GatherRequiredDepsForTest() string { name: "framework-res", no_framework_libs: true, } + + java_library { + name: "android.hidl.base-V1.0-java", + srcs: ["a.java"], + no_standard_libs: true, + sdk_version: "core_current", + system_modules: "core-platform-api-stubs-system-modules", + installable: true, + } + + java_library { + name: "android.hidl.manager-V1.0-java", + srcs: ["a.java"], + no_standard_libs: true, + sdk_version: "core_current", + system_modules: "core-platform-api-stubs-system-modules", + installable: true, + } + + java_library { + name: "org.apache.http.legacy", + srcs: ["a.java"], + no_standard_libs: true, + sdk_version: "core_current", + system_modules: "core-platform-api-stubs-system-modules", + installable: true, + } ` systemModules := []string{ diff --git a/scripts/build_broken_logs.go b/scripts/build_broken_logs.go index bdd4b2a75..8021e55d0 100644 --- a/scripts/build_broken_logs.go +++ b/scripts/build_broken_logs.go @@ -65,11 +65,6 @@ var buildBrokenSettings = []struct { warnings: []string{"overriding commands for target"}, }, { - name: "BUILD_BROKEN_ANDROIDMK_EXPORTS", - behavior: DefaultDeprecated, - warnings: []string{"export_keyword"}, - }, - { name: "BUILD_BROKEN_USES_NETWORK", behavior: DefaultDeprecated, }, diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go index 9fd6f6784..4335667d1 100644 --- a/ui/build/dumpvars.go +++ b/ui/build/dumpvars.go @@ -205,7 +205,6 @@ func runMakeProductConfig(ctx Context, config Config) { // Not used, but useful to be in the soong.log "BOARD_VNDK_VERSION", - "BUILD_BROKEN_ANDROIDMK_EXPORTS", "DEFAULT_WARNING_BUILD_MODULE_TYPES", "DEFAULT_ERROR_BUILD_MODULE_TYPES", |