diff options
74 files changed, 2587 insertions, 324 deletions
diff --git a/Android.bp b/Android.bp index 9c2bb4366..97233f555 100644 --- a/Android.bp +++ b/Android.bp @@ -132,6 +132,7 @@ bootstrap_go_package { pkgPath: "android/soong/cc/config", deps: [ "soong-android", + "soong-remoteexec", ], srcs: [ "cc/config/clang.go", @@ -186,6 +187,7 @@ bootstrap_go_package { "cc/rs.go", "cc/sanitize.go", "cc/sabi.go", + "cc/sdk.go", "cc/snapshot_utils.go", "cc/stl.go", "cc/strip.go", @@ -546,6 +548,22 @@ bootstrap_go_package { pluginFor: ["soong_build"], } +bootstrap_go_package { + name: "soong-remoteexec", + pkgPath: "android/soong/remoteexec", + deps: [ + "blueprint", + "soong-android", + ], + srcs: [ + "remoteexec/remoteexec.go", + ], + testSrcs: [ + "remoteexec/remoteexec_test.go", + ], + pluginFor: ["soong_build"], +} + // // Defaults to enable various configurations of host bionic // @@ -4,3 +4,4 @@ per-file ndk_*.go, *gen_stub_libs.py = danalbert@google.com per-file clang.go,global.go = srhines@google.com, chh@google.com, pirama@google.com, yikong@google.com per-file tidy.go = srhines@google.com, chh@google.com per-file lto.go,pgo.go = srhines@google.com, pirama@google.com, yikong@google.com +per-file docs/map_files.md = danalbert@google.com, enh@google.com, jiyong@google.com @@ -421,6 +421,7 @@ soong_config_module_type { config_namespace: "acme", variables: ["board"], bool_variables: ["feature"], + value_variables: ["width"], properties: ["cflags", "srcs"], } @@ -431,8 +432,9 @@ soong_config_string_variable { ``` This example describes a new `acme_cc_defaults` module type that extends the -`cc_defaults` module type, with two additional conditionals based on variables -`board` and `feature`, which can affect properties `cflags` and `srcs`. +`cc_defaults` module type, with three additional conditionals based on +variables `board`, `feature` and `width`, which can affect properties `cflags` +and `srcs`. The values of the variables can be set from a product's `BoardConfig.mk` file: ``` @@ -443,6 +445,7 @@ SOONG_CONFIG_acme += \ SOONG_CONFIG_acme_board := soc_a SOONG_CONFIG_acme_feature := true +SOONG_CONFIG_acme_width := 200 ``` The `acme_cc_defaults` module type can be used anywhere after the definition in @@ -471,6 +474,9 @@ acme_cc_defaults { feature: { cflags: ["-DFEATURE"], }, + width: { + cflags: ["-DWIDTH=%s"], + }, }, } @@ -482,7 +488,7 @@ cc_library { ``` With the `BoardConfig.mk` snippet above, libacme_foo would build with -cflags "-DGENERIC -DSOC_A -DFEATURE". +cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200". `soong_config_module_type` modules will work best when used to wrap defaults modules (`cc_defaults`, `java_defaults`, etc.), which can then be referenced diff --git a/android/apex.go b/android/apex.go index cbaf1c71a..9bf6fc717 100644 --- a/android/apex.go +++ b/android/apex.go @@ -19,6 +19,8 @@ import ( "sort" "strconv" "sync" + + "github.com/google/blueprint" ) const ( @@ -32,6 +34,14 @@ type ApexInfo struct { MinSdkVersion int } +// Extracted from ApexModule to make it easier to define custom subsets of the +// ApexModule interface and improve code navigation within the IDE. +type DepIsInSameApex interface { + // DepIsInSameApex tests if the other module 'dep' is installed to the same + // APEX as this module + DepIsInSameApex(ctx BaseModuleContext, dep Module) bool +} + // ApexModule is the interface that a module type is expected to implement if // the module has to be built differently depending on whether the module // is destined for an apex or not (installed to one of the regular partitions). @@ -49,6 +59,8 @@ type ApexInfo struct { // respectively. type ApexModule interface { Module + DepIsInSameApex + apexModuleBase() *ApexModuleBase // Marks that this module should be built for the specified APEXes. @@ -88,10 +100,6 @@ type ApexModule interface { // Tests if this module is available for the specified APEX or ":platform" AvailableFor(what string) bool - // DepIsInSameApex tests if the other module 'dep' is installed to the same - // APEX as this module - DepIsInSameApex(ctx BaseModuleContext, dep Module) bool - // Returns the highest version which is <= maxSdkVersion. // For example, with maxSdkVersion is 10 and versionList is [9,11] // it returns 9 as string @@ -111,6 +119,15 @@ type ApexProperties struct { Info ApexInfo `blueprint:"mutated"` } +// Marker interface that identifies dependencies that are excluded from APEX +// contents. +type ExcludeFromApexContentsTag interface { + blueprint.DependencyTag + + // Method that differentiates this interface from others. + ExcludeFromApexContents() +} + // Provides default implementation for the ApexModule interface. APEX-aware // modules are expected to include this struct and call InitApexModule(). type ApexModuleBase struct { diff --git a/android/api_levels.go b/android/api_levels.go index 4f6efee70..4b7a8fd9d 100644 --- a/android/api_levels.go +++ b/android/api_levels.go @@ -73,7 +73,7 @@ func getApiLevelsMap(config Config) map[string]int { "P": 28, "Q": 29, } - for i, codename := range config.PlatformVersionCombinedCodenames() { + for i, codename := range config.PlatformVersionActiveCodenames() { apiLevelsMap[codename] = baseApiLevel + i } diff --git a/android/config.go b/android/config.go index 558c828e0..c297b0597 100644 --- a/android/config.go +++ b/android/config.go @@ -652,22 +652,6 @@ func (c *config) PlatformVersionActiveCodenames() []string { return c.productVariables.Platform_version_active_codenames } -// Codenames that are available in the branch but not included in the current -// lunch target. -func (c *config) PlatformVersionFutureCodenames() []string { - return c.productVariables.Platform_version_future_codenames -} - -// All possible codenames in the current branch. NB: Not named AllCodenames -// because "all" has historically meant "active" in make, and still does in -// build.prop. -func (c *config) PlatformVersionCombinedCodenames() []string { - combined := []string{} - combined = append(combined, c.PlatformVersionActiveCodenames()...) - combined = append(combined, c.PlatformVersionFutureCodenames()...) - return combined -} - func (c *config) ProductAAPTConfig() []string { return c.productVariables.AAPTConfig } diff --git a/android/module.go b/android/module.go index 80f477b04..02b2c8926 100644 --- a/android/module.go +++ b/android/module.go @@ -128,6 +128,13 @@ type BaseModuleContext interface { // and returns a top-down dependency path from a start module to current child module. GetWalkPath() []Module + // GetTagPath is supposed to be called in visit function passed in WalkDeps() + // and returns a top-down dependency tags path from a start module to current child module. + // It has one less entry than GetWalkPath() as it contains the dependency tags that + // exist between each adjacent pair of modules in the GetWalkPath(). + // GetTagPath()[i] is the tag between GetWalkPath()[i] and GetWalkPath()[i+1] + GetTagPath() []blueprint.DependencyTag + AddMissingDependencies(missingDeps []string) Target() Target @@ -220,6 +227,7 @@ type Module interface { InstallBypassMake() bool InstallForceOS() *OsType SkipInstall() + IsSkipInstall() bool ExportedToMake() bool InitRc() Paths VintfFragments() Paths @@ -943,6 +951,10 @@ func (m *ModuleBase) SkipInstall() { m.commonProperties.SkipInstall = true } +func (m *ModuleBase) IsSkipInstall() bool { + return m.commonProperties.SkipInstall == true +} + func (m *ModuleBase) ExportedToMake() bool { return m.commonProperties.NamespaceExportedToMake } @@ -1400,6 +1412,7 @@ type baseModuleContext struct { debug bool walkPath []Module + tagPath []blueprint.DependencyTag strictVisitDeps bool // If true, enforce that all dependencies are enabled } @@ -1696,6 +1709,7 @@ func (b *baseModuleContext) WalkDepsBlueprint(visit func(blueprint.Module, bluep func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) { b.walkPath = []Module{b.Module()} + b.tagPath = []blueprint.DependencyTag{} b.bp.WalkDeps(func(child, parent blueprint.Module) bool { childAndroidModule, _ := child.(Module) parentAndroidModule, _ := parent.(Module) @@ -1703,8 +1717,10 @@ func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) { // record walkPath before visit for b.walkPath[len(b.walkPath)-1] != parentAndroidModule { b.walkPath = b.walkPath[0 : len(b.walkPath)-1] + b.tagPath = b.tagPath[0 : len(b.tagPath)-1] } b.walkPath = append(b.walkPath, childAndroidModule) + b.tagPath = append(b.tagPath, b.OtherModuleDependencyTag(childAndroidModule)) return visit(childAndroidModule, parentAndroidModule) } else { return false @@ -1716,6 +1732,10 @@ func (b *baseModuleContext) GetWalkPath() []Module { return b.walkPath } +func (b *baseModuleContext) GetTagPath() []blueprint.DependencyTag { + return b.tagPath +} + func (m *moduleContext) VisitAllModuleVariants(visit func(Module)) { m.bp.VisitAllModuleVariants(func(module blueprint.Module) { visit(module.(Module)) diff --git a/android/mutator.go b/android/mutator.go index a46d4beb1..10a815ae4 100644 --- a/android/mutator.go +++ b/android/mutator.go @@ -103,7 +103,7 @@ var postDeps = []RegisterMutatorFunc{ registerPathDepsMutator, RegisterPrebuiltsPostDepsMutators, RegisterVisibilityRuleEnforcer, - registerNeverallowMutator, + RegisterNeverallowMutator, RegisterOverridePostDepsMutators, } diff --git a/android/neverallow.go b/android/neverallow.go index 8fcfb8a6e..cf09792e0 100644 --- a/android/neverallow.go +++ b/android/neverallow.go @@ -17,6 +17,7 @@ package android import ( "path/filepath" "reflect" + "regexp" "strconv" "strings" @@ -41,7 +42,7 @@ import ( // counts as a match // - it has none of the "Without" properties matched (same rules as above) -func registerNeverallowMutator(ctx RegisterMutatorsContext) { +func RegisterNeverallowMutator(ctx RegisterMutatorsContext) { ctx.BottomUp("neverallow", neverallowMutator).Parallel() } @@ -53,6 +54,7 @@ func init() { AddNeverAllowRules(createLibcoreRules()...) AddNeverAllowRules(createMediaRules()...) AddNeverAllowRules(createJavaDeviceForHostRules()...) + AddNeverAllowRules(createCcSdkVariantRules()...) } // Add a NeverAllow rule to the set of rules to apply. @@ -146,7 +148,8 @@ func createLibcoreRules() []Rule { rules := []Rule{ NeverAllow(). NotIn(coreLibraryProjects...). - With("sdk_version", "none"), + With("sdk_version", "none"). + WithoutMatcher("name", Regexp("^android_.*stubs_current$")), } return rules @@ -175,6 +178,37 @@ func createJavaDeviceForHostRules() []Rule { } } +func createCcSdkVariantRules() []Rule { + sdkVersionOnlyWhitelist := []string{ + // derive_sdk_prefer32 has stem: "derive_sdk" which conflicts with the derive_sdk. + // This sometimes works because the APEX modules that contain derive_sdk and + // derive_sdk_prefer32 suppress the platform installation rules, but fails when + // the APEX modules contain the SDK variant and the platform variant still exists. + "frameworks/base/apex/sdkextensions/derive_sdk", + } + + platformVariantPropertiesWhitelist := []string{ + // android_native_app_glue and libRSSupport use native_window.h but target old + // sdk versions (minimum and 9 respectively) where libnativewindow didn't exist, + // so they can't add libnativewindow to shared_libs to get the header directory + // for the platform variant. Allow them to use the platform variant + // property to set shared_libs. + "prebuilts/ndk", + "frameworks/rs", + } + + return []Rule{ + NeverAllow(). + NotIn(sdkVersionOnlyWhitelist...). + WithMatcher("sdk_variant_only", isSetMatcherInstance). + Because("sdk_variant_only can only be used in whitelisted projects"), + NeverAllow(). + NotIn(platformVariantPropertiesWhitelist...). + WithMatcher("platform.shared_libs", isSetMatcherInstance). + Because("platform variant properties can only be used in whitelisted projects"), + } +} + func neverallowMutator(ctx BottomUpMutatorContext) { m, ok := ctx.Module().(Module) if !ok { @@ -213,7 +247,7 @@ func neverallowMutator(ctx BottomUpMutatorContext) { } type ValueMatcher interface { - test(string) bool + Test(string) bool String() string } @@ -221,7 +255,7 @@ type equalMatcher struct { expected string } -func (m *equalMatcher) test(value string) bool { +func (m *equalMatcher) Test(value string) bool { return m.expected == value } @@ -232,7 +266,7 @@ func (m *equalMatcher) String() string { type anyMatcher struct { } -func (m *anyMatcher) test(value string) bool { +func (m *anyMatcher) Test(value string) bool { return true } @@ -246,7 +280,7 @@ type startsWithMatcher struct { prefix string } -func (m *startsWithMatcher) test(value string) bool { +func (m *startsWithMatcher) Test(value string) bool { return strings.HasPrefix(value, m.prefix) } @@ -254,6 +288,30 @@ func (m *startsWithMatcher) String() string { return ".starts-with(" + m.prefix + ")" } +type regexMatcher struct { + re *regexp.Regexp +} + +func (m *regexMatcher) Test(value string) bool { + return m.re.MatchString(value) +} + +func (m *regexMatcher) String() string { + return ".regexp(" + m.re.String() + ")" +} + +type isSetMatcher struct{} + +func (m *isSetMatcher) Test(value string) bool { + return value != "" +} + +func (m *isSetMatcher) String() string { + return ".is-set" +} + +var isSetMatcherInstance = &isSetMatcher{} + type ruleProperty struct { fields []string // e.x.: Vndk.Enabled matcher ValueMatcher @@ -457,6 +515,14 @@ func StartsWith(prefix string) ValueMatcher { return &startsWithMatcher{prefix} } +func Regexp(re string) ValueMatcher { + r, err := regexp.Compile(re) + if err != nil { + panic(err) + } + return ®exMatcher{r} +} + // assorted utils func cleanPaths(paths []string) []string { @@ -507,7 +573,7 @@ func hasProperty(properties []interface{}, prop ruleProperty) bool { } check := func(value string) bool { - return prop.matcher.test(value) + return prop.matcher.Test(value) } if matchValue(propertiesValue, check) { @@ -564,6 +630,6 @@ func neverallowRules(config Config) []Rule { // Overrides the default neverallow rules for the supplied config. // // For testing only. -func setTestNeverallowRules(config Config, testRules []Rule) { +func SetTestNeverallowRules(config Config, testRules []Rule) { config.Once(neverallowRulesKey, func() interface{} { return testRules }) } diff --git a/android/neverallow_test.go b/android/neverallow_test.go index 6f07a4a73..2fc42e31f 100644 --- a/android/neverallow_test.go +++ b/android/neverallow_test.go @@ -227,6 +227,16 @@ var neverallowTests = []struct { }, }, { + name: "sdk_version: \"none\" on android_*stubs_current stub", + fs: map[string][]byte{ + "frameworks/base/Android.bp": []byte(` + java_library { + name: "android_stubs_current", + sdk_version: "none", + }`), + }, + }, + { name: "sdk_version: \"none\" outside core libraries", fs: map[string][]byte{ "Android.bp": []byte(` @@ -249,6 +259,50 @@ var neverallowTests = []struct { }`), }, }, + // CC sdk rule tests + { + name: `"sdk_variant_only" outside whitelist`, + fs: map[string][]byte{ + "Android.bp": []byte(` + cc_library { + name: "outside_whitelist", + sdk_version: "current", + sdk_variant_only: true, + }`), + }, + expectedErrors: []string{ + `module "outside_whitelist": violates neverallow`, + }, + }, + { + name: `"sdk_variant_only: false" outside whitelist`, + fs: map[string][]byte{ + "Android.bp": []byte(` + cc_library { + name: "outside_whitelist", + sdk_version: "current", + sdk_variant_only: false, + }`), + }, + expectedErrors: []string{ + `module "outside_whitelist": violates neverallow`, + }, + }, + { + name: `"platform" outside whitelist`, + fs: map[string][]byte{ + "Android.bp": []byte(` + cc_library { + name: "outside_whitelist", + platform: { + shared_libs: ["libfoo"], + }, + }`), + }, + expectedErrors: []string{ + `module "outside_whitelist": violates neverallow`, + }, + }, } func TestNeverallow(t *testing.T) { @@ -259,7 +313,7 @@ func TestNeverallow(t *testing.T) { t.Run(test.name, func(t *testing.T) { // If the test has its own rules then use them instead of the default ones. if test.rules != nil { - setTestNeverallowRules(config, test.rules) + SetTestNeverallowRules(config, test.rules) } _, errs := testNeverallow(config) CheckErrorsAgainstExpectations(t, errs, test.expectedErrors) @@ -273,7 +327,7 @@ func testNeverallow(config Config) (*TestContext, []error) { ctx.RegisterModuleType("java_library", newMockJavaLibraryModule) ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule) ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule) - ctx.PostDepsMutators(registerNeverallowMutator) + ctx.PostDepsMutators(RegisterNeverallowMutator) ctx.Register(config) _, errs := ctx.ParseBlueprintsFiles("Android.bp") @@ -289,6 +343,8 @@ type mockCcLibraryProperties struct { Include_dirs []string Vendor_available *bool Static_libs []string + Sdk_version *string + Sdk_variant_only *bool Vndk struct { Enabled *bool @@ -305,6 +361,10 @@ type mockCcLibraryProperties struct { Cflags []string } } + + Platform struct { + Shared_libs []string + } } type mockCcLibraryModule struct { diff --git a/android/prebuilt.go b/android/prebuilt.go index c902ec826..82745a498 100644 --- a/android/prebuilt.go +++ b/android/prebuilt.go @@ -39,6 +39,12 @@ var PrebuiltDepTag prebuiltDependencyTag // Mark this tag so dependencies that use it are excluded from visibility enforcement. func (t prebuiltDependencyTag) ExcludeFromVisibilityEnforcement() {} +// Mark this tag so dependencies that use it are excluded from APEX contents. +func (t prebuiltDependencyTag) ExcludeFromApexContents() {} + +var _ ExcludeFromVisibilityEnforcementTag = PrebuiltDepTag +var _ ExcludeFromApexContentsTag = PrebuiltDepTag + type PrebuiltProperties struct { // When prefer is set to true the prebuilt will be used instead of any source module with // a matching name. diff --git a/android/sdk.go b/android/sdk.go index 66094cda0..6f62f552c 100644 --- a/android/sdk.go +++ b/android/sdk.go @@ -22,17 +22,30 @@ import ( "github.com/google/blueprint/proptools" ) +// Extracted from SdkAware to make it easier to define custom subsets of the +// SdkAware interface and improve code navigation within the IDE. +// +// In addition to its use in SdkAware this interface must also be implemented by +// APEX to specify the SDKs required by that module and its contents. e.g. APEX +// is expected to implement RequiredSdks() by reading its own properties like +// `uses_sdks`. +type RequiredSdks interface { + // The set of SDKs required by an APEX and its contents. + RequiredSdks() SdkRefs +} + // SdkAware is the interface that must be supported by any module to become a member of SDK or to be // built with SDK type SdkAware interface { Module + RequiredSdks + sdkBase() *SdkBase MakeMemberOf(sdk SdkRef) IsInAnySdk() bool ContainingSdk() SdkRef MemberName() string BuildWithSdks(sdks SdkRefs) - RequiredSdks() SdkRefs } // SdkRef refers to a version of an SDK diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go index fa1e20448..619cf8615 100644 --- a/android/soong_config_modules.go +++ b/android/soong_config_modules.go @@ -73,6 +73,9 @@ type soongConfigModuleTypeImportProperties struct { // feature: { // cflags: ["-DFEATURE"], // }, +// width: { +// cflags: ["-DWIDTH=%s"], +// }, // }, // } // @@ -90,6 +93,7 @@ type soongConfigModuleTypeImportProperties struct { // config_namespace: "acme", // variables: ["board"], // bool_variables: ["feature"], +// value_variables: ["width"], // properties: ["cflags", "srcs"], // } // @@ -107,8 +111,9 @@ type soongConfigModuleTypeImportProperties struct { // // SOONG_CONFIG_acme_board := soc_a // SOONG_CONFIG_acme_feature := true +// SOONG_CONFIG_acme_width := 200 // -// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE". +// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200". func soongConfigModuleTypeImportFactory() Module { module := &soongConfigModuleTypeImport{} @@ -122,7 +127,10 @@ func soongConfigModuleTypeImportFactory() Module { } func (m *soongConfigModuleTypeImport) Name() string { - return "soong_config_module_type_import_" + soongconfig.CanonicalizeToProperty(m.properties.From) + // The generated name is non-deterministic, but it does not + // matter because this module does not emit any rules. + return soongconfig.CanonicalizeToProperty(m.properties.From) + + "soong_config_module_type_import_" + fmt.Sprintf("%p", m) } func (*soongConfigModuleTypeImport) Nameless() {} @@ -148,6 +156,7 @@ type soongConfigModuleTypeModule struct { // config_namespace: "acme", // variables: ["board"], // bool_variables: ["feature"], +// value_variables: ["width"], // properties: ["cflags", "srcs"], // } // @@ -171,6 +180,9 @@ type soongConfigModuleTypeModule struct { // feature: { // cflags: ["-DFEATURE"], // }, +// width: { +// cflags: ["-DWIDTH=%s"], +// }, // }, // } // @@ -189,6 +201,7 @@ type soongConfigModuleTypeModule struct { // // SOONG_CONFIG_acme_board := soc_a // SOONG_CONFIG_acme_feature := true +// SOONG_CONFIG_acme_width := 200 // // Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE". func soongConfigModuleTypeFactory() Module { @@ -349,7 +362,12 @@ func soongConfigModuleFactory(factory blueprint.ModuleFactory, AddLoadHook(module, func(ctx LoadHookContext) { config := ctx.Config().VendorConfig(moduleType.ConfigNamespace) - for _, ps := range soongconfig.PropertiesToApply(moduleType, conditionalProps, config) { + newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, config) + if err != nil { + ctx.ModuleErrorf("%s", err) + return + } + for _, ps := range newProps { ctx.AppendProperties(ps) } }) diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go index 1cf060dc0..f905b1ab2 100644 --- a/android/soong_config_modules_test.go +++ b/android/soong_config_modules_test.go @@ -45,6 +45,7 @@ func TestSoongConfigModule(t *testing.T) { config_namespace: "acme", variables: ["board", "feature1", "FEATURE3"], bool_variables: ["feature2"], + value_variables: ["size"], properties: ["cflags", "srcs"], } @@ -82,6 +83,9 @@ func TestSoongConfigModule(t *testing.T) { cflags: ["-DSOC_B"], }, }, + size: { + cflags: ["-DSIZE=%s"], + }, feature1: { cflags: ["-DFEATURE1"], }, @@ -101,6 +105,7 @@ func TestSoongConfigModule(t *testing.T) { config.TestProductVariables.VendorVars = map[string]map[string]string{ "acme": map[string]string{ "board": "soc_a", + "size": "42", "feature1": "true", "feature2": "false", // FEATURE3 unset @@ -121,7 +126,7 @@ func TestSoongConfigModule(t *testing.T) { FailIfErrored(t, errs) foo := ctx.ModuleForTests("foo", "").Module().(*soongConfigTestModule) - if g, w := foo.props.Cflags, []string{"-DGENERIC", "-DSOC_A", "-DFEATURE1"}; !reflect.DeepEqual(g, w) { + if g, w := foo.props.Cflags, []string{"-DGENERIC", "-DSIZE=42", "-DSOC_A", "-DFEATURE1"}; !reflect.DeepEqual(g, w) { t.Errorf("wanted foo cflags %q, got %q", w, g) } } diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go index 2d6063d79..142a81387 100644 --- a/android/soongconfig/modules.go +++ b/android/soongconfig/modules.go @@ -112,6 +112,10 @@ type ModuleTypeProperties struct { // the list of boolean SOONG_CONFIG variables that this module type will read Bool_variables []string + // the list of SOONG_CONFIG variables that this module type will read. The value will be + // inserted into the properties with %s substitution. + Value_variables []string + // the list of properties that this module type will extend. Properties []string } @@ -161,6 +165,18 @@ func processModuleTypeDef(v *SoongConfigDefinition, def *parser.Module) (errs [] }) } + for _, name := range props.Value_variables { + if name == "" { + return []error{fmt.Errorf("value_variables entry must not be blank")} + } + + mt.Variables = append(mt.Variables, &valueVariable{ + baseVariable: baseVariable{ + variable: name, + }, + }) + } + return nil } @@ -404,15 +420,17 @@ func typeForPropertyFromPropertyStruct(ps interface{}, property string) reflect. // PropertiesToApply returns the applicable properties from a ModuleType that should be applied // based on SoongConfig values. -func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) []interface{} { +func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) { var ret []interface{} props = props.Elem().FieldByName(soongConfigProperty) for i, c := range moduleType.Variables { - if ps := c.PropertiesToApply(config, props.Field(i)); ps != nil { + if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil { + return nil, err + } else if ps != nil { ret = append(ret, ps) } } - return ret + return ret, nil } type ModuleType struct { @@ -438,7 +456,7 @@ type soongConfigVariable interface { // PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied // to the module. - PropertiesToApply(config SoongConfig, values reflect.Value) interface{} + PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) } type baseVariable struct { @@ -473,14 +491,14 @@ func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) } } -func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) interface{} { +func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) { for j, v := range s.values { if config.String(s.variable) == v { - return values.Field(j).Interface() + return values.Field(j).Interface(), nil } } - return nil + return nil, nil } type boolVariable struct { @@ -495,11 +513,83 @@ func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) { v.Set(reflect.Zero(typ)) } -func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) interface{} { +func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) { if config.Bool(b.variable) { - return values.Interface() + return values.Interface(), nil + } + + return nil, nil +} + +type valueVariable struct { + baseVariable +} + +func (s *valueVariable) variableValuesType() reflect.Type { + return emptyInterfaceType +} + +func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) { + v.Set(reflect.Zero(typ)) +} + +func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) { + if !config.IsSet(s.variable) { + return nil, nil + } + configValue := config.String(s.variable) + + propStruct := values.Elem().Elem() + for i := 0; i < propStruct.NumField(); i++ { + field := propStruct.Field(i) + kind := field.Kind() + if kind == reflect.Ptr { + if field.IsNil() { + continue + } + field = field.Elem() + } + switch kind { + case reflect.String: + err := printfIntoProperty(field, configValue) + if err != nil { + return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err) + } + case reflect.Slice: + for j := 0; j < field.Len(); j++ { + err := printfIntoProperty(field.Index(j), configValue) + if err != nil { + return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err) + } + } + case reflect.Bool: + // Nothing to do + default: + return nil, fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, propStruct.Type().Field(i).Name, kind) + } + } + + return values.Interface(), nil +} + +func printfIntoProperty(propertyValue reflect.Value, configValue string) error { + s := propertyValue.String() + + count := strings.Count(s, "%") + if count == 0 { + return nil } + if count > 1 { + return fmt.Errorf("value variable properties only support a single '%%'") + } + + if !strings.Contains(s, "%s") { + return fmt.Errorf("unsupported %% in value variable property") + } + + propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, configValue))) + return nil } diff --git a/android/variable.go b/android/variable.go index 612d138c3..3b3916e2f 100644 --- a/android/variable.go +++ b/android/variable.go @@ -151,7 +151,6 @@ type productVariables struct { Platform_sdk_codename *string `json:",omitempty"` Platform_sdk_final *bool `json:",omitempty"` Platform_version_active_codenames []string `json:",omitempty"` - Platform_version_future_codenames []string `json:",omitempty"` Platform_vndk_version *string `json:",omitempty"` Platform_systemsdk_versions []string `json:",omitempty"` Platform_security_patch *string `json:",omitempty"` @@ -255,7 +254,7 @@ type productVariables struct { ClangTidy *bool `json:",omitempty"` TidyChecks *string `json:",omitempty"` - SamplingPGO *bool `json:",omitempty"` + SamplingPGO *bool `json:",omitempty"` NativeLineCoverage *bool `json:",omitempty"` Native_coverage *bool `json:",omitempty"` @@ -358,7 +357,6 @@ func (v *productVariables) SetDefaultConfig() { Platform_sdk_codename: stringPtr("Q"), Platform_sdk_final: boolPtr(false), Platform_version_active_codenames: []string{"Q"}, - Platform_version_future_codenames: []string{"Q"}, Platform_vndk_version: stringPtr("Q"), HostArch: stringPtr("x86_64"), diff --git a/apex/apex.go b/apex/apex.go index f0eb1dec9..8463ac123 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -18,6 +18,7 @@ import ( "fmt" "path" "path/filepath" + "regexp" "sort" "strconv" "strings" @@ -123,7 +124,6 @@ func makeApexAvailableWhitelist() map[string][]string { "crtbegin_dynamic1", "crtbegin_so1", "crtbrand", - "conscrypt.module.intra.core.api.stubs", "dex2oat_headers", "dt_fd_forward_export", "icu4c_extra_headers", @@ -305,25 +305,13 @@ func makeApexAvailableWhitelist() map[string][]string { "android.hidl.memory.token@1.0", "android.hidl.memory@1.0", "android.hidl.safe_union@1.0", - "gemmlowp_headers", "libarect", "libbuildversion", - "libeigen", - "libfmq", "libmath", - "libneuralnetworks_common", - "libneuralnetworks_headers", "libprocessgroup", "libprocessgroup_headers", "libprocpartition", "libsync", - "libtextclassifier_hash", - "libtextclassifier_hash_headers", - "libtextclassifier_hash_static", - "libtflite_kernel_utils", - "philox_random", - "philox_random_headers", - "tensorflow_headers", } // // Module separator @@ -848,10 +836,13 @@ func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) { // Mark the direct and transitive dependencies of apex bundles so that they // can be built for the apex bundles. func apexDepsMutator(mctx android.TopDownMutatorContext) { + if !mctx.Module().Enabled() { + return + } var apexBundles []android.ApexInfo var directDep bool if a, ok := mctx.Module().(*apexBundle); ok && !a.vndkApex { - apexBundles = []android.ApexInfo{android.ApexInfo{ + apexBundles = []android.ApexInfo{{ ApexName: mctx.ModuleName(), MinSdkVersion: a.minSdkVersion(mctx), }} @@ -865,22 +856,33 @@ func apexDepsMutator(mctx android.TopDownMutatorContext) { return } - cur := mctx.Module().(interface { - DepIsInSameApex(android.BaseModuleContext, android.Module) bool - }) + cur := mctx.Module().(android.DepIsInSameApex) mctx.VisitDirectDeps(func(child android.Module) { depName := mctx.OtherModuleName(child) if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() && - cur.DepIsInSameApex(mctx, child) { + (cur.DepIsInSameApex(mctx, child) || inAnySdk(child)) { android.UpdateApexDependency(apexBundles, depName, directDep) am.BuildForApexes(apexBundles) } }) } +// If a module in an APEX depends on a module from an SDK then it needs an APEX +// specific variant created for it. Refer to sdk.sdkDepsReplaceMutator. +func inAnySdk(module android.Module) bool { + if sa, ok := module.(android.SdkAware); ok { + return sa.IsInAnySdk() + } + + return false +} + // Create apex variations if a module is included in APEX(s). func apexMutator(mctx android.BottomUpMutatorContext) { + if !mctx.Module().Enabled() { + return + } if am, ok := mctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() { am.CreateApexVariations(mctx) } else if a, ok := mctx.Module().(*apexBundle); ok && !a.vndkApex { @@ -918,6 +920,9 @@ func addFlattenedFileContextsInfos(ctx android.BaseModuleContext, fileContextsIn } func apexFlattenedMutator(mctx android.BottomUpMutatorContext) { + if !mctx.Module().Enabled() { + return + } if ab, ok := mctx.Module().(*apexBundle); ok { var variants []string switch proptools.StringDefault(ab.properties.Payload_type, "image") { @@ -1811,6 +1816,24 @@ func (a *apexBundle) minSdkVersion(ctx android.BaseModuleContext) int { return android.FutureApiLevel } +// A regexp for removing boilerplate from BaseDependencyTag from the string representation of +// a dependency tag. +var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:blueprint.BaseDependencyTag{}\E(, )?`) + +func PrettyPrintTag(tag blueprint.DependencyTag) string { + // Use tag's custom String() method if available. + if stringer, ok := tag.(fmt.Stringer); ok { + return stringer.String() + } + + // Otherwise, get a default string representation of the tag's struct. + tagString := fmt.Sprintf("%#v", tag) + + // Remove the boilerplate from BaseDependencyTag as it adds no value. + tagString = tagCleaner.ReplaceAllString(tagString, "") + return tagString +} + // Ensures that the dependencies are marked as available for this APEX func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) { // Let's be practical. Availability for test, host, and the VNDK apex isn't important @@ -1834,12 +1857,23 @@ func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) { apexName := ctx.ModuleName() fromName := ctx.OtherModuleName(from) toName := ctx.OtherModuleName(to) + + // If `to` is not actually in the same APEX as `from` then it does not need apex_available and neither + // do any of its dependencies. + if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) { + // As soon as the dependency graph crosses the APEX boundary, don't go further. + return false + } + if to.AvailableFor(apexName) || whitelistedApexAvailable(apexName, toName) { return true } message := "" - for _, m := range ctx.GetWalkPath()[1:] { - message = fmt.Sprintf("%s\n -> %s", message, m.String()) + tagPath := ctx.GetTagPath() + // Skip the first module as that will be added at the start of the error message by ctx.ModuleErrorf(). + walkPath := ctx.GetWalkPath()[1:] + for i, m := range walkPath { + message = fmt.Sprintf("%s\n via tag %s\n -> %s", message, PrettyPrintTag(tagPath[i]), m.String()) } ctx.ModuleErrorf("%q requires %q that is not available for the APEX. Dependency path:%s", fromName, toName, message) // Visit this module's dependencies to check and report any issues with their availability. @@ -1949,6 +1983,9 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { // TODO(jiyong) do this using walkPayloadDeps ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool { depTag := ctx.OtherModuleDependencyTag(child) + if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok { + return false + } depName := ctx.OtherModuleName(child) if _, isDirectDep := parent.(*apexBundle); isDirectDep { switch depTag { @@ -2116,7 +2153,7 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName)) } } else if am.CanHaveApexVariants() && am.IsInstallableToApex() { - ctx.ModuleErrorf("unexpected tag %q for indirect dependency %q", depTag, depName) + ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", PrettyPrintTag(depTag), depName) } } } diff --git a/apex/apex_test.go b/apex/apex_test.go index c5edc3b67..9f55728f2 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -27,6 +27,7 @@ import ( "android/soong/android" "android/soong/cc" + "android/soong/dexpreopt" "android/soong/java" ) @@ -158,6 +159,7 @@ func testApexContext(t *testing.T, bp string, handlers ...testCustomizer) (*andr "my_include": nil, "foo/bar/MyClass.java": nil, "prebuilt.jar": nil, + "prebuilt.so": nil, "vendor/foo/devkeys/test.x509.pem": nil, "vendor/foo/devkeys/test.pk8": nil, "testkey.x509.pem": nil, @@ -367,7 +369,7 @@ func TestBasicApex(t *testing.T) { apex_available: [ "myapex" ], } - cc_library { + cc_library_shared { name: "mylib2", srcs: ["mylib.cpp"], system_shared_libs: [], @@ -381,6 +383,16 @@ func TestBasicApex(t *testing.T) { ], } + cc_prebuilt_library_shared { + name: "mylib2", + srcs: ["prebuilt.so"], + // TODO: remove //apex_available:platform + apex_available: [ + "//apex_available:platform", + "myapex", + ], + } + cc_library_static { name: "libstatic", srcs: ["mylib.cpp"], @@ -3384,7 +3396,7 @@ func TestApexWithApps(t *testing.T) { } // JNI libraries including transitive deps are for _, jni := range []string{"libjni", "libfoo"} { - jniOutput := ctx.ModuleForTests(jni, "android_arm64_armv8-a_shared_myapex").Module().(*cc.Module).OutputFile() + jniOutput := ctx.ModuleForTests(jni, "android_arm64_armv8-a_sdk_shared_myapex").Module().(*cc.Module).OutputFile() // ... embedded inside APK (jnilibs.zip) ensureListContains(t, appZipRule.Implicits.Strings(), jniOutput.String()) // ... and not directly inside the APEX @@ -3581,12 +3593,12 @@ func TestApexAvailable_DirectDep(t *testing.T) { func TestApexAvailable_IndirectDep(t *testing.T) { // libbbaz is an indirect dep testApexError(t, `requires "libbaz" that is not available for the APEX. Dependency path: +.*via tag apex\.dependencyTag.*"sharedLib".* .*-> libfoo.*link:shared.* -.*-> libfoo.*link:static.* +.*via tag cc\.DependencyTag.*"shared".* .*-> libbar.*link:shared.* -.*-> libbar.*link:static.* -.*-> libbaz.*link:shared.* -.*-> libbaz.*link:static.*`, ` +.*via tag cc\.DependencyTag.*"shared".* +.*-> libbaz.*link:shared.*`, ` apex { name: "myapex", key: "myapex.key", @@ -4180,6 +4192,27 @@ func TestApexWithJniLibs(t *testing.T) { }) } +func TestApexMutatorsDontRunIfDisabled(t *testing.T) { + ctx, _ := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + } + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + `, func(fs map[string][]byte, config android.Config) { + delete(config.Targets, android.Android) + config.AndroidCommonTarget = android.Target{} + }) + + if expected, got := []string{""}, ctx.ModuleVariantsForTests("myapex"); !reflect.DeepEqual(expected, got) { + t.Errorf("Expected variants: %v, but got: %v", expected, got) + } +} + func TestApexWithJniLibs_Errors(t *testing.T) { testApexError(t, `jni_libs: "xxx" is not a cc_library`, ` apex { @@ -4233,6 +4266,175 @@ func TestAppBundle(t *testing.T) { ensureContains(t, content, `"apex_config":{"apex_embedded_apk_config":[{"package_name":"com.android.foo","path":"app/AppFoo/AppFoo.apk"}]}`) } +func testNoUpdatableJarsInBootImage(t *testing.T, errmsg, bp string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) { + t.Helper() + + bp = bp + ` + filegroup { + name: "some-updatable-apex-file_contexts", + srcs: [ + "system/sepolicy/apex/some-updatable-apex-file_contexts", + ], + } + ` + bp += cc.GatherRequiredDepsForTest(android.Android) + bp += java.GatherRequiredDepsForTest() + bp += dexpreopt.BpToolModulesForTest() + + fs := map[string][]byte{ + "a.java": nil, + "a.jar": nil, + "build/make/target/product/security": nil, + "apex_manifest.json": nil, + "AndroidManifest.xml": nil, + "system/sepolicy/apex/some-updatable-apex-file_contexts": nil, + "system/sepolicy/apex/com.android.art.something-file_contexts": nil, + "framework/aidl/a.aidl": nil, + } + cc.GatherRequiredFilesForTest(fs) + + ctx := android.NewTestArchContext() + ctx.RegisterModuleType("apex", BundleFactory) + ctx.RegisterModuleType("apex_key", ApexKeyFactory) + ctx.RegisterModuleType("filegroup", android.FileGroupFactory) + cc.RegisterRequiredBuildComponentsForTest(ctx) + java.RegisterJavaBuildComponents(ctx) + java.RegisterSystemModulesBuildComponents(ctx) + java.RegisterAppBuildComponents(ctx) + java.RegisterDexpreoptBootJarsComponents(ctx) + ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators) + ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators) + ctx.PreDepsMutators(RegisterPreDepsMutators) + ctx.PostDepsMutators(RegisterPostDepsMutators) + + config := android.TestArchConfig(buildDir, nil, bp, fs) + ctx.Register(config) + + _ = dexpreopt.GlobalSoongConfigForTests(config) + dexpreopt.RegisterToolModulesForTest(ctx) + pathCtx := android.PathContextForTesting(config) + dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx) + transformDexpreoptConfig(dexpreoptConfig) + dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig) + + _, errs := ctx.ParseBlueprintsFiles("Android.bp") + android.FailIfErrored(t, errs) + + _, errs = ctx.PrepareBuildActions(config) + if errmsg == "" { + android.FailIfErrored(t, errs) + } else if len(errs) > 0 { + android.FailIfNoMatchingErrors(t, errmsg, errs) + return + } else { + t.Fatalf("missing expected error %q (0 errors are returned)", errmsg) + } +} + +func TestNoUpdatableJarsInBootImage(t *testing.T) { + bp := ` + java_library { + name: "some-updatable-apex-lib", + srcs: ["a.java"], + apex_available: [ + "some-updatable-apex", + ], + } + + java_library { + name: "some-platform-lib", + srcs: ["a.java"], + installable: true, + } + + java_library { + name: "some-art-lib", + srcs: ["a.java"], + apex_available: [ + "com.android.art.something", + ], + hostdex: true, + } + + apex { + name: "some-updatable-apex", + key: "some-updatable-apex.key", + java_libs: ["some-updatable-apex-lib"], + } + + apex_key { + name: "some-updatable-apex.key", + } + + apex { + name: "com.android.art.something", + key: "com.android.art.something.key", + java_libs: ["some-art-lib"], + } + + apex_key { + name: "com.android.art.something.key", + } + ` + + var error string + var transform func(*dexpreopt.GlobalConfig) + + // updatable jar from ART apex in the ART boot image => ok + transform = func(config *dexpreopt.GlobalConfig) { + config.ArtApexJars = []string{"some-art-lib"} + } + testNoUpdatableJarsInBootImage(t, "", bp, transform) + + // updatable jar from ART apex in the framework boot image => error + error = "module 'some-art-lib' from updatable apex 'com.android.art.something' is not allowed in the framework boot image" + transform = func(config *dexpreopt.GlobalConfig) { + config.BootJars = []string{"some-art-lib"} + } + testNoUpdatableJarsInBootImage(t, error, bp, transform) + + // updatable jar from some other apex in the ART boot image => error + error = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the ART boot image" + transform = func(config *dexpreopt.GlobalConfig) { + config.ArtApexJars = []string{"some-updatable-apex-lib"} + } + testNoUpdatableJarsInBootImage(t, error, bp, transform) + + // updatable jar from some other apex in the framework boot image => error + error = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the framework boot image" + transform = func(config *dexpreopt.GlobalConfig) { + config.BootJars = []string{"some-updatable-apex-lib"} + } + testNoUpdatableJarsInBootImage(t, error, bp, transform) + + // nonexistent jar in the ART boot image => error + error = "failed to find a dex jar path for module 'nonexistent'" + transform = func(config *dexpreopt.GlobalConfig) { + config.ArtApexJars = []string{"nonexistent"} + } + testNoUpdatableJarsInBootImage(t, error, bp, transform) + + // nonexistent jar in the framework boot image => error + error = "failed to find a dex jar path for module 'nonexistent'" + transform = func(config *dexpreopt.GlobalConfig) { + config.BootJars = []string{"nonexistent"} + } + testNoUpdatableJarsInBootImage(t, error, bp, transform) + + // platform jar in the ART boot image => error + error = "module 'some-platform-lib' is part of the platform and not allowed in the ART boot image" + transform = func(config *dexpreopt.GlobalConfig) { + config.ArtApexJars = []string{"some-platform-lib"} + } + testNoUpdatableJarsInBootImage(t, error, bp, transform) + + // platform jar in the framework boot image => ok + transform = func(config *dexpreopt.GlobalConfig) { + config.BootJars = []string{"some-platform-lib"} + } + testNoUpdatableJarsInBootImage(t, "", bp, transform) +} + func TestMain(m *testing.M) { run := func() int { setUp() diff --git a/apex/vndk.go b/apex/vndk.go index f2e913eb3..f948d7608 100644 --- a/apex/vndk.go +++ b/apex/vndk.go @@ -16,6 +16,7 @@ package apex import ( "path/filepath" + "strconv" "strings" "sync" @@ -95,6 +96,10 @@ func apexVndkMutator(mctx android.TopDownMutatorContext) { func apexVndkDepsMutator(mctx android.BottomUpMutatorContext) { if m, ok := mctx.Module().(*cc.Module); ok && cc.IsForVndkApex(mctx, m) { vndkVersion := m.VndkVersion() + // For VNDK-Lite device, we gather core-variants of VNDK-Sp libraries, which doesn't have VNDK version defined + if vndkVersion == "" { + vndkVersion = mctx.DeviceConfig().PlatformVndkVersion() + } vndkApexList := vndkApexList(mctx.Config()) if vndkApex, ok := vndkApexList[vndkVersion]; ok { mctx.AddReverseDependency(mctx.Module(), sharedLibTag, vndkApex) @@ -117,10 +122,13 @@ func makeCompatSymlinks(name string, ctx android.ModuleContext) (symlinks []stri // When all hard-coded references are fixed, remove symbolic links // Note that we should keep following symlinks for older VNDKs (<=29) // Since prebuilt vndk libs still depend on system/lib/vndk path - if strings.HasPrefix(name, vndkApexName) { - vndkVersion := ctx.DeviceConfig().PlatformVndkVersion() - if strings.HasPrefix(name, vndkApexNamePrefix) { - vndkVersion = strings.TrimPrefix(name, vndkApexNamePrefix) + if strings.HasPrefix(name, vndkApexNamePrefix) { + vndkVersion := strings.TrimPrefix(name, vndkApexNamePrefix) + if numVer, err := strconv.Atoi(vndkVersion); err != nil { + ctx.ModuleErrorf("apex_vndk should be named as %v<ver:number>: %s", vndkApexNamePrefix, name) + return + } else if numVer > android.SdkVersion_Android10 { + return } // the name of vndk apex is formatted "com.android.vndk.v" + version apexName := vndkApexNamePrefix + vndkVersion diff --git a/apex/vndk_test.go b/apex/vndk_test.go index a9e26adc8..523ac2630 100644 --- a/apex/vndk_test.go +++ b/apex/vndk_test.go @@ -8,6 +8,59 @@ import ( "android/soong/android" ) +func TestVndkApexForVndkLite(t *testing.T) { + ctx, _ := testApex(t, ` + apex_vndk { + name: "myapex", + key: "myapex.key", + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "libvndk", + srcs: ["mylib.cpp"], + vendor_available: true, + vndk: { + enabled: true, + }, + system_shared_libs: [], + stl: "none", + apex_available: [ "myapex" ], + } + + cc_library { + name: "libvndksp", + srcs: ["mylib.cpp"], + vendor_available: true, + vndk: { + enabled: true, + support_system_process: true, + }, + system_shared_libs: [], + stl: "none", + apex_available: [ "myapex" ], + } + `+vndkLibrariesTxtFiles("current"), func(fs map[string][]byte, config android.Config) { + config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("") + }) + // VNDK-Lite contains only core variants of VNDK-Sp libraries + ensureExactContents(t, ctx, "myapex", "android_common_image", []string{ + "lib/libvndksp.so", + "lib/libc++.so", + "lib64/libvndksp.so", + "lib64/libc++.so", + "etc/llndk.libraries.VER.txt", + "etc/vndkcore.libraries.VER.txt", + "etc/vndksp.libraries.VER.txt", + "etc/vndkprivate.libraries.VER.txt", + }) +} + func TestVndkApexUsesVendorVariant(t *testing.T) { bp := ` apex_vndk { @@ -90,6 +143,7 @@ func TestVndkApexUsesVendorVariant(t *testing.T) { system_shared_libs: [], stl: "none", notice: "custom_notice", + sdk_version: "current", } cc_library { name: "libprofile-clang-extras_ndk", @@ -98,6 +152,7 @@ func TestVndkApexUsesVendorVariant(t *testing.T) { system_shared_libs: [], stl: "none", notice: "custom_notice", + sdk_version: "current", } `, func(fs map[string][]byte, config android.Config) { config.TestProductVariables.Native_coverage = proptools.BoolPtr(true) diff --git a/cc/androidmk.go b/cc/androidmk.go index ef695b003..5438b149d 100644 --- a/cc/androidmk.go +++ b/cc/androidmk.go @@ -29,6 +29,7 @@ var ( vendorSuffix = ".vendor" ramdiskSuffix = ".ramdisk" recoverySuffix = ".recovery" + sdkSuffix = ".sdk" ) type AndroidMkContext interface { @@ -103,6 +104,28 @@ func (c *Module) AndroidMkEntries() []android.AndroidMkEntries { } } } + if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake { + // Make the SDK variant uninstallable so that there are not two rules to install + // to the same location. + entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true) + // Add the unsuffixed name to SOONG_SDK_VARIANT_MODULES so that Make can rewrite + // dependencies to the .sdk suffix when building a module that uses the SDK. + entries.SetString("SOONG_SDK_VARIANT_MODULES", + "$(SOONG_SDK_VARIANT_MODULES) $(patsubst %.sdk,%,$(LOCAL_MODULE))") + } + }, + }, + ExtraFooters: []android.AndroidMkExtraFootersFunc{ + func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) { + if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake && + c.CcLibraryInterface() && c.Shared() { + // Using the SDK variant as a JNI library needs a copy of the .so that + // is not named .sdk.so so that it can be packaged into the APK with + // the right name. + fmt.Fprintln(w, "$(eval $(call copy-one-file,", + "$(LOCAL_BUILT_MODULE),", + "$(patsubst %.sdk.so,%.so,$(LOCAL_BUILT_MODULE))))") + } }, }, } @@ -248,6 +271,10 @@ func (library *libraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries entries.SubName = "." + library.stubsVersion() } entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) { + // Note library.skipInstall() has a special case to get here for static + // libraries that otherwise would have skipped installation and hence not + // have executed AndroidMkEntries at all. The reason is to ensure they get + // a NOTICE file make target which other libraries might depend on. entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true) if library.buildStubs() { entries.SetBool("LOCAL_NO_NOTICE_FILE", true) @@ -393,6 +420,9 @@ func (library *toolchainLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, } func (installer *baseInstaller) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) { + if installer.path == (android.InstallPath{}) { + return + } // Soong installation is only supported for host modules. Have Make // installation trigger Soong installation. if ctx.Target().Os.Class == android.Host { diff --git a/cc/builder.go b/cc/builder.go index 136263b41..b583586f3 100644 --- a/cc/builder.go +++ b/cc/builder.go @@ -29,6 +29,7 @@ import ( "android/soong/android" "android/soong/cc/config" + "android/soong/remoteexec" ) const ( @@ -62,7 +63,7 @@ var ( }, "ccCmd", "cFlags") - ld = pctx.AndroidStaticRule("ld", + ld, ldRE = remoteexec.StaticRules(pctx, "ld", blueprint.RuleParams{ Command: "$ldCmd ${crtBegin} @${out}.rsp " + "${libFlags} ${crtEnd} -o ${out} ${ldFlags} ${extraLibFlags}", @@ -72,16 +73,28 @@ var ( // clang -Wl,--out-implib doesn't update its output file if it hasn't changed. Restat: true, }, - "ldCmd", "crtBegin", "libFlags", "crtEnd", "ldFlags", "extraLibFlags") - - partialLd = pctx.AndroidStaticRule("partialLd", + &remoteexec.REParams{Labels: map[string]string{"type": "link", "tool": "clang"}, + ExecStrategy: "${config.RECXXLinksExecStrategy}", + Inputs: []string{"${out}.rsp"}, + RSPFile: "${out}.rsp", + OutputFiles: []string{"${out}"}, + ToolchainInputs: []string{"$ldCmd"}, + Platform: map[string]string{remoteexec.PoolKey: "${config.RECXXLinksPool}"}, + }, []string{"ldCmd", "crtBegin", "libFlags", "crtEnd", "ldFlags", "extraLibFlags"}, nil) + + partialLd, partialLdRE = remoteexec.StaticRules(pctx, "partialLd", blueprint.RuleParams{ // Without -no-pie, clang 7.0 adds -pie to link Android files, // but -r and -pie cannot be used together. Command: "$ldCmd -fuse-ld=lld -nostdlib -no-pie -Wl,-r ${in} -o ${out} ${ldFlags}", CommandDeps: []string{"$ldCmd"}, - }, - "ldCmd", "ldFlags") + }, &remoteexec.REParams{ + Labels: map[string]string{"type": "link", "tool": "clang"}, + ExecStrategy: "${config.RECXXLinksExecStrategy}", Inputs: []string{"$inCommaList"}, + OutputFiles: []string{"${out}"}, + ToolchainInputs: []string{"$ldCmd"}, + Platform: map[string]string{remoteexec.PoolKey: "${config.RECXXLinksPool}"}, + }, []string{"ldCmd", "ldFlags"}, []string{"inCommaList"}) ar = pctx.AndroidStaticRule("ar", blueprint.RuleParams{ @@ -262,6 +275,7 @@ func init() { } pctx.HostBinToolVariable("SoongZipCmd", "soong_zip") + pctx.Import("android/soong/remoteexec") } type builderFlags struct { @@ -657,8 +671,13 @@ func TransformObjToDynamicBinary(ctx android.ModuleContext, deps = append(deps, crtBegin.Path(), crtEnd.Path()) } + rule := ld + if ctx.Config().IsEnvTrue("RBE_CXX_LINKS") { + rule = ldRE + } + ctx.Build(pctx, android.BuildParams{ - Rule: ld, + Rule: rule, Description: "link " + outputFile.Base(), Output: outputFile, ImplicitOutputs: implicitOutputs, @@ -798,16 +817,22 @@ func TransformObjsToObj(ctx android.ModuleContext, objFiles android.Paths, ldCmd := "${config.ClangBin}/clang++" + rule := partialLd + args := map[string]string{ + "ldCmd": ldCmd, + "ldFlags": flags.globalLdFlags + " " + flags.localLdFlags, + } + if ctx.Config().IsEnvTrue("RBE_CXX_LINKS") { + rule = partialLdRE + args["inCommaList"] = strings.Join(objFiles.Strings(), ",") + } ctx.Build(pctx, android.BuildParams{ - Rule: partialLd, + Rule: rule, Description: "link " + outputFile.Base(), Output: outputFile, Inputs: objFiles, Implicits: deps, - Args: map[string]string{ - "ldCmd": ldCmd, - "ldFlags": flags.globalLdFlags + " " + flags.localLdFlags, - }, + Args: args, }) } @@ -42,6 +42,7 @@ func RegisterCCBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("cc_defaults", defaultsFactory) ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) { + ctx.BottomUp("sdk", sdkMutator).Parallel() ctx.BottomUp("vndk", VndkMutator).Parallel() ctx.BottomUp("link", LinkageMutator).Parallel() ctx.BottomUp("ndk_api", NdkApiMutator).Parallel() @@ -208,9 +209,13 @@ type BaseProperties struct { // Deprecated. true is the default, false is invalid. Clang *bool `android:"arch_variant"` - // Minimum sdk version supported when compiling against the ndk + // Minimum sdk version supported when compiling against the ndk. Setting this property causes + // two variants to be built, one for the platform and one for apps. Sdk_version *string + // If true, always create an sdk variant and don't create a platform variant. + Sdk_variant_only *bool + AndroidMkSharedLibs []string `blueprint:"mutated"` AndroidMkStaticLibs []string `blueprint:"mutated"` AndroidMkRuntimeLibs []string `blueprint:"mutated"` @@ -252,6 +257,16 @@ type BaseProperties struct { SnapshotRuntimeLibs []string `blueprint:"mutated"` Installable *bool + + // Set by factories of module types that can only be referenced from variants compiled against + // the SDK. + AlwaysSdk bool `blueprint:"mutated"` + + // Variant is an SDK variant created by sdkMutator + IsSdkVariant bool `blueprint:"mutated"` + // Set when both SDK and platform variants are exported to Make to trigger renaming the SDK + // variant to have a ".sdk" suffix. + SdkAndPlatformVariantVisibleToMake bool `blueprint:"mutated"` } type VendorProperties struct { @@ -389,6 +404,7 @@ type installer interface { inSanitizerDir() bool hostToolPath() android.OptionalPath relativeInstallPath() string + skipInstall(mod *Module) } type xref interface { @@ -530,7 +546,10 @@ func (c *Module) Shared() bool { } func (c *Module) SelectedStl() string { - return c.stl.Properties.SelectedStl + if c.stl != nil { + return c.stl.Properties.SelectedStl + } + return "" } func (c *Module) ToolchainLibrary() bool { @@ -558,6 +577,10 @@ func (c *Module) SdkVersion() string { return String(c.Properties.Sdk_version) } +func (c *Module) AlwaysSdk() bool { + return c.Properties.AlwaysSdk || Bool(c.Properties.Sdk_variant_only) +} + func (c *Module) IncludeDirs() android.Paths { if c.linker != nil { if library, ok := c.linker.(exportedFlagsProducer); ok { @@ -586,13 +609,6 @@ func (c *Module) GetDepsInLinkOrder() []android.Path { return c.depsInLinkOrder } -func (c *Module) StubsSymbolFile() android.OptionalPath { - if library, ok := c.linker.(*libraryDecorator); ok { - return library.stubsSymbolFile - } - return android.OptionalPath{} -} - func (c *Module) StubsVersions() []string { if c.linker != nil { if library, ok := c.linker.(*libraryDecorator); ok { @@ -824,6 +840,17 @@ func (c *Module) UseVndk() bool { return c.Properties.VndkVersion != "" } +func (c *Module) canUseSdk() bool { + return c.Os() == android.Android && !c.UseVndk() && !c.InRamdisk() && !c.InRecovery() +} + +func (c *Module) UseSdk() bool { + if c.canUseSdk() { + return String(c.Properties.Sdk_version) != "" + } + return false +} + func (c *Module) isCoverageVariant() bool { return c.coverage.Properties.IsCoverageVariant } @@ -1081,21 +1108,18 @@ func (ctx *moduleContextImpl) header() bool { } func (ctx *moduleContextImpl) canUseSdk() bool { - return ctx.ctx.Device() && !ctx.useVndk() && !ctx.inRamdisk() && !ctx.inRecovery() && !ctx.ctx.Fuchsia() + return ctx.mod.canUseSdk() } func (ctx *moduleContextImpl) useSdk() bool { - if ctx.canUseSdk() { - return String(ctx.mod.Properties.Sdk_version) != "" - } - return false + return ctx.mod.UseSdk() } func (ctx *moduleContextImpl) sdkVersion() string { if ctx.ctx.Device() { if ctx.useVndk() { vndkVer := ctx.mod.VndkVersion() - if inList(vndkVer, ctx.ctx.Config().PlatformVersionCombinedCodenames()) { + if inList(vndkVer, ctx.ctx.Config().PlatformVersionActiveCodenames()) { return "current" } return vndkVer @@ -1407,6 +1431,8 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { c.Properties.SubName += ramdiskSuffix } else if c.InRecovery() && !c.OnlyInRecovery() { c.Properties.SubName += recoverySuffix + } else if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake { + c.Properties.SubName += sdkSuffix } ctx := &moduleContext{ @@ -2614,6 +2640,14 @@ func (c *Module) InstallInRecovery() bool { return c.InRecovery() } +func (c *Module) SkipInstall() { + if c.installer == nil { + c.ModuleBase.SkipInstall() + return + } + c.installer.skipInstall(c) +} + func (c *Module) HostToolPath() android.OptionalPath { if c.installer == nil { return android.OptionalPath{} diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go index 19aedd99d..5575baaab 100644 --- a/cc/config/arm64_device.go +++ b/cc/config/arm64_device.go @@ -37,10 +37,8 @@ var ( } arm64Ldflags = []string{ - "-Wl,-m,aarch64_elf64_le_vec", "-Wl,--hash-style=gnu", "-Wl,-z,separate-code", - "-fuse-ld=gold", "-Wl,--icf=safe", } diff --git a/cc/config/clang.go b/cc/config/clang.go index 274ccd5d5..bdd9030f4 100644 --- a/cc/config/clang.go +++ b/cc/config/clang.go @@ -80,10 +80,8 @@ var ClangUnknownCflags = sorted([]string{ // Ldflags that should be filtered out when linking with clang lld var ClangUnknownLldflags = sorted([]string{ - "-fuse-ld=gold", "-Wl,--fix-cortex-a8", "-Wl,--no-fix-cortex-a8", - "-Wl,-m,aarch64_elf64_le_vec", }) var ClangLibToolingUnknownCflags = sorted([]string{}) diff --git a/cc/config/global.go b/cc/config/global.go index 29020ab24..d85ac5f6a 100644 --- a/cc/config/global.go +++ b/cc/config/global.go @@ -18,6 +18,7 @@ import ( "strings" "android/soong/android" + "android/soong/remoteexec" ) var ( @@ -127,8 +128,8 @@ var ( // prebuilts/clang default settings. ClangDefaultBase = "prebuilts/clang/host" - ClangDefaultVersion = "clang-r377782c" - ClangDefaultShortVersion = "10.0.5" + ClangDefaultVersion = "clang-r377782d" + ClangDefaultShortVersion = "10.0.6" // Directories with warnings from Android.bp files. WarningAllowedProjects = []string{ @@ -255,6 +256,9 @@ func init() { } return "" }) + + pctx.VariableFunc("RECXXLinksPool", envOverrideFunc("RBE_CXX_LINKS_POOL", remoteexec.DefaultPool)) + pctx.VariableFunc("RECXXLinksExecStrategy", envOverrideFunc("RBE_CXX_LINKS_EXEC_STRATEGY", remoteexec.LocalExecStrategy)) } var HostPrebuiltTag = pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS) @@ -268,3 +272,12 @@ func bionicHeaders(kernelArch string) string { "-isystem bionic/libc/kernel/android/uapi", }, " ") } + +func envOverrideFunc(envVar, defaultVal string) func(ctx android.PackageVarContext) string { + return func(ctx android.PackageVarContext) string { + if override := ctx.Config().Getenv(envVar); override != "" { + return override + } + return defaultVal + } +} diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go index 43e8c85e8..cd0a50837 100644 --- a/cc/config/x86_windows_host.go +++ b/cc/config/x86_windows_host.go @@ -58,8 +58,11 @@ var ( "-Wl,--dynamicbase", "-Wl,--nxcompat", } + windowsLldflags = []string{ + "-Wl,--Xlink=-Brepro", // Enable deterministic build + } windowsClangLdflags = append(ClangFilterUnknownCflags(windowsLdflags), []string{}...) - windowsClangLldflags = ClangFilterUnknownLldflags(windowsClangLdflags) + windowsClangLldflags = append(ClangFilterUnknownLldflags(windowsClangLdflags), windowsLldflags...) windowsX86Cflags = []string{ "-m32", diff --git a/cc/genrule.go b/cc/genrule.go index 155e410dc..9331448b8 100644 --- a/cc/genrule.go +++ b/cc/genrule.go @@ -27,6 +27,7 @@ type GenruleExtraProperties struct { Vendor_available *bool Ramdisk_available *bool Recovery_available *bool + Sdk_version *string } // cc_genrule is a genrule that can depend on other cc_* objects. diff --git a/cc/installer.go b/cc/installer.go index 200d59e9f..0b4a68cc8 100644 --- a/cc/installer.go +++ b/cc/installer.go @@ -106,3 +106,7 @@ func (installer *baseInstaller) hostToolPath() android.OptionalPath { func (installer *baseInstaller) relativeInstallPath() string { return String(installer.Properties.Relative_install_path) } + +func (installer *baseInstaller) skipInstall(mod *Module) { + mod.ModuleBase.SkipInstall() +} diff --git a/cc/library.go b/cc/library.go index 26e86f940..94fffb951 100644 --- a/cc/library.go +++ b/cc/library.go @@ -367,9 +367,6 @@ type libraryDecorator struct { // Location of the file that should be copied to dist dir when requested distFile android.OptionalPath - // stubs.symbol_file - stubsSymbolFile android.OptionalPath - versionScriptPath android.ModuleGenPath post_install_cmds []string @@ -606,7 +603,6 @@ func (library *libraryDecorator) shouldCreateSourceAbiDump(ctx ModuleContext) bo func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects { if library.buildStubs() { - library.stubsSymbolFile = android.OptionalPathForModuleSrc(ctx, library.Properties.Stubs.Symbol_file) objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Stubs.Symbol_file), library.MutatedProperties.StubsVersion, "--apex") library.versionScriptPath = versionScript return objs @@ -1241,7 +1237,7 @@ func (library *libraryDecorator) install(ctx ModuleContext, file android.Path) { if Bool(library.Properties.Static_ndk_lib) && library.static() && !ctx.useVndk() && !ctx.inRamdisk() && !ctx.inRecovery() && ctx.Device() && library.baseLinker.sanitize.isUnsanitizedVariant() && - !library.buildStubs() { + !library.buildStubs() && ctx.sdkVersion() == "" { installPath := getNdkSysrootBase(ctx).Join( ctx, "usr/lib", config.NDKTriple(ctx.toolchain()), file.Base()) @@ -1333,6 +1329,18 @@ func (library *libraryDecorator) availableFor(what string) bool { return android.CheckAvailableForApex(what, list) } +func (library *libraryDecorator) skipInstall(mod *Module) { + if library.static() && library.buildStatic() && !library.buildStubs() { + // If we're asked to skip installation of a static library (in particular + // when it's not //apex_available:platform) we still want an AndroidMk entry + // for it to ensure we get the relevant NOTICE file targets (cf. + // notice_files.mk) that other libraries might depend on. AndroidMkEntries + // always sets LOCAL_UNINSTALLABLE_MODULE for these entries. + return + } + mod.ModuleBase.SkipInstall() +} + var versioningMacroNamesListKey = android.NewOnceKey("versioningMacroNamesList") func versioningMacroNamesList(config android.Config) *map[string]string { diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go index 4e4df5b0e..754b96a98 100644 --- a/cc/library_sdk_member.go +++ b/cc/library_sdk_member.go @@ -271,10 +271,7 @@ func addPossiblyArchSpecificProperties(sdkModuleContext android.ModuleContext, b } if len(libInfo.StubsVersion) > 0 { - symbolFilePath := filepath.Join(nativeEtcDir, libInfo.StubsSymbolFile.Path().Base()) - builder.CopyToSnapshot(libInfo.StubsSymbolFile.Path(), symbolFilePath) stubsSet := outputProperties.AddPropertySet("stubs") - stubsSet.AddProperty("symbol_file", symbolFilePath) stubsSet.AddProperty("versions", []string{libInfo.StubsVersion}) } } @@ -283,7 +280,6 @@ const ( nativeIncludeDir = "include" nativeGeneratedIncludeDir = "include_gen" nativeStubDir = "lib" - nativeEtcDir = "etc" ) // path to the native library. Relative to <sdk_root>/<api_dir> @@ -348,9 +344,6 @@ type nativeLibInfoProperties struct { // are not in use. StubsVersion string - // The stubs symbol file. - StubsSymbolFile android.OptionalPath - // outputFile is not exported as it is always arch specific. outputFile android.Path } @@ -389,7 +382,6 @@ func (p *nativeLibInfoProperties) PopulateFromVariant(ctx android.SdkMemberConte if ccModule.HasStubsVariants() { p.StubsVersion = ccModule.StubsVersion() - p.StubsSymbolFile = ccModule.StubsSymbolFile() } } diff --git a/cc/linkable.go b/cc/linkable.go index 80cd6b8a2..fbe61a4f4 100644 --- a/cc/linkable.go +++ b/cc/linkable.go @@ -45,12 +45,14 @@ type LinkableInterface interface { InRecovery() bool OnlyInRecovery() bool + UseSdk() bool UseVndk() bool MustUseVendorVariant() bool IsVndk() bool HasVendorVariant() bool SdkVersion() string + AlwaysSdk() bool ToolchainLibrary() bool NdkPrebuiltStl() bool diff --git a/cc/linker.go b/cc/linker.go index f65d7c8d3..57a0c016a 100644 --- a/cc/linker.go +++ b/cc/linker.go @@ -158,6 +158,13 @@ type BaseLinkerProperties struct { // the ramdisk variant of the C/C++ module. Exclude_static_libs []string } + Platform struct { + // list of shared libs that should be use to build the platform variant + // of a module that sets sdk_version. This should rarely be necessary, + // in most cases the same libraries are available for the SDK and platform + // variants. + Shared_libs []string + } } // make android::build:GetBuildNumber() available containing the build ID. @@ -255,6 +262,10 @@ func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs) } + if !ctx.useSdk() { + deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Target.Platform.Shared_libs...) + } + if ctx.toolchain().Bionic() { // libclang_rt.builtins and libatomic have to be last on the command line if !Bool(linker.Properties.No_libcrt) { diff --git a/cc/llndk_library.go b/cc/llndk_library.go index 09050aa34..7ff20f472 100644 --- a/cc/llndk_library.go +++ b/cc/llndk_library.go @@ -83,7 +83,7 @@ func (stub *llndkStubDecorator) compilerFlags(ctx ModuleContext, flags Flags, de func (stub *llndkStubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects { vndkVer := ctx.Module().(*Module).VndkVersion() - if !inList(vndkVer, ctx.Config().PlatformVersionCombinedCodenames()) || vndkVer == "" { + if !inList(vndkVer, ctx.Config().PlatformVersionActiveCodenames()) || vndkVer == "" { // For non-enforcing devices, vndkVer is empty. Use "current" in that case, too. vndkVer = "current" } diff --git a/cc/ndk_library.go b/cc/ndk_library.go index 2a86d3362..68d4ac067 100644 --- a/cc/ndk_library.go +++ b/cc/ndk_library.go @@ -381,6 +381,9 @@ func newStubLibrary() *Module { module.linker = stub module.installer = stub + module.Properties.AlwaysSdk = true + module.Properties.Sdk_version = StringPtr("current") + module.AddProperties(&stub.properties, &library.MutatedProperties) return module diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go index 3a630d2c0..c4d770837 100644 --- a/cc/ndk_prebuilt.go +++ b/cc/ndk_prebuilt.go @@ -76,6 +76,8 @@ func NdkPrebuiltObjectFactory() android.Module { baseLinker: NewBaseLinker(nil), }, } + module.Properties.AlwaysSdk = true + module.Properties.Sdk_version = StringPtr("current") module.Properties.HideFromMake = true return module.Init() } @@ -125,10 +127,9 @@ func NdkPrebuiltSharedStlFactory() android.Module { libraryDecorator: library, } module.installer = nil - minVersionString := "minimum" - noStlString := "none" - module.Properties.Sdk_version = &minVersionString - module.stl.Properties.Stl = &noStlString + module.Properties.Sdk_version = StringPtr("minimum") + module.Properties.AlwaysSdk = true + module.stl.Properties.Stl = StringPtr("none") return module.Init() } @@ -145,6 +146,9 @@ func NdkPrebuiltStaticStlFactory() android.Module { } module.installer = nil module.Properties.HideFromMake = true + module.Properties.AlwaysSdk = true + module.Properties.Sdk_version = StringPtr("current") + module.stl.Properties.Stl = StringPtr("none") module.ModuleBase.EnableNativeBridgeSupportByDefault() return module.Init() } diff --git a/cc/prebuilt.go b/cc/prebuilt.go index fc9cc17d3..ac990f35f 100644 --- a/cc/prebuilt.go +++ b/cc/prebuilt.go @@ -43,6 +43,11 @@ type prebuiltLinkerProperties struct { // Check the prebuilt ELF files (e.g. DT_SONAME, DT_NEEDED, resolution of undefined // symbols, etc), default true. Check_elf_files *bool + + // Optionally provide an import library if this is a Windows PE DLL prebuilt. + // This is needed only if this library is linked by other modules in build time. + // Only makes sense for the Windows target. + Windows_import_lib *string `android:"path,arch_variant"` } type prebuiltLinker struct { @@ -109,9 +114,16 @@ func (p *prebuiltLibraryLinker) link(ctx ModuleContext, in := android.PathForModuleSrc(ctx, srcs[0]) + if p.static() { + return in + } + if p.shared() { p.unstrippedOutputFile = in libName := p.libraryDecorator.getLibName(ctx) + flags.Toolchain.ShlibSuffix() + outputFile := android.PathForModuleOut(ctx, libName) + var implicits android.Paths + if p.needsStrip(ctx) { stripped := android.PathForModuleOut(ctx, "stripped", libName) p.stripExecutableOrSharedLib(ctx, in, stripped, builderFlags) @@ -122,10 +134,41 @@ func (p *prebuiltLibraryLinker) link(ctx ModuleContext, // depending on a table of contents file instead of the library itself. tocFile := android.PathForModuleOut(ctx, libName+".toc") p.tocFile = android.OptionalPathForPath(tocFile) - TransformSharedObjectToToc(ctx, in, tocFile, builderFlags) - } + TransformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags) + + if ctx.Windows() && p.properties.Windows_import_lib != nil { + // Consumers of this library actually links to the import library in build + // time and dynamically links to the DLL in run time. i.e. + // a.exe <-- static link --> foo.lib <-- dynamic link --> foo.dll + importLibSrc := android.PathForModuleSrc(ctx, String(p.properties.Windows_import_lib)) + importLibName := p.libraryDecorator.getLibName(ctx) + ".lib" + importLibOutputFile := android.PathForModuleOut(ctx, importLibName) + implicits = append(implicits, importLibOutputFile) + + ctx.Build(pctx, android.BuildParams{ + Rule: android.Cp, + Description: "prebuilt import library", + Input: importLibSrc, + Output: importLibOutputFile, + Args: map[string]string{ + "cpFlags": "-L", + }, + }) + } - return in + ctx.Build(pctx, android.BuildParams{ + Rule: android.Cp, + Description: "prebuilt shared library", + Implicits: implicits, + Input: in, + Output: outputFile, + Args: map[string]string{ + "cpFlags": "-L", + }, + }) + + return outputFile + } } return nil @@ -155,6 +198,10 @@ func (p *prebuiltLibraryLinker) disablePrebuilt() { p.properties.Srcs = nil } +func (p *prebuiltLibraryLinker) skipInstall(mod *Module) { + mod.ModuleBase.SkipInstall() +} + func NewPrebuiltLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) { module, library := NewLibrary(hod) module.compiler = nil @@ -163,6 +210,7 @@ func NewPrebuiltLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDec libraryDecorator: library, } module.linker = prebuilt + module.installer = prebuilt module.AddProperties(&prebuilt.properties) diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go index 0b018c10d..adb44bd71 100644 --- a/cc/prebuilt_test.go +++ b/cc/prebuilt_test.go @@ -184,7 +184,7 @@ func TestPrebuiltLibraryShared(t *testing.T) { }) shared := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_shared").Module().(*Module) - assertString(t, shared.OutputFile().String(), "libf.so") + assertString(t, shared.OutputFile().Path().Base(), "libtest.so") } func TestPrebuiltLibraryStatic(t *testing.T) { @@ -198,7 +198,7 @@ func TestPrebuiltLibraryStatic(t *testing.T) { }) static := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_static").Module().(*Module) - assertString(t, static.OutputFile().String(), "libf.a") + assertString(t, static.OutputFile().Path().Base(), "libf.a") } func TestPrebuiltLibrary(t *testing.T) { @@ -221,8 +221,53 @@ func TestPrebuiltLibrary(t *testing.T) { }) shared := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_shared").Module().(*Module) - assertString(t, shared.OutputFile().String(), "libf.so") + assertString(t, shared.OutputFile().Path().Base(), "libtest.so") static := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_static").Module().(*Module) - assertString(t, static.OutputFile().String(), "libf.a") + assertString(t, static.OutputFile().Path().Base(), "libf.a") +} + +func TestPrebuiltLibraryStem(t *testing.T) { + ctx := testPrebuilt(t, ` + cc_prebuilt_library { + name: "libfoo", + stem: "libbar", + static: { + srcs: ["libfoo.a"], + }, + shared: { + srcs: ["libfoo.so"], + }, + strip: { + none: true, + }, + } + `, map[string][]byte{ + "libfoo.a": nil, + "libfoo.so": nil, + }) + + static := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Module().(*Module) + assertString(t, static.OutputFile().Path().Base(), "libfoo.a") + + shared := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module().(*Module) + assertString(t, shared.OutputFile().Path().Base(), "libbar.so") +} + +func TestPrebuiltLibrarySharedStem(t *testing.T) { + ctx := testPrebuilt(t, ` + cc_prebuilt_library_shared { + name: "libfoo", + stem: "libbar", + srcs: ["libfoo.so"], + strip: { + none: true, + }, + } + `, map[string][]byte{ + "libfoo.so": nil, + }) + + shared := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module().(*Module) + assertString(t, shared.OutputFile().Path().Base(), "libbar.so") } diff --git a/cc/sdk.go b/cc/sdk.go new file mode 100644 index 000000000..d05a04a12 --- /dev/null +++ b/cc/sdk.go @@ -0,0 +1,65 @@ +// Copyright 2020 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 cc + +import ( + "android/soong/android" + "android/soong/genrule" +) + +// sdkMutator sets a creates a platform and an SDK variant for modules +// that set sdk_version, and ignores sdk_version for the platform +// variant. The SDK variant will be used for embedding in APKs +// that may be installed on older platforms. Apexes use their own +// variants that enforce backwards compatibility. +func sdkMutator(ctx android.BottomUpMutatorContext) { + if ctx.Os() != android.Android { + return + } + + switch m := ctx.Module().(type) { + case LinkableInterface: + if m.AlwaysSdk() { + if !m.UseSdk() { + ctx.ModuleErrorf("UseSdk() must return true when AlwaysSdk is set, did the factory forget to set Sdk_version?") + } + ctx.CreateVariations("sdk") + } else if m.UseSdk() { + modules := ctx.CreateVariations("", "sdk") + modules[0].(*Module).Properties.Sdk_version = nil + modules[1].(*Module).Properties.IsSdkVariant = true + + if ctx.Config().UnbundledBuild() { + modules[0].(*Module).Properties.HideFromMake = true + } else { + modules[1].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true + modules[1].(*Module).Properties.PreventInstall = true + } + ctx.AliasVariation("") + } else { + ctx.CreateVariations("") + ctx.AliasVariation("") + } + case *genrule.Module: + if p, ok := m.Extra.(*GenruleExtraProperties); ok { + if String(p.Sdk_version) != "" { + ctx.CreateVariations("", "sdk") + } else { + ctx.CreateVariations("") + } + ctx.AliasVariation("") + } + } +} diff --git a/cc/sdk_test.go b/cc/sdk_test.go new file mode 100644 index 000000000..5a3c181dc --- /dev/null +++ b/cc/sdk_test.go @@ -0,0 +1,102 @@ +// Copyright 2020 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 cc + +import ( + "testing" + + "android/soong/android" +) + +func TestSdkMutator(t *testing.T) { + bp := ` + cc_library { + name: "libsdk", + shared_libs: ["libsdkdep"], + sdk_version: "current", + stl: "c++_shared", + } + + cc_library { + name: "libsdkdep", + sdk_version: "current", + stl: "c++_shared", + } + + cc_library { + name: "libplatform", + shared_libs: ["libsdk"], + stl: "libc++", + } + + cc_binary { + name: "platformbinary", + shared_libs: ["libplatform"], + stl: "libc++", + } + + cc_binary { + name: "sdkbinary", + shared_libs: ["libsdk"], + sdk_version: "current", + stl: "libc++", + } + ` + + assertDep := func(t *testing.T, from, to android.TestingModule) { + t.Helper() + found := false + + var toFile android.Path + m := to.Module().(*Module) + if toc := m.Toc(); toc.Valid() { + toFile = toc.Path() + } else { + toFile = m.outputFile.Path() + } + + rule := from.Description("link") + for _, dep := range rule.Implicits { + if dep.String() == toFile.String() { + found = true + } + } + if !found { + t.Errorf("expected %q in %q", toFile.String(), rule.Implicits.Strings()) + } + } + + ctx := testCc(t, bp) + + libsdkNDK := ctx.ModuleForTests("libsdk", "android_arm64_armv8-a_sdk_shared") + libsdkPlatform := ctx.ModuleForTests("libsdk", "android_arm64_armv8-a_shared") + libsdkdepNDK := ctx.ModuleForTests("libsdkdep", "android_arm64_armv8-a_sdk_shared") + libsdkdepPlatform := ctx.ModuleForTests("libsdkdep", "android_arm64_armv8-a_shared") + libplatform := ctx.ModuleForTests("libplatform", "android_arm64_armv8-a_shared") + platformbinary := ctx.ModuleForTests("platformbinary", "android_arm64_armv8-a") + sdkbinary := ctx.ModuleForTests("sdkbinary", "android_arm64_armv8-a_sdk") + + libcxxNDK := ctx.ModuleForTests("ndk_libc++_shared", "android_arm64_armv8-a_sdk_shared") + libcxxPlatform := ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared") + + assertDep(t, libsdkNDK, libsdkdepNDK) + assertDep(t, libsdkPlatform, libsdkdepPlatform) + assertDep(t, libplatform, libsdkPlatform) + assertDep(t, platformbinary, libplatform) + assertDep(t, sdkbinary, libsdkNDK) + + assertDep(t, libsdkNDK, libcxxNDK) + assertDep(t, libsdkPlatform, libcxxPlatform) +} @@ -115,9 +115,13 @@ func (stl *stl) begin(ctx BaseModuleContext) { switch s { case "libc++", "libc++_static": return s + case "c++_shared": + return "libc++" + case "c++_static": + return "libc++_static" case "none": return "" - case "": + case "", "system": if ctx.static() { return "libc++_static" } else { diff --git a/cc/testing.go b/cc/testing.go index f85795b33..53f09955a 100644 --- a/cc/testing.go +++ b/cc/testing.go @@ -152,6 +152,7 @@ func GatherRequiredDepsForTest(oses ...android.OsType) string { name: "libgcc_stripped", vendor_available: true, recovery_available: true, + sdk_version: "current", src: "", } @@ -169,6 +170,7 @@ func GatherRequiredDepsForTest(oses ...android.OsType) string { llndk_library { name: "libc", symbol_file: "", + sdk_version: "current", } cc_library { name: "libm", @@ -188,6 +190,7 @@ func GatherRequiredDepsForTest(oses ...android.OsType) string { llndk_library { name: "libm", symbol_file: "", + sdk_version: "current", } cc_library { name: "libdl", @@ -207,6 +210,7 @@ func GatherRequiredDepsForTest(oses ...android.OsType) string { llndk_library { name: "libdl", symbol_file: "", + sdk_version: "current", } cc_library { name: "libft2", @@ -219,6 +223,7 @@ func GatherRequiredDepsForTest(oses ...android.OsType) string { name: "libft2", symbol_file: "", vendor_available: false, + sdk_version: "current", } cc_library { name: "libc++_static", @@ -375,6 +380,16 @@ func GatherRequiredDepsForTest(oses ...android.OsType) string { sdk_version: "27", } + ndk_prebuilt_object { + name: "ndk_crtbegin_dynamic.27", + sdk_version: "27", + } + + ndk_prebuilt_object { + name: "ndk_crtend_android.27", + sdk_version: "27", + } + ndk_prebuilt_shared_stl { name: "ndk_libc++_shared", } diff --git a/cc/tidy.go b/cc/tidy.go index 545539232..cfb5b6886 100644 --- a/cc/tidy.go +++ b/cc/tidy.go @@ -117,6 +117,10 @@ func (tidy *tidyFeature) flags(ctx ModuleContext, flags Flags) Flags { // which is used in many Android files. tidyChecks = tidyChecks + ",-cert-dcl16-c" } + // https://b.corp.google.com/issues/153464409 + // many local projects enable cert-* checks, which + // trigger bugprone-reserved-identifier. + tidyChecks = tidyChecks + ",-bugprone-reserved-identifier*,-cert-dcl51-cpp,-cert-dcl37-c" flags.TidyFlags = append(flags.TidyFlags, tidyChecks) if len(tidy.Properties.Tidy_checks_as_errors) > 0 { diff --git a/cc/toolchain_library.go b/cc/toolchain_library.go index dfc6f7679..042e012d9 100644 --- a/cc/toolchain_library.go +++ b/cc/toolchain_library.go @@ -67,6 +67,7 @@ func ToolchainLibraryFactory() android.Module { module.stl = nil module.sanitize = nil module.installer = nil + module.Properties.Sdk_version = StringPtr("current") return module.Init() } diff --git a/cc/vndk.go b/cc/vndk.go index 4888dcf07..dbe1f3b5b 100644 --- a/cc/vndk.go +++ b/cc/vndk.go @@ -309,6 +309,10 @@ func processVndkLibrary(mctx android.BottomUpMutatorContext, m *Module) { panic(err) } + if m.HasStubsVariants() { + mctx.PropertyErrorf("vndk.enabled", "This library provides stubs. Shouldn't be VNDK. Consider making it as LLNDK") + } + vndkLibrariesLock.Lock() defer vndkLibrariesLock.Unlock() @@ -350,6 +354,15 @@ func IsForVndkApex(mctx android.BottomUpMutatorContext, m *Module) bool { } if lib, ok := m.linker.(libraryInterface); ok { + // VNDK APEX for VNDK-Lite devices will have VNDK-SP libraries from core variants + if mctx.DeviceConfig().VndkVersion() == "" { + // b/73296261: filter out libz.so because it is considered as LLNDK for VNDK-lite devices + if mctx.ModuleName() == "libz" { + return false + } + return m.ImageVariation().Variation == android.CoreVariation && lib.shared() && m.isVndkSp() + } + useCoreVariant := m.VndkVersion() == mctx.DeviceConfig().PlatformVndkVersion() && mctx.DeviceConfig().VndkUseCoreVariant() && !m.MustUseVendorVariant() return lib.shared() && m.inVendor() && m.IsVndk() && !m.isVndkExt() && !useCoreVariant diff --git a/cmd/zipsync/zipsync.go b/cmd/zipsync/zipsync.go index a6023d3bf..294e5ef0a 100644 --- a/cmd/zipsync/zipsync.go +++ b/cmd/zipsync/zipsync.go @@ -115,7 +115,7 @@ func main() { filename := filepath.Join(*outputDir, name) if f.FileInfo().IsDir() { - must(os.MkdirAll(filename, f.FileInfo().Mode())) + must(os.MkdirAll(filename, 0777)) } else { must(os.MkdirAll(filepath.Dir(filename), 0777)) in, err := f.Open() diff --git a/docs/map_files.md b/docs/map_files.md new file mode 100644 index 000000000..9fc0d14d6 --- /dev/null +++ b/docs/map_files.md @@ -0,0 +1,174 @@ +# Native API Map Files + +Native APIs such as those exposed by the NDK, LL-NDK, or APEX are described by +map.txt files. These files are [linker version scripts] with comments that are +semantically meaningful to [gen_stub_libs.py]. For an example of a map file, see +[libc.map.txt]. + +[gen_stub_libs.py]: https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/gen_stub_libs.py +[libc.map.txt]: https://cs.android.com/android/platform/superproject/+/master:bionic/libc/libc.map.txt +[linker version scripts]: https://www.gnu.org/software/gnulib/manual/html_node/LD-Version-Scripts.html + +## Basic format + +A linker version script defines at least one alphanumeric "version" definition, +each of which contain a list of symbols. For example: + +```txt +MY_API_R { # introduced=R + global: + api_foo; + api_bar; + local: + *; +}; + +MY_API_S { # introduced=S + global: + api_baz; +} MY_API_R; +``` + +Comments on the same line as either a version definition or a symbol name have +meaning. If you need to add any comments that should not be interpreted by the +stub generator, keep them on their own line. For a list of supported comments, +see the "Tags" section. + +Here, `api_foo` and `api_bar` are exposed in the generated stubs with the +`MY_API_R` version and `api_baz` is exposed with the `MY_API_S` version. No +other symbols are defined as public by this API. `MY_API_S` inherits all symbols +defined by `MY_API_R`. + +When generating NDK API stubs from this version script, the stub library for R +will define `api_foo` and `api_bar`. The stub library for S will define all +three APIs. + +Note that, with few exceptions (see "Special version names" below), the name of +the version has no inherent meaning. + +These map files can (and should) also be used as version scripts for building +the implementation library rather than just defining the stub interface by using +the `version_script` property of `cc_library`. This has the effect of limiting +symbol visibility of the library to expose only the interface named by the map +file. Without this, APIs that you have not explicitly exposed will still be +available to users via `dlsym`. Note: All comments are ignored in this case. Any +symbol named in any `global:` group will be visible. + +## Special version names + +Version names that end with `_PRIVATE` or `_PLATFORM` will not be exposed in any +stubs, but will be exposed in the implementation library. Using either of these +naming schemes is equivalent to marking the version with the `platform-only` +tag. See the docs for `platform-only` for more information. + +## Tags + +Comments on the same line as a version definition or a symbol name are +interpreted by the stub generator. Multiple space-delimited tags may be used on +the same line. The supported tags are: + +### apex + +Indicates that the version or symbol is to be exposed in the APEX stubs rather +than the NDK. May be used in combination with `llndk` if the symbol is exposed +to both APEX and the LL-NDK. + +### future + +Indicates that the version or symbol is first introduced in the "future" API +level. This is an abitrarily high API level used to define APIs that have not +yet been added to a specific release. + +### introduced + +Indicates the version in which an API was first introduced. For example, +`introduced=21` specifies that the API was first added (or first made public) in +API level 21. This tag can be applied to either a version definition or an +individual symbol. If applied to a version, all symbols contained in the version +will have the tag applied. An `introduced` tag on a symbol overrides the value +set for the version, if both are defined. + +Note: The map file alone does not contain all the information needed to +determine which API level an API was added in. The `first_version` property of +`ndk_library` will dictate which API levels stubs are generated for. If the +module sets `first_version: "21"`, no symbols were introduced before API 21. + +Codenames can (and typically should) be used when defining new APIs. This allows +the actual number of the API level to remain vague during development of that +release. For example, `introduced=S` can be used to define APIs added in S. Any +code name known to the build system can be used. For a list of versions known to +the build system, see `out/soong/api_levels.json` (if not present, run `m +out/soong/api_levels.json` to generate it). + +Architecture-specific variants of this tag exist: + +* `introduced-arm=VERSION` +* `introduced-arm64=VERSION` +* `introduced-x86=VERSION` +* `introduced-x86_64=VERSION` + +The architecture-specific tag will take precedence over the architecture-generic +tag when generating stubs for that architecture if both are present. If the +symbol is defined with only architecture-specific tags, it will not be present +for architectures that are not named. + +Note: The architecture-specific tags should, in general, not be used. These are +primarily needed for APIs that were wrongly inconsistently exposed by libc/libm +in old versions of Android before the stubs were well maintained. Think hard +before using an architecture-specific tag for a new API. + +### llndk + +Indicates that the version or symbol is to be exposed in the LL-NDK stubs rather +than the NDK. May be used in combination with `apex` if the symbol is exposed to +both APEX and the LL-NDK. + +### platform-only + +Indicates that the version or symbol is public in the implementation library but +should not be exposed in the stub library. Developers can still access them via +`dlsym`, but they will not be exposed in the stubs so it should at least be +clear to the developer that they are up to no good. + +The typical use for this tag is for exposing an API to the platform that is not +for use by the NDK, LL-NDK, or APEX. It is preferable to keep such APIs in an +entirely separate library to protect them from access via `dlsym`, but this is +not always possible. + +### var + +Used to define a public global variable. By default all symbols are exposed as +functions. In the uncommon situation of exposing a global variable, the `var` +tag may be used. + +### versioned=VERSION + +Behaves similarly to `introduced` but defines the first version that the stub +library should apply symbol versioning. For example: + +```txt +R { # introduced=R + global: + foo; + bar; # versioned=S + local: + *; +}; +``` + +The stub library for R will contain symbols for both `foo` and `bar`, but only +`foo` will include a versioned symbol `foo@R`. The stub library for S will +contain both symbols, as well as the versioned symbols `foo@R` and `bar@R`. + +This tag is not commonly needed and is only used to hide symbol versioning +mistakes that shipped as part of the platform. + +Note: Like `introduced`, the map file does not tell the whole story. The +`ndk_library` Soong module may define a `unversioned_until` property that sets +the default for the entire map file. + +### weak + +Indicates that the symbol should be [weak] in the stub library. + +[weak]: https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Function-Attributes.html diff --git a/java/androidmk.go b/java/androidmk.go index 136bb36ca..d37eac840 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -545,10 +545,21 @@ func (ddoc *Droiddoc) AndroidMkEntries() []android.AndroidMkEntries { } func (dstubs *Droidstubs) AndroidMkEntries() []android.AndroidMkEntries { + // If the stubsSrcJar is not generated (because generate_stubs is false) then + // use the api file as the output file to ensure the relevant phony targets + // are created in make if only the api txt file is being generated. This is + // needed because an invalid output file would prevent the make entries from + // being written. + // TODO(b/146727827): Revert when we do not need to generate stubs and API separately. + distFile := android.OptionalPathForPath(dstubs.apiFile) + outputFile := android.OptionalPathForPath(dstubs.stubsSrcJar) + if !outputFile.Valid() { + outputFile = distFile + } return []android.AndroidMkEntries{android.AndroidMkEntries{ Class: "JAVA_LIBRARIES", - DistFile: android.OptionalPathForPath(dstubs.apiFile), - OutputFile: android.OptionalPathForPath(dstubs.stubsSrcJar), + DistFile: distFile, + OutputFile: outputFile, Include: "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk", ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(entries *android.AndroidMkEntries) { diff --git a/java/app.go b/java/app.go index 449558033..7d509eea1 100755 --- a/java/app.go +++ b/java/app.go @@ -110,6 +110,10 @@ type appProperties struct { PreventInstall bool `blueprint:"mutated"` HideFromMake bool `blueprint:"mutated"` IsCoverageVariant bool `blueprint:"mutated"` + + // Whether this app is considered mainline updatable or not. When set to true, this will enforce + // additional rules for making sure that the APK is truly updatable. Default is false. + Updatable *bool } // android_app properties that can be overridden by override_android_app @@ -214,6 +218,13 @@ func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) { for _, jniTarget := range ctx.MultiTargets() { variation := append(jniTarget.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"}) + + // If the app builds against an Android SDK use the SDK variant of JNI dependencies + // unless jni_uses_platform_apis is set. + if a.sdkVersion().specified() && a.sdkVersion().kind != sdkCorePlatform && + !Bool(a.appProperties.Jni_uses_platform_apis) { + variation = append(variation, blueprint.Variation{Mutator: "sdk", Variation: "sdk"}) + } ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...) } @@ -242,11 +253,21 @@ func (a *AndroidTestHelperApp) GenerateAndroidBuildActions(ctx android.ModuleCon } func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) { - a.checkPlatformAPI(ctx) - a.checkSdkVersion(ctx) + a.checkAppSdkVersions(ctx) a.generateAndroidBuildActions(ctx) } +func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) { + if Bool(a.appProperties.Updatable) { + if !a.sdkVersion().stable() { + ctx.PropertyErrorf("sdk_version", "Updatable apps must use stable SDKs, found %v", a.sdkVersion()) + } + } + + a.checkPlatformAPI(ctx) + a.checkSdkVersions(ctx) +} + // Returns true if the native libraries should be stored in the APK uncompressed and the // extractNativeLibs application flag should be set to false in the manifest. func (a *AndroidApp) useEmbeddedNativeLibs(ctx android.ModuleContext) bool { @@ -385,7 +406,18 @@ func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext TransformJniLibsToJar(ctx, jniJarFile, jniLibs, a.useEmbeddedNativeLibs(ctx)) for _, jni := range jniLibs { if jni.coverageFile.Valid() { - a.jniCoverageOutputs = append(a.jniCoverageOutputs, jni.coverageFile.Path()) + // Only collect coverage for the first target arch if this is a multilib target. + // TODO(jungjw): Ideally, we want to collect both reports, but that would cause coverage + // data file path collisions since the current coverage file path format doesn't contain + // arch-related strings. This is fine for now though; the code coverage team doesn't use + // multi-arch targets such as test_suite_* for coverage collections yet. + // + // Work with the team to come up with a new format that handles multilib modules properly + // and change this. + if len(ctx.Config().Targets[android.Android]) == 1 || + ctx.Config().Targets[android.Android][0].Arch.ArchType == jni.target.Arch.ArchType { + a.jniCoverageOutputs = append(a.jniCoverageOutputs, jni.coverageFile.Path()) + } } } } else { @@ -1360,6 +1392,12 @@ type RuntimeResourceOverlayProperties struct { // if not blank, set the minimum version of the sdk that the compiled artifacts will run against. // Defaults to sdk_version if not set. Min_sdk_version *string + + // list of android_library modules whose resources are extracted and linked against statically + Static_libs []string + + // list of android_app modules whose resources are extracted and linked against + Resource_libs []string } func (r *RuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) { @@ -1372,6 +1410,9 @@ func (r *RuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) if cert != "" { ctx.AddDependency(ctx.Module(), certificateTag, cert) } + + ctx.AddVariationDependencies(nil, staticLibTag, r.properties.Static_libs...) + ctx.AddVariationDependencies(nil, libTag, r.properties.Resource_libs...) } func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleContext) { diff --git a/java/app_test.go b/java/app_test.go index f3cd07096..7b04e4629 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -264,6 +264,108 @@ func TestAndroidAppLinkType(t *testing.T) { `) } +func TestUpdatableApps(t *testing.T) { + testCases := []struct { + name string + bp string + expectedError string + }{ + { + name: "Stable public SDK", + bp: `android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "29", + updatable: true, + }`, + }, + { + name: "Stable system SDK", + bp: `android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "system_29", + updatable: true, + }`, + }, + { + name: "Current public SDK", + bp: `android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "current", + updatable: true, + }`, + }, + { + name: "Current system SDK", + bp: `android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "system_current", + updatable: true, + }`, + }, + { + name: "Current module SDK", + bp: `android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "module_current", + updatable: true, + }`, + }, + { + name: "Current core SDK", + bp: `android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "core_current", + updatable: true, + }`, + }, + { + name: "No Platform APIs", + bp: `android_app { + name: "foo", + srcs: ["a.java"], + platform_apis: true, + updatable: true, + }`, + expectedError: "Updatable apps must use stable SDKs", + }, + { + name: "No Core Platform APIs", + bp: `android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "core_platform", + updatable: true, + }`, + expectedError: "Updatable apps must use stable SDKs", + }, + { + name: "No unspecified APIs", + bp: `android_app { + name: "foo", + srcs: ["a.java"], + updatable: true, + }`, + expectedError: "Updatable apps must use stable SDK", + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + if test.expectedError == "" { + testJava(t, test.bp) + } else { + testJavaError(t, test.expectedError, test.bp) + } + }) + } +} + func TestResourceDirs(t *testing.T) { testCases := []struct { name string @@ -791,6 +893,7 @@ func TestJNIABI(t *testing.T) { cc_library { name: "libjni", system_shared_libs: [], + sdk_version: "current", stl: "none", } @@ -928,26 +1031,26 @@ func TestJNIPackaging(t *testing.T) { android_test { name: "test", - sdk_version: "core_platform", + sdk_version: "current", jni_libs: ["libjni"], } android_test { name: "test_noembed", - sdk_version: "core_platform", + sdk_version: "current", jni_libs: ["libjni"], use_embedded_native_libs: false, } android_test_helper_app { name: "test_helper", - sdk_version: "core_platform", + sdk_version: "current", jni_libs: ["libjni"], } android_test_helper_app { name: "test_helper_noembed", - sdk_version: "core_platform", + sdk_version: "current", jni_libs: ["libjni"], use_embedded_native_libs: false, } @@ -979,6 +1082,10 @@ func TestJNIPackaging(t *testing.T) { if g, w := !strings.Contains(jniLibZip.Args["jarArgs"], "-L 0"), test.compressed; g != w { t.Errorf("expected jni compressed %v, got %v", w, g) } + + if !strings.Contains(jniLibZip.Implicits[0].String(), "_sdk_") { + t.Errorf("expected input %q to use sdk variant", jniLibZip.Implicits[0].String()) + } } }) } @@ -2344,11 +2451,17 @@ func checkAapt2LinkFlag(t *testing.T, aapt2Flags, flagName, expectedValue string } func TestRuntimeResourceOverlay(t *testing.T) { - ctx, config := testJava(t, ` + fs := map[string][]byte{ + "baz/res/res/values/strings.xml": nil, + "bar/res/res/values/strings.xml": nil, + } + bp := ` runtime_resource_overlay { name: "foo", certificate: "platform", product_specific: true, + static_libs: ["bar"], + resource_libs: ["baz"], aaptflags: ["--keep-raw-values"], } @@ -2358,7 +2471,21 @@ func TestRuntimeResourceOverlay(t *testing.T) { product_specific: true, theme: "faza", } - `) + + android_library { + name: "bar", + resource_dirs: ["bar/res"], + } + + android_app { + name: "baz", + sdk_version: "current", + resource_dirs: ["baz/res"], + } + ` + config := testAppConfig(nil, bp, fs) + ctx := testContext() + run(t, ctx, config) m := ctx.ModuleForTests("foo", "android_common") @@ -2370,6 +2497,19 @@ func TestRuntimeResourceOverlay(t *testing.T) { t.Errorf("expected values, %q are missing in aapt2 link flags, %q", absentFlags, aapt2Flags) } + // Check overlay.list output for static_libs dependency. + overlayList := m.Output("aapt2/overlay.list").Inputs.Strings() + staticLibPackage := buildDir + "/.intermediates/bar/android_common/package-res.apk" + if !inList(staticLibPackage, overlayList) { + t.Errorf("Stactic lib res package %q missing in overlay list: %q", staticLibPackage, overlayList) + } + + // Check AAPT2 link flags for resource_libs dependency. + resourceLibFlag := "-I " + buildDir + "/.intermediates/baz/android_common/package-res.apk" + if !strings.Contains(aapt2Flags, resourceLibFlag) { + t.Errorf("Resource lib flag %q missing in aapt2 link flags: %q", resourceLibFlag, aapt2Flags) + } + // Check cert signing flag. signedApk := m.Output("signed/foo.apk") signingFlag := signedApk.Args["certificates"] diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index d00864d23..543b2333c 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -26,7 +26,7 @@ import ( ) func init() { - android.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory) + RegisterDexpreoptBootJarsComponents(android.InitRegistrationContext) } // Target-independent description of pre-compiled boot image. @@ -174,6 +174,10 @@ func dexpreoptBootJarsFactory() android.Singleton { return &dexpreoptBootJars{} } +func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) { + ctx.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory) +} + func skipDexpreoptBootJars(ctx android.PathContext) bool { if dexpreopt.GetGlobalConfig(ctx).DisablePreopt { return true @@ -242,16 +246,66 @@ func (d *dexpreoptBootJars) GenerateBuildActions(ctx android.SingletonContext) { dumpOatRules(ctx, d.defaultBootImage) } +// Inspect this module to see if it contains a bootclasspath dex jar. +// Note that the same jar may occur in multiple modules. +// This logic is tested in the apex package to avoid import cycle apex <-> java. +func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, module android.Module) (int, android.Path) { + // All apex Java libraries have non-installable platform variants, skip them. + if module.IsSkipInstall() { + return -1, nil + } + + jar, hasJar := module.(interface{ DexJar() android.Path }) + if !hasJar { + return -1, nil + } + + name := ctx.ModuleName(module) + index := android.IndexList(name, image.modules) + if index == -1 { + return -1, nil + } + + // Check that this module satisfies constraints for a particular boot image. + apex, isApexModule := module.(android.ApexModule) + if image.name == artBootImageName { + if isApexModule && strings.HasPrefix(apex.ApexName(), "com.android.art.") { + // ok, found the jar in the ART apex + } else if isApexModule && !apex.IsForPlatform() { + // this jar is part of an updatable apex other than ART, fail immediately + ctx.Errorf("module '%s' from updatable apex '%s' is not allowed in the ART boot image", name, apex.ApexName()) + } else if isApexModule && apex.IsForPlatform() && Bool(module.(*Library).deviceProperties.Hostdex) { + // this is a special "hostdex" variant, skip it and resume search + return -1, nil + } else if name == "jacocoagent" && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") { + // this is Jacoco platform variant for a coverage build, skip it and resume search + return -1, nil + } else { + // this (installable) jar is part of the platform, fail immediately + ctx.Errorf("module '%s' is part of the platform and not allowed in the ART boot image", name) + } + } else if image.name == frameworkBootImageName { + if !isApexModule || apex.IsForPlatform() { + // ok, this jar is part of the platform + } else { + // this jar is part of an updatable apex, fail immediately + ctx.Errorf("module '%s' from updatable apex '%s' is not allowed in the framework boot image", name, apex.ApexName()) + } + } else { + panic("unknown boot image: " + image.name) + } + + return index, jar.DexJar() +} + // buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image. func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootImageConfig { + // Collect dex jar paths for the boot image modules. + // This logic is tested in the apex package to avoid import cycle apex <-> java. bootDexJars := make(android.Paths, len(image.modules)) ctx.VisitAllModules(func(module android.Module) { - // Collect dex jar paths for the modules listed above. - if j, ok := module.(interface{ DexJar() android.Path }); ok { - name := ctx.ModuleName(module) - if i := android.IndexList(name, image.modules); i != -1 { - bootDexJars[i] = j.DexJar() - } + if i, j := getBootImageJar(ctx, image, module); i != -1 { + bootDexJars[i] = j } }) @@ -263,7 +317,8 @@ func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootI missingDeps = append(missingDeps, image.modules[i]) bootDexJars[i] = android.PathForOutput(ctx, "missing") } else { - ctx.Errorf("failed to find dex jar path for module %q", + ctx.Errorf("failed to find a dex jar path for module '%s'"+ + ", note that some jars may be filtered out by module constraints", image.modules[i]) } } diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go index 94ca8bb97..127c20159 100644 --- a/java/dexpreopt_bootjars_test.go +++ b/java/dexpreopt_bootjars_test.go @@ -53,7 +53,7 @@ func TestDexpreoptBootJars(t *testing.T) { ctx := testContext() - ctx.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory) + RegisterDexpreoptBootJarsComponents(ctx) run(t, ctx, config) diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go index 1315aba64..066694cc3 100644 --- a/java/dexpreopt_config.go +++ b/java/dexpreopt_config.go @@ -81,7 +81,7 @@ func stemOf(moduleName string) string { func getDexLocation(ctx android.PathContext, target android.Target, subdir string, name string) string { if target.Os.Class == android.Host { - return filepath.Join("out", "host", ctx.Config().PrebuiltOS(), subdir, name) + return filepath.Join(ctx.Config().Getenv("OUT_DIR"), "host", ctx.Config().PrebuiltOS(), subdir, name) } else { return filepath.Join("/", subdir, name) } diff --git a/java/droiddoc.go b/java/droiddoc.go index b0efaa5bd..e4582f653 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -301,6 +301,11 @@ type DroidstubsProperties struct { // if set to true, allow Metalava to generate doc_stubs source files. Defaults to false. Create_doc_stubs *bool + // if set to false then do not write out stubs. Defaults to true. + // + // TODO(b/146727827): Remove capability when we do not need to generate stubs and API separately. + Generate_stubs *bool + // is set to true, Metalava will allow framework SDK to contain API levels annotations. Api_levels_annotations_enabled *bool @@ -370,10 +375,18 @@ func ignoreMissingModules(ctx android.BottomUpMutatorContext, apiToCheck *ApiToC apiToCheck.Removed_api_file = nil } +// Used by xsd_config type ApiFilePath interface { ApiFilePath() android.Path } +// Provider of information about API stubs, used by java_sdk_library. +type ApiStubsProvider interface { + ApiFilePath + RemovedApiFilePath() android.Path + StubsSrcJar() android.Path +} + // // Javadoc // @@ -1259,6 +1272,14 @@ func (d *Droidstubs) ApiFilePath() android.Path { return d.apiFilePath } +func (d *Droidstubs) RemovedApiFilePath() android.Path { + return d.removedApiFile +} + +func (d *Droidstubs) StubsSrcJar() android.Path { + return d.stubsSrcJar +} + func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) { d.Javadoc.addDeps(ctx) @@ -1285,7 +1306,7 @@ func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) { } } -func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.WritablePath) { +func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath) { if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") || apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") || String(d.properties.Api_filename) != "" { @@ -1341,11 +1362,13 @@ func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuil cmd.FlagWithArg("--sdk-values ", d.metadataDir.String()) } - if Bool(d.properties.Create_doc_stubs) { - cmd.FlagWithArg("--doc-stubs ", stubsDir.String()) - } else { - cmd.FlagWithArg("--stubs ", stubsDir.String()) - cmd.Flag("--exclude-documentation-from-stubs") + if stubsDir.Valid() { + if Bool(d.properties.Create_doc_stubs) { + cmd.FlagWithArg("--doc-stubs ", stubsDir.String()) + } else { + cmd.FlagWithArg("--stubs ", stubsDir.String()) + cmd.Flag("--exclude-documentation-from-stubs") + } } } @@ -1502,15 +1525,18 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { // Create rule for metalava - d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar") - srcJarDir := android.PathForModuleOut(ctx, "srcjars") - stubsDir := android.PathForModuleOut(ctx, "stubsDir") rule := android.NewRuleBuilder() - rule.Command().Text("rm -rf").Text(stubsDir.String()) - rule.Command().Text("mkdir -p").Text(stubsDir.String()) + generateStubs := BoolDefault(d.properties.Generate_stubs, true) + var stubsDir android.OptionalPath + if generateStubs { + d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar") + stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, "stubsDir")) + rule.Command().Text("rm -rf").Text(stubsDir.String()) + rule.Command().Text("mkdir -p").Text(stubsDir.String()) + } srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars) @@ -1536,13 +1562,15 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { cmd.ImplicitOutput(android.PathForModuleGen(ctx, o)) } - rule.Command(). - BuiltTool(ctx, "soong_zip"). - Flag("-write_if_changed"). - Flag("-jar"). - FlagWithOutput("-o ", d.Javadoc.stubsSrcJar). - FlagWithArg("-C ", stubsDir.String()). - FlagWithArg("-D ", stubsDir.String()) + if generateStubs { + rule.Command(). + BuiltTool(ctx, "soong_zip"). + Flag("-write_if_changed"). + Flag("-jar"). + FlagWithOutput("-o ", d.Javadoc.stubsSrcJar). + FlagWithArg("-C ", stubsDir.String()). + FlagWithArg("-D ", stubsDir.String()) + } if Bool(d.properties.Write_sdk_values) { d.metadataZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-metadata.zip") diff --git a/java/java.go b/java/java.go index c81b781b5..61974f5af 100644 --- a/java/java.go +++ b/java/java.go @@ -86,7 +86,7 @@ func RegisterJavaBuildComponents(ctx android.RegistrationContext) { ctx.RegisterSingletonType("kythe_java_extract", kytheExtractJavaFactory) } -func (j *Module) checkSdkVersion(ctx android.ModuleContext) { +func (j *Module) checkSdkVersions(ctx android.ModuleContext) { if j.SocSpecific() || j.DeviceSpecific() || (j.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) { if sc, ok := ctx.Module().(sdkContext); ok { @@ -96,6 +96,18 @@ func (j *Module) checkSdkVersion(ctx android.ModuleContext) { } } } + + ctx.VisitDirectDeps(func(module android.Module) { + tag := ctx.OtherModuleDependencyTag(module) + switch module.(type) { + // TODO(satayev): cover other types as well, e.g. imports + case *Library, *AndroidLibrary: + switch tag { + case bootClasspathTag, libTag, staticLibTag, java9LibTag: + checkLinkType(ctx, j, module.(linkTypeContext), tag.(dependencyTag)) + } + } + }) } func (j *Module) checkPlatformAPI(ctx android.ModuleContext) { @@ -898,15 +910,7 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { // Handled by AndroidApp.collectAppDeps return } - switch module.(type) { - case *Library, *AndroidLibrary: - if to, ok := module.(linkTypeContext); ok { - switch tag { - case bootClasspathTag, libTag, staticLibTag: - checkLinkType(ctx, j, to, tag.(dependencyTag)) - } - } - } + switch dep := module.(type) { case SdkLibraryDependency: switch tag { @@ -1760,11 +1764,6 @@ func (j *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Modu if staticLibTag == ctx.OtherModuleDependencyTag(dep) { return true } - // Also, a dependency to an sdk member is also considered as such. This is required because - // sdk members should be mutated into APEXes. Refer to sdk.sdkDepsReplaceMutator. - if sa, ok := dep.(android.SdkAware); ok && sa.IsInAnySdk() { - return true - } return false } @@ -1823,7 +1822,7 @@ func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bo } func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { - j.checkSdkVersion(ctx) + j.checkSdkVersions(ctx) j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar") j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter) @@ -2513,11 +2512,6 @@ func (j *Import) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Modu if staticLibTag == ctx.OtherModuleDependencyTag(dep) { return true } - // Also, a dependency to an sdk member is also considered as such. This is required because - // sdk members should be mutated into APEXes. Refer to sdk.sdkDepsReplaceMutator. - if sa, ok := dep.(android.SdkAware); ok && sa.IsInAnySdk() { - return true - } return false } diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go index cb17feedd..86eddb172 100644 --- a/java/prebuilt_apis.go +++ b/java/prebuilt_apis.go @@ -28,10 +28,6 @@ func init() { func RegisterPrebuiltApisBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("prebuilt_apis", PrebuiltApisFactory) - - ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) { - ctx.TopDown("prebuilt_apis", PrebuiltApisMutator).Parallel() - }) } type prebuiltApisProperties struct { @@ -48,7 +44,7 @@ func (module *prebuiltApis) GenerateAndroidBuildActions(ctx android.ModuleContex // no need to implement } -func parseJarPath(ctx android.BaseModuleContext, path string) (module string, apiver string, scope string) { +func parseJarPath(path string) (module string, apiver string, scope string) { elements := strings.Split(path, "/") apiver = elements[0] @@ -58,7 +54,7 @@ func parseJarPath(ctx android.BaseModuleContext, path string) (module string, ap return } -func parseApiFilePath(ctx android.BaseModuleContext, path string) (module string, apiver string, scope string) { +func parseApiFilePath(ctx android.LoadHookContext, path string) (module string, apiver string, scope string) { elements := strings.Split(path, "/") apiver = elements[0] @@ -73,7 +69,7 @@ func parseApiFilePath(ctx android.BaseModuleContext, path string) (module string return } -func createImport(mctx android.TopDownMutatorContext, module string, scope string, apiver string, path string) { +func createImport(mctx android.LoadHookContext, module string, scope string, apiver string, path string) { props := struct { Name *string Jars []string @@ -89,7 +85,7 @@ func createImport(mctx android.TopDownMutatorContext, module string, scope strin mctx.CreateModule(ImportFactory, &props) } -func createFilegroup(mctx android.TopDownMutatorContext, module string, scope string, apiver string, path string) { +func createFilegroup(mctx android.LoadHookContext, module string, scope string, apiver string, path string) { fgName := module + ".api." + scope + "." + apiver filegroupProps := struct { Name *string @@ -100,7 +96,7 @@ func createFilegroup(mctx android.TopDownMutatorContext, module string, scope st mctx.CreateModule(android.FileGroupFactory, &filegroupProps) } -func getPrebuiltFiles(mctx android.TopDownMutatorContext, name string) []string { +func getPrebuiltFiles(mctx android.LoadHookContext, name string) []string { mydir := mctx.ModuleDir() + "/" var files []string for _, apiver := range mctx.Module().(*prebuiltApis).properties.Api_dirs { @@ -115,7 +111,7 @@ func getPrebuiltFiles(mctx android.TopDownMutatorContext, name string) []string return files } -func prebuiltSdkStubs(mctx android.TopDownMutatorContext) { +func prebuiltSdkStubs(mctx android.LoadHookContext) { mydir := mctx.ModuleDir() + "/" // <apiver>/<scope>/<module>.jar files := getPrebuiltFiles(mctx, "*.jar") @@ -123,12 +119,12 @@ func prebuiltSdkStubs(mctx android.TopDownMutatorContext) { for _, f := range files { // create a Import module for each jar file localPath := strings.TrimPrefix(f, mydir) - module, apiver, scope := parseJarPath(mctx, localPath) + module, apiver, scope := parseJarPath(localPath) createImport(mctx, module, scope, apiver, localPath) } } -func prebuiltApiFiles(mctx android.TopDownMutatorContext) { +func prebuiltApiFiles(mctx android.LoadHookContext) { mydir := mctx.ModuleDir() + "/" // <apiver>/<scope>/api/<module>.txt files := getPrebuiltFiles(mctx, "api/*.txt") @@ -178,7 +174,7 @@ func prebuiltApiFiles(mctx android.TopDownMutatorContext) { } } -func PrebuiltApisMutator(mctx android.TopDownMutatorContext) { +func createPrebuiltApiModules(mctx android.LoadHookContext) { if _, ok := mctx.Module().(*prebuiltApis); ok { prebuiltApiFiles(mctx) prebuiltSdkStubs(mctx) @@ -191,9 +187,15 @@ func PrebuiltApisMutator(mctx android.TopDownMutatorContext) { // generates a filegroup module named <module>-api.<scope>.<ver>. // // It also creates <module>-api.<scope>.latest for the latest <ver>. +// +// Similarly, it generates a java_import for all API .jar files found under the +// directory where the Android.bp is located. Specifically, an API file located +// at ./<ver>/<scope>/api/<module>.jar generates a java_import module named +// <prebuilt-api-module>.<scope>.<ver>.<module>. func PrebuiltApisFactory() android.Module { module := &prebuiltApis{} module.AddProperties(&module.properties) android.InitAndroidModule(module) + android.AddLoadHook(module, createPrebuiltApiModules) return module } diff --git a/java/sdk.go b/java/sdk.go index 0e132d399..92076f4b5 100644 --- a/java/sdk.go +++ b/java/sdk.go @@ -147,6 +147,10 @@ type sdkSpec struct { raw string } +func (s sdkSpec) String() string { + return fmt.Sprintf("%s_%s", s.kind, s.version) +} + // valid checks if this sdkSpec is well-formed. Note however that true doesn't mean that the // specified SDK actually exists. func (s sdkSpec) valid() bool { @@ -158,6 +162,23 @@ func (s sdkSpec) specified() bool { return s.valid() && s.kind != sdkPrivate } +// whether the API surface is managed and versioned, i.e. has .txt file that +// get frozen on SDK freeze and changes get reviewed by API council. +func (s sdkSpec) stable() bool { + if !s.specified() { + return false + } + switch s.kind { + case sdkCore, sdkPublic, sdkSystem, sdkModule, sdkSystemServer: + return true + case sdkNone, sdkCorePlatform, sdkTest, sdkPrivate: + return false + default: + panic(fmt.Errorf("unknown sdkKind=%v", s.kind)) + } + return false +} + // prebuiltSdkAvailableForUnbundledBuilt tells whether this sdkSpec can have a prebuilt SDK // that can be used for unbundled builds. func (s sdkSpec) prebuiltSdkAvailableForUnbundledBuild() bool { diff --git a/java/sdk_library.go b/java/sdk_library.go index 52c900489..ff80d633b 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -15,17 +15,18 @@ package java import ( - "android/soong/android" - "fmt" "path" "path/filepath" + "reflect" "sort" "strings" "sync" "github.com/google/blueprint" "github.com/google/blueprint/proptools" + + "android/soong/android" ) const ( @@ -66,6 +67,9 @@ type apiScope struct { // The name of the api scope, e.g. public, system, test name string + // The name of the field in the dynamically created structure. + fieldName string + // The tag to use to depend on the stubs library module. stubsTag scopeDependencyTag @@ -86,11 +90,14 @@ type apiScope struct { // *current. Older stubs library built with a numbered SDK version is created from // the prebuilt jar. sdkVersion string + + // Extra arguments to pass to droidstubs for this scope. + droidstubsArgs []string } // Initialize a scope, creating and adding appropriate dependency tags func initApiScope(scope *apiScope) *apiScope { - //apiScope := &scope + scope.fieldName = proptools.FieldNameForProperty(scope.name) scope.stubsTag = scopeDependencyTag{ name: scope.name + "-stubs", apiScope: scope, @@ -131,6 +138,7 @@ var ( moduleSuffix: sdkSystemApiSuffix, apiFileMakeVariableSuffix: "_SYSTEM", sdkVersion: "system_current", + droidstubsArgs: []string{"-showAnnotation android.annotation.SystemApi"}, }) apiScopeTest = initApiScope(&apiScope{ name: "test", @@ -138,6 +146,7 @@ var ( moduleSuffix: sdkTestApiSuffix, apiFileMakeVariableSuffix: "_TEST", sdkVersion: "test_current", + droidstubsArgs: []string{"-showAnnotation android.annotation.TestApi"}, }) allApiScopes = apiScopes{ apiScopePublic, @@ -162,6 +171,14 @@ func init() { sort.Strings(*javaSdkLibraries) ctx.Strict("JAVA_SDK_LIBRARIES", strings.Join(*javaSdkLibraries, " ")) }) + + // Register sdk member types. + android.RegisterSdkMemberType(&sdkLibrarySdkMemberType{ + android.SdkMemberTypeBase{ + PropertyName: "java_sdk_libs", + SupportsSdk: true, + }, + }) } func RegisterSdkLibraryBuildComponents(ctx android.RegistrationContext) { @@ -219,9 +236,11 @@ type sdkLibraryProperties struct { } type scopePaths struct { - stubsHeaderPath android.Paths - stubsImplPath android.Paths - apiFilePath android.Path + stubsHeaderPath android.Paths + stubsImplPath android.Paths + currentApiFilePath android.Path + removedApiFilePath android.Path + stubsSrcJar android.Path } // Common code between sdk library and sdk library import @@ -308,11 +327,13 @@ func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) scopePaths.stubsImplPath = lib.ImplementationJars() } } - if doc, ok := to.(ApiFilePath); ok { + if doc, ok := to.(ApiStubsProvider); ok { if scopeTag, ok := tag.(scopeDependencyTag); ok { apiScope := scopeTag.apiScope scopePaths := module.getScopePaths(apiScope) - scopePaths.apiFilePath = doc.ApiFilePath() + scopePaths.currentApiFilePath = doc.ApiFilePath() + scopePaths.removedApiFilePath = doc.RemovedApiFilePath() + scopePaths.stubsSrcJar = doc.StubsSrcJar() } else { ctx.ModuleErrorf("depends on module %q of unknown tag %q", otherName, tag) } @@ -458,7 +479,7 @@ func (module *SdkLibrary) createStubsLibrary(mctx android.LoadHookContext, apiSc mctx.CreateModule(LibraryFactory, &props) } -// Creates a droiddoc module that creates stubs source files from the given full source +// Creates a droidstubs module that creates stubs source files from the given full source // files func (module *SdkLibrary) createStubsSources(mctx android.LoadHookContext, apiScope *apiScope) { props := struct { @@ -516,15 +537,15 @@ func (module *SdkLibrary) createStubsSources(mctx android.LoadHookContext, apiSc props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs - droiddocArgs := []string{} + droidstubsArgs := []string{} if len(module.sdkLibraryProperties.Api_packages) != 0 { - droiddocArgs = append(droiddocArgs, "--stub-packages "+strings.Join(module.sdkLibraryProperties.Api_packages, ":")) + droidstubsArgs = append(droidstubsArgs, "--stub-packages "+strings.Join(module.sdkLibraryProperties.Api_packages, ":")) } if len(module.sdkLibraryProperties.Hidden_api_packages) != 0 { - droiddocArgs = append(droiddocArgs, + droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(module.sdkLibraryProperties.Hidden_api_packages, " --hide-package ")) } - droiddocArgs = append(droiddocArgs, module.sdkLibraryProperties.Droiddoc_options...) + droidstubsArgs = append(droidstubsArgs, module.sdkLibraryProperties.Droiddoc_options...) disabledWarnings := []string{ "MissingPermission", "BroadcastBehavior", @@ -536,16 +557,12 @@ func (module *SdkLibrary) createStubsSources(mctx android.LoadHookContext, apiSc "Todo", "Typo", } - droiddocArgs = append(droiddocArgs, android.JoinWithPrefix(disabledWarnings, "--hide ")) + droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide ")) - switch apiScope { - case apiScopeSystem: - droiddocArgs = append(droiddocArgs, "-showAnnotation android.annotation.SystemApi") - case apiScopeTest: - droiddocArgs = append(droiddocArgs, " -showAnnotation android.annotation.TestApi") - } + // Add in scope specific arguments. + droidstubsArgs = append(droidstubsArgs, apiScope.droidstubsArgs...) props.Arg_files = module.sdkLibraryProperties.Droiddoc_option_files - props.Args = proptools.StringPtr(strings.Join(droiddocArgs, " ")) + props.Args = proptools.StringPtr(strings.Join(droidstubsArgs, " ")) // List of APIs identified from the provided source files are created. They are later // compared against to the not-yet-released (a.k.a current) list of APIs and to the @@ -813,42 +830,89 @@ type sdkLibraryScopeProperties struct { // List of shared java libs that this module has dependencies to Libs []string + + // The stub sources. + Stub_srcs []string `android:"path"` + + // The current.txt + Current_api string `android:"path"` + + // The removed.txt + Removed_api string `android:"path"` } type sdkLibraryImportProperties struct { // List of shared java libs, common to all scopes, that this module has // dependencies to Libs []string - - // Properties associated with the public api scope. - Public sdkLibraryScopeProperties - - // Properties associated with the system api scope. - System sdkLibraryScopeProperties - - // Properties associated with the test api scope. - Test sdkLibraryScopeProperties } type sdkLibraryImport struct { android.ModuleBase android.DefaultableModuleBase prebuilt android.Prebuilt + android.ApexModuleBase + android.SdkBase properties sdkLibraryImportProperties + // Map from api scope to the scope specific property structure. + scopeProperties map[*apiScope]*sdkLibraryScopeProperties + commonToSdkLibraryAndImport } var _ SdkLibraryDependency = (*sdkLibraryImport)(nil) +// The type of a structure that contains a field of type sdkLibraryScopeProperties +// for each apiscope in allApiScopes, e.g. something like: +// struct { +// Public sdkLibraryScopeProperties +// System sdkLibraryScopeProperties +// ... +// } +var allScopeStructType = createAllScopePropertiesStructType() + +// Dynamically create a structure type for each apiscope in allApiScopes. +func createAllScopePropertiesStructType() reflect.Type { + var fields []reflect.StructField + for _, apiScope := range allApiScopes { + field := reflect.StructField{ + Name: apiScope.fieldName, + Type: reflect.TypeOf(sdkLibraryScopeProperties{}), + } + fields = append(fields, field) + } + + return reflect.StructOf(fields) +} + +// Create an instance of the scope specific structure type and return a map +// from apiscope to a pointer to each scope specific field. +func createPropertiesInstance() (interface{}, map[*apiScope]*sdkLibraryScopeProperties) { + allScopePropertiesPtr := reflect.New(allScopeStructType) + allScopePropertiesStruct := allScopePropertiesPtr.Elem() + scopeProperties := make(map[*apiScope]*sdkLibraryScopeProperties) + + for _, apiScope := range allApiScopes { + field := allScopePropertiesStruct.FieldByName(apiScope.fieldName) + scopeProperties[apiScope] = field.Addr().Interface().(*sdkLibraryScopeProperties) + } + + return allScopePropertiesPtr.Interface(), scopeProperties +} + // java_sdk_library_import imports a prebuilt java_sdk_library. func sdkLibraryImportFactory() android.Module { module := &sdkLibraryImport{} - module.AddProperties(&module.properties) + allScopeProperties, scopeToProperties := createPropertiesInstance() + module.scopeProperties = scopeToProperties + module.AddProperties(&module.properties, allScopeProperties) android.InitPrebuiltModule(module, &[]string{""}) + android.InitApexModule(module) + android.InitSdkAwareModule(module) InitJavaModule(module, android.HostAndDeviceSupported) android.AddLoadHook(module, func(mctx android.LoadHookContext) { module.createInternalModules(mctx) }) @@ -870,48 +934,14 @@ func (module *sdkLibraryImport) createInternalModules(mctx android.LoadHookConte module.prebuilt.ForcePrefer() } - for apiScope, scopeProperties := range module.scopeProperties() { + for apiScope, scopeProperties := range module.scopeProperties { if len(scopeProperties.Jars) == 0 { continue } - // Creates a java import for the jar with ".stubs" suffix - props := struct { - Name *string - Soc_specific *bool - Device_specific *bool - Product_specific *bool - System_ext_specific *bool - Sdk_version *string - Libs []string - Jars []string - Prefer *bool - }{} - - props.Name = proptools.StringPtr(apiScope.stubsModuleName(module.BaseModuleName())) - props.Sdk_version = scopeProperties.Sdk_version - // Prepend any of the libs from the legacy public properties to the libs for each of the - // scopes to avoid having to duplicate them in each scope. - props.Libs = append(module.properties.Libs, scopeProperties.Libs...) - props.Jars = scopeProperties.Jars - - if module.SocSpecific() { - props.Soc_specific = proptools.BoolPtr(true) - } else if module.DeviceSpecific() { - props.Device_specific = proptools.BoolPtr(true) - } else if module.ProductSpecific() { - props.Product_specific = proptools.BoolPtr(true) - } else if module.SystemExtSpecific() { - props.System_ext_specific = proptools.BoolPtr(true) - } + module.createJavaImportForStubs(mctx, apiScope, scopeProperties) - // If the build should use prebuilt sdks then set prefer to true on the stubs library. - // That will cause the prebuilt version of the stubs to override the source version. - if mctx.Config().UnbundledBuildUsePrebuiltSdks() { - props.Prefer = proptools.BoolPtr(true) - } - - mctx.CreateModule(ImportFactory, &props) + module.createPrebuiltStubsSources(mctx, apiScope, scopeProperties) } javaSdkLibraries := javaSdkLibraries(mctx.Config()) @@ -920,16 +950,54 @@ func (module *sdkLibraryImport) createInternalModules(mctx android.LoadHookConte *javaSdkLibraries = append(*javaSdkLibraries, module.BaseModuleName()) } -func (module *sdkLibraryImport) scopeProperties() map[*apiScope]*sdkLibraryScopeProperties { - p := make(map[*apiScope]*sdkLibraryScopeProperties) - p[apiScopePublic] = &module.properties.Public - p[apiScopeSystem] = &module.properties.System - p[apiScopeTest] = &module.properties.Test - return p +func (module *sdkLibraryImport) createJavaImportForStubs(mctx android.LoadHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) { + // Creates a java import for the jar with ".stubs" suffix + props := struct { + Name *string + Soc_specific *bool + Device_specific *bool + Product_specific *bool + System_ext_specific *bool + Sdk_version *string + Libs []string + Jars []string + Prefer *bool + }{} + props.Name = proptools.StringPtr(apiScope.stubsModuleName(module.BaseModuleName())) + props.Sdk_version = scopeProperties.Sdk_version + // Prepend any of the libs from the legacy public properties to the libs for each of the + // scopes to avoid having to duplicate them in each scope. + props.Libs = append(module.properties.Libs, scopeProperties.Libs...) + props.Jars = scopeProperties.Jars + if module.SocSpecific() { + props.Soc_specific = proptools.BoolPtr(true) + } else if module.DeviceSpecific() { + props.Device_specific = proptools.BoolPtr(true) + } else if module.ProductSpecific() { + props.Product_specific = proptools.BoolPtr(true) + } else if module.SystemExtSpecific() { + props.System_ext_specific = proptools.BoolPtr(true) + } + // If the build should use prebuilt sdks then set prefer to true on the stubs library. + // That will cause the prebuilt version of the stubs to override the source version. + if mctx.Config().UnbundledBuildUsePrebuiltSdks() { + props.Prefer = proptools.BoolPtr(true) + } + mctx.CreateModule(ImportFactory, &props) +} + +func (module *sdkLibraryImport) createPrebuiltStubsSources(mctx android.LoadHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) { + props := struct { + Name *string + Srcs []string + }{} + props.Name = proptools.StringPtr(apiScope.docsModuleName(module.BaseModuleName())) + props.Srcs = scopeProperties.Stub_srcs + mctx.CreateModule(PrebuiltStubsSourcesFactory, &props) } func (module *sdkLibraryImport) DepsMutator(ctx android.BottomUpMutatorContext) { - for apiScope, scopeProperties := range module.scopeProperties() { + for apiScope, scopeProperties := range module.scopeProperties { if len(scopeProperties.Jars) == 0 { continue } @@ -1096,3 +1164,110 @@ func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries { }, }} } + +type sdkLibrarySdkMemberType struct { + android.SdkMemberTypeBase +} + +func (s *sdkLibrarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) { + mctx.AddVariationDependencies(nil, dependencyTag, names...) +} + +func (s *sdkLibrarySdkMemberType) IsInstance(module android.Module) bool { + _, ok := module.(*SdkLibrary) + return ok +} + +func (s *sdkLibrarySdkMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule { + return ctx.SnapshotBuilder().AddPrebuiltModule(member, "java_sdk_library_import") +} + +func (s *sdkLibrarySdkMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties { + return &sdkLibrarySdkMemberProperties{} +} + +type sdkLibrarySdkMemberProperties struct { + android.SdkMemberPropertiesBase + + // Scope to per scope properties. + Scopes map[*apiScope]scopeProperties + + // Additional libraries that the exported stubs libraries depend upon. + Libs []string + + // The Java stubs source files. + Stub_srcs []string +} + +type scopeProperties struct { + Jars android.Paths + StubsSrcJar android.Path + CurrentApiFile android.Path + RemovedApiFile android.Path + SdkVersion string +} + +func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) { + sdk := variant.(*SdkLibrary) + + s.Scopes = make(map[*apiScope]scopeProperties) + for _, apiScope := range allApiScopes { + paths := sdk.getScopePaths(apiScope) + jars := paths.stubsImplPath + if len(jars) > 0 { + properties := scopeProperties{} + properties.Jars = jars + properties.SdkVersion = apiScope.sdkVersion + properties.StubsSrcJar = paths.stubsSrcJar + properties.CurrentApiFile = paths.currentApiFilePath + properties.RemovedApiFile = paths.removedApiFilePath + s.Scopes[apiScope] = properties + } + } + + s.Libs = sdk.properties.Libs +} + +func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) { + for _, apiScope := range allApiScopes { + if properties, ok := s.Scopes[apiScope]; ok { + scopeSet := propertySet.AddPropertySet(apiScope.name) + + scopeDir := filepath.Join("sdk_library", s.OsPrefix(), apiScope.name) + + var jars []string + for _, p := range properties.Jars { + dest := filepath.Join(scopeDir, ctx.Name()+"-stubs.jar") + ctx.SnapshotBuilder().CopyToSnapshot(p, dest) + jars = append(jars, dest) + } + scopeSet.AddProperty("jars", jars) + + // Merge the stubs source jar into the snapshot zip so that when it is unpacked + // the source files are also unpacked. + snapshotRelativeDir := filepath.Join(scopeDir, ctx.Name()+"_stub_sources") + ctx.SnapshotBuilder().UnzipToSnapshot(properties.StubsSrcJar, snapshotRelativeDir) + scopeSet.AddProperty("stub_srcs", []string{snapshotRelativeDir}) + + if properties.CurrentApiFile != nil { + currentApiSnapshotPath := filepath.Join(scopeDir, ctx.Name()+".txt") + ctx.SnapshotBuilder().CopyToSnapshot(properties.CurrentApiFile, currentApiSnapshotPath) + scopeSet.AddProperty("current_api", currentApiSnapshotPath) + } + + if properties.RemovedApiFile != nil { + removedApiSnapshotPath := filepath.Join(scopeDir, ctx.Name()+"-removed.txt") + ctx.SnapshotBuilder().CopyToSnapshot(properties.CurrentApiFile, removedApiSnapshotPath) + scopeSet.AddProperty("removed_api", removedApiSnapshotPath) + } + + if properties.SdkVersion != "" { + scopeSet.AddProperty("sdk_version", properties.SdkVersion) + } + } + } + + if len(s.Libs) > 0 { + propertySet.AddPropertyWithTag("libs", s.Libs, ctx.SnapshotBuilder().SdkMemberReferencePropertyTag(false)) + } +} diff --git a/remoteexec/remoteexec.go b/remoteexec/remoteexec.go new file mode 100644 index 000000000..d43dc6c01 --- /dev/null +++ b/remoteexec/remoteexec.go @@ -0,0 +1,155 @@ +// Copyright 2020 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 remoteexec + +import ( + "sort" + "strings" + + "android/soong/android" + + "github.com/google/blueprint" +) + +const ( + // ContainerImageKey is the key identifying the container image in the platform spec. + ContainerImageKey = "container-image" + + // PoolKey is the key identifying the pool to use for remote execution. + PoolKey = "Pool" + + // DefaultImage is the default container image used for Android remote execution. The + // image was built with the Dockerfile at + // https://android.googlesource.com/platform/prebuilts/remoteexecution-client/+/refs/heads/master/docker/Dockerfile + DefaultImage = "docker://gcr.io/androidbuild-re-dockerimage/android-build-remoteexec-image@sha256:582efb38f0c229ea39952fff9e132ccbe183e14869b39888010dacf56b360d62" + + // DefaultWrapperPath is the default path to the remote execution wrapper. + DefaultWrapperPath = "prebuilts/remoteexecution-client/live/rewrapper" + + // DefaultPool is the name of the pool to use for remote execution when none is specified. + DefaultPool = "default" + + // LocalExecStrategy is the exec strategy to indicate that the action should be run locally. + LocalExecStrategy = "local" + + // RemoteExecStrategy is the exec strategy to indicate that the action should be run + // remotely. + RemoteExecStrategy = "remote" + + // RemoteLocalFallbackExecStrategy is the exec strategy to indicate that the action should + // be run remotely and fallback to local execution if remote fails. + RemoteLocalFallbackExecStrategy = "remote_local_fallback" +) + +var ( + defaultLabels = map[string]string{"type": "tool"} + defaultExecStrategy = LocalExecStrategy + pctx = android.NewPackageContext("android/soong/remoteexec") +) + +// REParams holds information pertinent to the remote execution of a rule. +type REParams struct { + // Platform is the key value pair used for remotely executing the action. + Platform map[string]string + // Labels is a map of labels that identify the rule. + Labels map[string]string + // ExecStrategy is the remote execution strategy: remote, local, or remote_local_fallback. + ExecStrategy string + // Inputs is a list of input paths or ninja variables. + Inputs []string + // RSPFile is the name of the ninja variable used by the rule as a placeholder for an rsp + // input. + RSPFile string + // OutputFiles is a list of output file paths or ninja variables as placeholders for rule + // outputs. + OutputFiles []string + // ToolchainInputs is a list of paths or ninja variables pointing to the location of + // toolchain binaries used by the rule. + ToolchainInputs []string +} + +func init() { + pctx.VariableFunc("Wrapper", func(ctx android.PackageVarContext) string { + if override := ctx.Config().Getenv("RBE_WRAPPER"); override != "" { + return override + } + return DefaultWrapperPath + }) +} + +// Generate the remote execution wrapper template to be added as a prefix to the rule's command. +func (r *REParams) Template() string { + template := "${remoteexec.Wrapper}" + + var kvs []string + labels := r.Labels + if len(labels) == 0 { + labels = defaultLabels + } + for k, v := range labels { + kvs = append(kvs, k+"="+v) + } + sort.Strings(kvs) + template += " --labels=" + strings.Join(kvs, ",") + + var platform []string + for k, v := range r.Platform { + if v == "" { + continue + } + platform = append(platform, k+"="+v) + } + if _, ok := r.Platform[ContainerImageKey]; !ok { + platform = append(platform, ContainerImageKey+"="+DefaultImage) + } + if platform != nil { + sort.Strings(platform) + template += " --platform=\"" + strings.Join(platform, ",") + "\"" + } + + strategy := r.ExecStrategy + if strategy == "" { + strategy = defaultExecStrategy + } + template += " --exec_strategy=" + strategy + + if len(r.Inputs) > 0 { + template += " --inputs=" + strings.Join(r.Inputs, ",") + } + + if r.RSPFile != "" { + template += " --input_list_paths=" + r.RSPFile + } + + if len(r.OutputFiles) > 0 { + template += " --output_files=" + strings.Join(r.OutputFiles, ",") + } + + if len(r.ToolchainInputs) > 0 { + template += " --toolchain_inputs=" + strings.Join(r.ToolchainInputs, ",") + } + + return template + " -- " +} + +// StaticRules returns a pair of rules based on the given RuleParams, where the first rule is a +// locally executable rule and the second rule is a remotely executable rule. +func StaticRules(ctx android.PackageContext, name string, ruleParams blueprint.RuleParams, reParams *REParams, commonArgs []string, reArgs []string) (blueprint.Rule, blueprint.Rule) { + ruleParamsRE := ruleParams + ruleParamsRE.Command = reParams.Template() + ruleParamsRE.Command + + return ctx.AndroidStaticRule(name, ruleParams, commonArgs...), + ctx.AndroidRemoteStaticRule(name+"RE", android.RemoteRuleSupports{RBE: true}, ruleParamsRE, append(commonArgs, reArgs...)...) +} diff --git a/remoteexec/remoteexec_test.go b/remoteexec/remoteexec_test.go new file mode 100644 index 000000000..30e891ced --- /dev/null +++ b/remoteexec/remoteexec_test.go @@ -0,0 +1,83 @@ +// Copyright 2020 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 remoteexec + +import ( + "fmt" + "testing" +) + +func TestTemplate(t *testing.T) { + tests := []struct { + name string + params *REParams + want string + }{ + { + name: "basic", + params: &REParams{ + Labels: map[string]string{"type": "compile", "lang": "cpp", "compiler": "clang"}, + Inputs: []string{"$in"}, + OutputFiles: []string{"$out"}, + Platform: map[string]string{ + ContainerImageKey: DefaultImage, + PoolKey: "default", + }, + }, + want: fmt.Sprintf("${remoteexec.Wrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out -- ", DefaultImage), + }, + { + name: "all params", + params: &REParams{ + Labels: map[string]string{"type": "compile", "lang": "cpp", "compiler": "clang"}, + Inputs: []string{"$in"}, + OutputFiles: []string{"$out"}, + ExecStrategy: "remote", + RSPFile: "$out.rsp", + ToolchainInputs: []string{"clang++"}, + Platform: map[string]string{ + ContainerImageKey: DefaultImage, + PoolKey: "default", + }, + }, + want: fmt.Sprintf("${remoteexec.Wrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=remote --inputs=$in --input_list_paths=$out.rsp --output_files=$out --toolchain_inputs=clang++ -- ", DefaultImage), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if got := test.params.Template(); got != test.want { + t.Errorf("Template() returned\n%s\nwant\n%s", got, test.want) + } + }) + } +} + +func TestTemplateDeterminism(t *testing.T) { + r := &REParams{ + Labels: map[string]string{"type": "compile", "lang": "cpp", "compiler": "clang"}, + Inputs: []string{"$in"}, + OutputFiles: []string{"$out"}, + Platform: map[string]string{ + ContainerImageKey: DefaultImage, + PoolKey: "default", + }, + } + want := fmt.Sprintf("${remoteexec.Wrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out -- ", DefaultImage) + for i := 0; i < 1000; i++ { + if got := r.Template(); got != want { + t.Fatalf("Template() returned\n%s\nwant\n%s", got, want) + } + } +} diff --git a/rust/compiler.go b/rust/compiler.go index 4593165f4..81b258c1e 100644 --- a/rust/compiler.go +++ b/rust/compiler.go @@ -222,7 +222,10 @@ func (compiler *baseCompiler) installDir(ctx ModuleContext) android.InstallPath if ctx.toolchain().Is64Bit() && compiler.dir64 != "" { dir = compiler.dir64 } - if !ctx.Host() || ctx.Target().NativeBridge == android.NativeBridgeEnabled { + if ctx.Target().NativeBridge == android.NativeBridgeEnabled { + dir = filepath.Join(dir, ctx.Target().NativeBridgeRelativePath) + } + if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) { dir = filepath.Join(dir, ctx.Arch().ArchType.String()) } return android.PathForModuleInstall(ctx, dir, compiler.subDir, diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go index 60796d8d8..180fd8ba0 100644 --- a/rust/config/arm64_device.go +++ b/rust/config/arm64_device.go @@ -27,7 +27,6 @@ var ( "-Wl,--icf=safe", "-Wl,-z,max-page-size=4096", - "-Wl,--execute-only", "-Wl,-z,separate-code", } diff --git a/rust/library.go b/rust/library.go index 0cf2dd045..bf863bb67 100644 --- a/rust/library.go +++ b/rust/library.go @@ -318,6 +318,8 @@ func (library *libraryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps { if ctx.toolchain().Bionic() && (library.dylib() || library.shared()) { deps = library.baseCompiler.bionicDeps(ctx, deps) + deps.CrtBegin = "crtbegin_so" + deps.CrtEnd = "crtend_so" } return deps diff --git a/rust/rust.go b/rust/rust.go index f446ef039..6fe887129 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -164,6 +164,10 @@ func (mod *Module) OnlyInRecovery() bool { return false } +func (mod *Module) UseSdk() bool { + return false +} + func (mod *Module) UseVndk() bool { return false } @@ -184,6 +188,10 @@ func (mod *Module) SdkVersion() string { return "" } +func (mod *Module) AlwaysSdk() bool { + return false +} + func (mod *Module) ToolchainLibrary() bool { return false } diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go index 9e6086f15..780da9fc3 100644 --- a/sdk/cc_sdk_test.go +++ b/sdk/cc_sdk_test.go @@ -1766,7 +1766,6 @@ cc_prebuilt_library_shared { sdk_member_name: "stubslib", installable: false, stubs: { - symbol_file: "etc/stubslib.map.txt", versions: ["3"], }, arch: { @@ -1783,7 +1782,6 @@ cc_prebuilt_library_shared { name: "stubslib", prefer: false, stubs: { - symbol_file: "etc/stubslib.map.txt", versions: ["3"], }, arch: { diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go index cbffb501b..c0ad35c3f 100644 --- a/sdk/java_sdk_test.go +++ b/sdk/java_sdk_test.go @@ -24,7 +24,51 @@ func testSdkWithJava(t *testing.T, bp string) *testSdkResult { fs := map[string][]byte{ "Test.java": nil, "aidl/foo/bar/Test.aidl": nil, + + // For java_sdk_library + "api/current.txt": nil, + "api/removed.txt": nil, + "api/system-current.txt": nil, + "api/system-removed.txt": nil, + "api/test-current.txt": nil, + "api/test-removed.txt": nil, + "build/soong/scripts/gen-java-current-api-files.sh": nil, } + + // for java_sdk_library tests + bp = ` +java_system_modules_import { + name: "core-current-stubs-system-modules", +} +java_system_modules_import { + name: "core-platform-api-stubs-system-modules", +} +java_import { + name: "core.platform.api.stubs", +} +java_sdk_library_import { + name: "android_stubs_current", +} +java_sdk_library_import { + name: "android_system_stubs_current", +} +java_sdk_library_import { + name: "android_test_stubs_current", +} +java_import { + name: "core-lambda-stubs", + sdk_version: "none", +} +java_import { + name: "ext", + sdk_version: "none", +} +java_import { + name: "framework", + sdk_version: "none", +} +` + bp + return testSdkWithFs(t, bp, fs) } @@ -53,30 +97,18 @@ func TestBasicSdkWithJavaLibrary(t *testing.T) { system_modules: "none", sdk_version: "none", host_supported: true, - apex_available: [ - "//apex_available:platform", - "//apex_available:anyapex", - ], } java_import { name: "sdkmember_mysdk_1", sdk_member_name: "sdkmember", host_supported: true, - apex_available: [ - "//apex_available:platform", - "//apex_available:anyapex", - ], } java_import { name: "sdkmember_mysdk_2", sdk_member_name: "sdkmember", host_supported: true, - apex_available: [ - "//apex_available:platform", - "//apex_available:anyapex", - ], } java_library { @@ -592,7 +624,7 @@ module_exports_snapshot { `), checkAllCopyRules(""), - checkMergeZip(".intermediates/myexports/common_os/tmp/java/myjavaapistubs_stubs_sources.zip"), + checkMergeZips(".intermediates/myexports/common_os/tmp/java/myjavaapistubs_stubs_sources.zip"), ) } @@ -646,7 +678,7 @@ module_exports_snapshot { } `), checkAllCopyRules(""), - checkMergeZip(".intermediates/myexports/common_os/tmp/java/myjavaapistubs_stubs_sources.zip"), + checkMergeZips(".intermediates/myexports/common_os/tmp/java/myjavaapistubs_stubs_sources.zip"), ) } @@ -939,3 +971,99 @@ module_exports_snapshot { `), ) } + +func TestSnapshotWithJavaSdkLibrary(t *testing.T) { + result := testSdkWithJava(t, ` + sdk { + name: "mysdk", + java_sdk_libs: ["myjavalib"], + } + + java_sdk_library { + name: "myjavalib", + apex_available: ["//apex_available:anyapex"], + srcs: ["Test.java"], + sdk_version: "current", + } + `) + + result.CheckSnapshot("mysdk", "", + checkAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. + +java_sdk_library_import { + name: "mysdk_myjavalib@current", + sdk_member_name: "myjavalib", + apex_available: ["//apex_available:anyapex"], + public: { + jars: ["sdk_library/public/myjavalib-stubs.jar"], + stub_srcs: ["sdk_library/public/myjavalib_stub_sources"], + current_api: "sdk_library/public/myjavalib.txt", + removed_api: "sdk_library/public/myjavalib-removed.txt", + sdk_version: "current", + }, + system: { + jars: ["sdk_library/system/myjavalib-stubs.jar"], + stub_srcs: ["sdk_library/system/myjavalib_stub_sources"], + current_api: "sdk_library/system/myjavalib.txt", + removed_api: "sdk_library/system/myjavalib-removed.txt", + sdk_version: "system_current", + }, + test: { + jars: ["sdk_library/test/myjavalib-stubs.jar"], + stub_srcs: ["sdk_library/test/myjavalib_stub_sources"], + current_api: "sdk_library/test/myjavalib.txt", + removed_api: "sdk_library/test/myjavalib-removed.txt", + sdk_version: "test_current", + }, +} + +java_sdk_library_import { + name: "myjavalib", + prefer: false, + apex_available: ["//apex_available:anyapex"], + public: { + jars: ["sdk_library/public/myjavalib-stubs.jar"], + stub_srcs: ["sdk_library/public/myjavalib_stub_sources"], + current_api: "sdk_library/public/myjavalib.txt", + removed_api: "sdk_library/public/myjavalib-removed.txt", + sdk_version: "current", + }, + system: { + jars: ["sdk_library/system/myjavalib-stubs.jar"], + stub_srcs: ["sdk_library/system/myjavalib_stub_sources"], + current_api: "sdk_library/system/myjavalib.txt", + removed_api: "sdk_library/system/myjavalib-removed.txt", + sdk_version: "system_current", + }, + test: { + jars: ["sdk_library/test/myjavalib-stubs.jar"], + stub_srcs: ["sdk_library/test/myjavalib_stub_sources"], + current_api: "sdk_library/test/myjavalib.txt", + removed_api: "sdk_library/test/myjavalib-removed.txt", + sdk_version: "test_current", + }, +} + +sdk_snapshot { + name: "mysdk@current", + java_sdk_libs: ["mysdk_myjavalib@current"], +} +`), + checkAllCopyRules(` +.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar +.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt +.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib-removed.txt +.intermediates/myjavalib.stubs.system/android_common/javac/myjavalib.stubs.system.jar -> sdk_library/system/myjavalib-stubs.jar +.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt +.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib-removed.txt +.intermediates/myjavalib.stubs.test/android_common/javac/myjavalib.stubs.test.jar -> sdk_library/test/myjavalib-stubs.jar +.intermediates/myjavalib.stubs.source.test/android_common/myjavalib.stubs.source.test_api.txt -> sdk_library/test/myjavalib.txt +.intermediates/myjavalib.stubs.source.test/android_common/myjavalib.stubs.source.test_api.txt -> sdk_library/test/myjavalib-removed.txt +`), + checkMergeZips( + ".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip", + ".intermediates/mysdk/common_os/tmp/sdk_library/system/myjavalib_stub_sources.zip", + ".intermediates/mysdk/common_os/tmp/sdk_library/test/myjavalib_stub_sources.zip"), + ) +} diff --git a/sdk/sdk.go b/sdk/sdk.go index dabdf851e..cb5a6053d 100644 --- a/sdk/sdk.go +++ b/sdk/sdk.go @@ -312,7 +312,7 @@ func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) { ctx.BottomUp("SdkMemberInterVersion", memberInterVersionMutator).Parallel() } -// RegisterPostDepshMutators registers post-deps mutators to support modules implementing SdkAware +// RegisterPostDepsMutators registers post-deps mutators to support modules implementing SdkAware // interface and the sdk module type. This function has been made public to be called by tests // outside of the sdk package func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) { @@ -431,23 +431,31 @@ func sdkDepsReplaceMutator(mctx android.BottomUpMutatorContext) { } } -// Step 6: ensure that the dependencies from outside of the APEX are all from the required SDKs +// Step 6: ensure that the dependencies outside of the APEX are all from the required SDKs func sdkRequirementsMutator(mctx android.TopDownMutatorContext) { if m, ok := mctx.Module().(interface { - DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool - RequiredSdks() android.SdkRefs + android.DepIsInSameApex + android.RequiredSdks }); ok { requiredSdks := m.RequiredSdks() if len(requiredSdks) == 0 { return } mctx.VisitDirectDeps(func(dep android.Module) { - if mctx.OtherModuleDependencyTag(dep) == android.DefaultsDepTag { + tag := mctx.OtherModuleDependencyTag(dep) + if tag == android.DefaultsDepTag { // dependency to defaults is always okay return } - // If the dep is from outside of the APEX, but is not in any of the + // Ignore the dependency from the unversioned member to any versioned members as an + // apex that depends on the unversioned member will not also be depending on a versioned + // member. + if _, ok := tag.(sdkMemberVersionedDepTag); ok { + return + } + + // If the dep is outside of the APEX, but is not in any of the // required SDKs, we know that the dep is a violation. if sa, ok := dep.(android.SdkAware); ok { if !m.DepIsInSameApex(mctx, dep) && !requiredSdks.Contains(sa.ContainingSdk()) { diff --git a/sdk/testing.go b/sdk/testing.go index 00245cef4..9e272019e 100644 --- a/sdk/testing.go +++ b/sdk/testing.go @@ -90,6 +90,7 @@ func testSdkContext(bp string, fs map[string][]byte) (*android.TestContext, andr // from java package java.RegisterJavaBuildComponents(ctx) java.RegisterAppBuildComponents(ctx) + java.RegisterSdkLibraryBuildComponents(ctx) java.RegisterStubsBuildComponents(ctx) java.RegisterSystemModulesBuildComponents(ctx) @@ -338,14 +339,15 @@ func checkAllOtherCopyRules(expected string) snapshotBuildInfoChecker { } } -// Check that the specified path is in the list of zips to merge with the intermediate zip. -func checkMergeZip(expected string) snapshotBuildInfoChecker { +// Check that the specified paths match the list of zips to merge with the intermediate zip. +func checkMergeZips(expected ...string) snapshotBuildInfoChecker { return func(info *snapshotBuildInfo) { info.r.t.Helper() if info.intermediateZip == "" { info.r.t.Errorf("No intermediate zip file was created") } - ensureListContains(info.r.t, info.mergeZips, expected) + + info.r.AssertDeepEquals("mismatching merge zip files", expected, info.mergeZips) } } diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go index ce8f968d7..a3234e339 100644 --- a/ui/build/dumpvars.go +++ b/ui/build/dumpvars.go @@ -190,6 +190,7 @@ func runMakeProductConfig(ctx Context, config Config) { // compiler wrappers set up by make "CC_WRAPPER", "CXX_WRAPPER", + "RBE_WRAPPER", "JAVAC_WRAPPER", "R8_WRAPPER", "D8_WRAPPER", diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go index 5ca83cce6..98eb028a8 100644 --- a/ui/build/sandbox_linux.go +++ b/ui/build/sandbox_linux.go @@ -19,6 +19,7 @@ import ( "os" "os/exec" "os/user" + "path/filepath" "strings" "sync" ) @@ -75,11 +76,22 @@ func (c *Cmd) sandboxSupported() bool { sandboxConfig.group = "nobody" } + // These directories will be bind mounted + // so we need full non-symlink paths sandboxConfig.srcDir = absPath(c.ctx, ".") + if derefPath, err := filepath.EvalSymlinks(sandboxConfig.srcDir); err == nil { + sandboxConfig.srcDir = absPath(c.ctx, derefPath) + } sandboxConfig.outDir = absPath(c.ctx, c.config.OutDir()) + if derefPath, err := filepath.EvalSymlinks(sandboxConfig.outDir); err == nil { + sandboxConfig.outDir = absPath(c.ctx, derefPath) + } sandboxConfig.distDir = absPath(c.ctx, c.config.DistDir()) + if derefPath, err := filepath.EvalSymlinks(sandboxConfig.distDir); err == nil { + sandboxConfig.distDir = absPath(c.ctx, derefPath) + } - cmd := exec.CommandContext(c.ctx.Context, nsjailPath, + sandboxArgs := []string{ "-H", "android-build", "-e", "-u", "nobody", @@ -88,10 +100,21 @@ func (c *Cmd) sandboxSupported() bool { "-B", sandboxConfig.srcDir, "-B", "/tmp", "-B", sandboxConfig.outDir, - "-B", sandboxConfig.distDir, + } + + if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) { + //Mount dist dir as read-write if it already exists + sandboxArgs = append(sandboxArgs, "-B", + sandboxConfig.distDir) + } + + sandboxArgs = append(sandboxArgs, "--disable_clone_newcgroup", "--", "/bin/bash", "-c", `if [ $(hostname) == "android-build" ]; then echo "Android" "Success"; else echo Failure; fi`) + + cmd := exec.CommandContext(c.ctx.Context, nsjailPath, sandboxArgs...) + cmd.Env = c.config.Environment().Environ() c.ctx.Verboseln(cmd.Args) @@ -164,9 +187,6 @@ func (c *Cmd) wrapSandbox() { //Mount out dir as read-write "-B", sandboxConfig.outDir, - //Mount dist dir as read-write - "-B", sandboxConfig.distDir, - // Mount a writable tmp dir "-B", "/tmp", @@ -178,6 +198,11 @@ func (c *Cmd) wrapSandbox() { "-q", } + if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) { + //Mount dist dir as read-write if it already exists + sandboxArgs = append(sandboxArgs, "-B", sandboxConfig.distDir) + } + if c.Sandbox.AllowBuildBrokenUsesNetwork && c.config.BuildBrokenUsesNetwork() { c.ctx.Printf("AllowBuildBrokenUsesNetwork: %v", c.Sandbox.AllowBuildBrokenUsesNetwork) c.ctx.Printf("BuildBrokenUsesNetwork: %v", c.config.BuildBrokenUsesNetwork()) diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp index 529639de5..3596e1019 100644 --- a/ui/metrics/Android.bp +++ b/ui/metrics/Android.bp @@ -17,6 +17,7 @@ bootstrap_go_package { pkgPath: "android/soong/ui/metrics", deps: [ "golang-protobuf-proto", + "soong-ui-metrics_upload_proto", "soong-ui-metrics_proto", "soong-ui-tracer", ], @@ -35,3 +36,11 @@ bootstrap_go_package { ], } +bootstrap_go_package { + name: "soong-ui-metrics_upload_proto", + pkgPath: "android/soong/ui/metrics/upload_proto", + deps: ["golang-protobuf-proto"], + srcs: [ + "upload_proto/upload.pb.go", + ], +} diff --git a/ui/metrics/upload_proto/regen.sh b/ui/metrics/upload_proto/regen.sh new file mode 100755 index 000000000..4521df7bd --- /dev/null +++ b/ui/metrics/upload_proto/regen.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# Generates the golang source file of upload.proto file. + +set -e + +function die() { echo "ERROR: $1" >&2; exit 1; } + +readonly error_msg="Maybe you need to run 'lunch aosp_arm-eng && m aprotoc blueprint_tools'?" + +if ! hash aprotoc &>/dev/null; then + die "could not find aprotoc. ${error_msg}" +fi + +if ! aprotoc --go_out=paths=source_relative:. upload.proto; then + die "build failed. ${error_msg}" +fi diff --git a/ui/metrics/upload_proto/upload.pb.go b/ui/metrics/upload_proto/upload.pb.go new file mode 100644 index 000000000..1b1e5e843 --- /dev/null +++ b/ui/metrics/upload_proto/upload.pb.go @@ -0,0 +1,122 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: upload.proto + +package soong_metrics_upload_proto + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type Upload struct { + // The timestamp in milliseconds that the build was created. + CreationTimestampMs *uint64 `protobuf:"varint,1,opt,name=creation_timestamp_ms,json=creationTimestampMs" json:"creation_timestamp_ms,omitempty"` + // The timestamp in milliseconds when the build was completed. + CompletionTimestampMs *uint64 `protobuf:"varint,2,opt,name=completion_timestamp_ms,json=completionTimestampMs" json:"completion_timestamp_ms,omitempty"` + // The branch name. + BranchName *string `protobuf:"bytes,3,opt,name=branch_name,json=branchName" json:"branch_name,omitempty"` + // The target name. + TargetName *string `protobuf:"bytes,4,opt,name=target_name,json=targetName" json:"target_name,omitempty"` + // A list of metrics filepaths to upload. + MetricsFiles []string `protobuf:"bytes,5,rep,name=metrics_files,json=metricsFiles" json:"metrics_files,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Upload) Reset() { *m = Upload{} } +func (m *Upload) String() string { return proto.CompactTextString(m) } +func (*Upload) ProtoMessage() {} +func (*Upload) Descriptor() ([]byte, []int) { + return fileDescriptor_91b94b655bd2a7e5, []int{0} +} + +func (m *Upload) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Upload.Unmarshal(m, b) +} +func (m *Upload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Upload.Marshal(b, m, deterministic) +} +func (m *Upload) XXX_Merge(src proto.Message) { + xxx_messageInfo_Upload.Merge(m, src) +} +func (m *Upload) XXX_Size() int { + return xxx_messageInfo_Upload.Size(m) +} +func (m *Upload) XXX_DiscardUnknown() { + xxx_messageInfo_Upload.DiscardUnknown(m) +} + +var xxx_messageInfo_Upload proto.InternalMessageInfo + +func (m *Upload) GetCreationTimestampMs() uint64 { + if m != nil && m.CreationTimestampMs != nil { + return *m.CreationTimestampMs + } + return 0 +} + +func (m *Upload) GetCompletionTimestampMs() uint64 { + if m != nil && m.CompletionTimestampMs != nil { + return *m.CompletionTimestampMs + } + return 0 +} + +func (m *Upload) GetBranchName() string { + if m != nil && m.BranchName != nil { + return *m.BranchName + } + return "" +} + +func (m *Upload) GetTargetName() string { + if m != nil && m.TargetName != nil { + return *m.TargetName + } + return "" +} + +func (m *Upload) GetMetricsFiles() []string { + if m != nil { + return m.MetricsFiles + } + return nil +} + +func init() { + proto.RegisterType((*Upload)(nil), "soong_metrics_upload.Upload") +} + +func init() { + proto.RegisterFile("upload.proto", fileDescriptor_91b94b655bd2a7e5) +} + +var fileDescriptor_91b94b655bd2a7e5 = []byte{ + // 201 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x29, 0x2d, 0xc8, 0xc9, + 0x4f, 0x4c, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x29, 0xce, 0xcf, 0xcf, 0x4b, 0x8f, + 0xcf, 0x4d, 0x2d, 0x29, 0xca, 0x4c, 0x2e, 0x8e, 0x87, 0xc8, 0x29, 0xdd, 0x66, 0xe4, 0x62, 0x0b, + 0x05, 0x33, 0x85, 0x8c, 0xb8, 0x44, 0x93, 0x8b, 0x52, 0x13, 0x4b, 0x32, 0xf3, 0xf3, 0xe2, 0x4b, + 0x32, 0x73, 0x53, 0x8b, 0x4b, 0x12, 0x73, 0x0b, 0xe2, 0x73, 0x8b, 0x25, 0x18, 0x15, 0x18, 0x35, + 0x58, 0x82, 0x84, 0x61, 0x92, 0x21, 0x30, 0x39, 0xdf, 0x62, 0x21, 0x33, 0x2e, 0xf1, 0xe4, 0xfc, + 0xdc, 0x82, 0x9c, 0x54, 0x4c, 0x5d, 0x4c, 0x60, 0x5d, 0xa2, 0x08, 0x69, 0x64, 0x7d, 0xf2, 0x5c, + 0xdc, 0x49, 0x45, 0x89, 0x79, 0xc9, 0x19, 0xf1, 0x79, 0x89, 0xb9, 0xa9, 0x12, 0xcc, 0x0a, 0x8c, + 0x1a, 0x9c, 0x41, 0x5c, 0x10, 0x21, 0xbf, 0xc4, 0xdc, 0x54, 0x90, 0x82, 0x92, 0xc4, 0xa2, 0xf4, + 0xd4, 0x12, 0x88, 0x02, 0x16, 0x88, 0x02, 0x88, 0x10, 0x58, 0x81, 0x32, 0x17, 0x2f, 0xcc, 0x2b, + 0x69, 0x99, 0x39, 0xa9, 0xc5, 0x12, 0xac, 0x0a, 0xcc, 0x1a, 0x9c, 0x41, 0x3c, 0x50, 0x41, 0x37, + 0x90, 0x98, 0x93, 0x4c, 0x94, 0x14, 0x36, 0x5f, 0xc7, 0x83, 0x43, 0x04, 0x10, 0x00, 0x00, 0xff, + 0xff, 0xe2, 0x01, 0x74, 0x65, 0x20, 0x01, 0x00, 0x00, +} diff --git a/ui/metrics/upload_proto/upload.proto b/ui/metrics/upload_proto/upload.proto new file mode 100644 index 000000000..7a9f080e0 --- /dev/null +++ b/ui/metrics/upload_proto/upload.proto @@ -0,0 +1,35 @@ +// Copyright 2020 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 soong_metrics_upload; +option go_package = "soong_metrics_upload_proto"; + +message Upload { + // The timestamp in milliseconds that the build was created. + optional uint64 creation_timestamp_ms = 1; + + // The timestamp in milliseconds when the build was completed. + optional uint64 completion_timestamp_ms = 2; + + // The branch name. + optional string branch_name = 3; + + // The target name. + optional string target_name = 4; + + // A list of metrics filepaths to upload. + repeated string metrics_files = 5; +} |