diff options
46 files changed, 1690 insertions, 168 deletions
diff --git a/android/Android.bp b/android/Android.bp index f3a385025..6450a06ca 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -110,6 +110,7 @@ bootstrap_go_package { "paths_test.go", "prebuilt_test.go", "rule_builder_test.go", + "sdk_test.go", "singleton_module_test.go", "soong_config_modules_test.go", "util_test.go", diff --git a/android/arch.go b/android/arch.go index ddc082b00..6add1b132 100644 --- a/android/arch.go +++ b/android/arch.go @@ -934,6 +934,8 @@ func filterArchStruct(field reflect.StructField, prefix string) (bool, reflect.S if len(values) > 0 && values[0] != "path" { panic(fmt.Errorf("unknown tags %q in field %q", values, prefix+field.Name)) } else if len(values) == 1 { + // FIXME(b/200678898): This assumes that the only tag type when there's + // `android:"arch_variant"` is `android` itself and thus clobbers others field.Tag = reflect.StructTag(`android:"` + strings.Join(values, ",") + `"`) } else { field.Tag = `` diff --git a/android/bazel.go b/android/bazel.go index 9cebc800e..1cd84c9f6 100644 --- a/android/bazel.go +++ b/android/bazel.go @@ -150,17 +150,23 @@ var ( "build/bazel/platforms":/* recursive = */ true, "build/bazel/product_variables":/* recursive = */ true, "build/bazel_common_rules":/* recursive = */ true, + "build/make/tools":/* recursive = */ true, "build/pesto":/* recursive = */ true, // external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails // e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed "external/bazelbuild-rules_android":/* recursive = */ true, "external/bazel-skylib":/* recursive = */ true, + "external/guava":/* recursive = */ true, + "external/error_prone":/* recursive = */ true, + "external/jsr305":/* recursive = */ true, + "frameworks/ex/common":/* recursive = */ true, "prebuilts/sdk":/* recursive = */ false, "prebuilts/sdk/tools":/* recursive = */ false, "prebuilts/r8":/* recursive = */ false, "packages/apps/Music":/* recursive = */ true, + "packages/apps/QuickSearchBox":/* recursive = */ true, } // Configure modules in these directories to enable bp2build_available: true or false by default. @@ -265,12 +271,11 @@ var ( // Per-module denylist to opt modules out of mixed builds. Such modules will // still be generated via bp2build. mixedBuildsDisabledList = []string{ - "libbrotli", // http://b/198585397, ld.lld: error: bionic/libc/arch-arm64/generic/bionic/memmove.S:95:(.text+0x10): relocation R_AARCH64_CONDBR19 out of range: -1404176 is not in [-1048576, 1048575]; references __memcpy - "libc++fs", // http://b/198403271, Missing symbols/members in the global namespace when referenced from headers in //external/libcxx/includes - "libc++_experimental", // http://b/198403271, Missing symbols/members in the global namespace when referenced from headers in //external/libcxx/includes - "libc++_static", // http://b/198403271, Missing symbols/members in the global namespace when referenced from headers in //external/libcxx/includes - "libc++abi", // http://b/195970501, cc_library_static, duplicate symbols because it propagates libc objects. - "libc++demangle", // http://b/195970501, cc_library_static, duplicate symbols because it propagates libc objects. + "libbrotli", // http://b/198585397, ld.lld: error: bionic/libc/arch-arm64/generic/bionic/memmove.S:95:(.text+0x10): relocation R_AARCH64_CONDBR19 out of range: -1404176 is not in [-1048576, 1048575]; references __memcpy + "func_to_syscall_nrs", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module. + "libseccomp_policy_app_zygote_sources", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module. + "libseccomp_policy_app_sources", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module. + "libseccomp_policy_system_sources", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module. } // Used for quicker lookups diff --git a/android/neverallow.go b/android/neverallow.go index 19b58a775..a91d523b1 100644 --- a/android/neverallow.go +++ b/android/neverallow.go @@ -152,7 +152,7 @@ func createJavaDeviceForHostRules() []Rule { javaDeviceForHostProjectsAllowedList := []string{ "external/guava", "external/robolectric-shadows", - "framework/layoutlib", + "frameworks/layoutlib", } return []Rule{ diff --git a/android/sdk.go b/android/sdk.go index 100f63b0e..1d63d7a94 100644 --- a/android/sdk.go +++ b/android/sdk.go @@ -15,6 +15,7 @@ package android import ( + "fmt" "sort" "strings" @@ -376,6 +377,224 @@ func (b BpPrintableBase) bpPrintable() { var _ BpPrintable = BpPrintableBase{} +// sdkRegisterable defines the interface that must be implemented by objects that can be registered +// in an sdkRegistry. +type sdkRegisterable interface { + // SdkPropertyName returns the name of the corresponding property on an sdk module. + SdkPropertyName() string +} + +// sdkRegistry provides support for registering and retrieving objects that define properties for +// use by sdk and module_exports module types. +type sdkRegistry struct { + // The list of registered objects sorted by property name. + list []sdkRegisterable +} + +// copyAndAppend creates a new sdkRegistry that includes all the traits registered in +// this registry plus the supplied trait. +func (r *sdkRegistry) copyAndAppend(registerable sdkRegisterable) *sdkRegistry { + oldList := r.list + + // Make sure that list does not already contain the property. Uses a simple linear search instead + // of a binary search even though the list is sorted. That is because the number of items in the + // list is small and so not worth the overhead of a binary search. + found := false + newPropertyName := registerable.SdkPropertyName() + for _, r := range oldList { + if r.SdkPropertyName() == newPropertyName { + found = true + break + } + } + if found { + names := []string{} + for _, r := range oldList { + names = append(names, r.SdkPropertyName()) + } + panic(fmt.Errorf("duplicate properties found, %q already exists in %q", newPropertyName, names)) + } + + // Copy the slice just in case this is being read while being modified, e.g. when testing. + list := make([]sdkRegisterable, 0, len(oldList)+1) + list = append(list, oldList...) + list = append(list, registerable) + + // Sort the registered objects by their property name to ensure that registry order has no effect + // on behavior. + sort.Slice(list, func(i1, i2 int) bool { + t1 := list[i1] + t2 := list[i2] + + return t1.SdkPropertyName() < t2.SdkPropertyName() + }) + + // Create a new registry so the pointer uniquely identifies the set of registered types. + return &sdkRegistry{ + list: list, + } +} + +// registeredObjects returns the list of registered instances. +func (r *sdkRegistry) registeredObjects() []sdkRegisterable { + return r.list +} + +// uniqueOnceKey returns a key that uniquely identifies this instance and can be used with +// OncePer.Once +func (r *sdkRegistry) uniqueOnceKey() OnceKey { + // Use the pointer to the registry as the unique key. The pointer is used because it is guaranteed + // to uniquely identify the contained list. The list itself cannot be used as slices are not + // comparable. Using the pointer does mean that two separate registries with identical lists would + // have different keys and so cause whatever information is cached to be created multiple times. + // However, that is not an issue in practice as it should not occur outside tests. Constructing a + // string representation of the list to use instead would avoid that but is an unnecessary + // complication that provides no significant benefit. + return NewCustomOnceKey(r) +} + +// SdkMemberTrait represents a trait that members of an sdk module can contribute to the sdk +// snapshot. +// +// A trait is simply a characteristic of sdk member that is not required by default which may be +// required for some members but not others. Traits can cause additional information to be output +// to the sdk snapshot or replace the default information exported for a member with something else. +// e.g. +// * By default cc libraries only export the default image variants to the SDK. However, for some +// members it may be necessary to export specific image variants, e.g. vendor, or recovery. +// * By default cc libraries export all the configured architecture variants except for the native +// bridge architecture variants. However, for some members it may be necessary to export the +// native bridge architecture variants as well. +// * By default cc libraries export the platform variant (i.e. sdk:). However, for some members it +// may be necessary to export the sdk variant (i.e. sdk:sdk). +// +// A sdk can request a module to provide no traits, one trait or a collection of traits. The exact +// behavior of a trait is determined by how SdkMemberType implementations handle the traits. A trait +// could be specific to one SdkMemberType or many. Some trait combinations could be incompatible. +// +// The sdk module type will create a special traits structure that contains a property for each +// trait registered with RegisterSdkMemberTrait(). The property names are those returned from +// SdkPropertyName(). Each property contains a list of modules that are required to have that trait. +// e.g. something like this: +// +// sdk { +// name: "sdk", +// ... +// traits: { +// recovery_image: ["module1", "module4", "module5"], +// native_bridge: ["module1", "module2"], +// native_sdk: ["module1", "module3"], +// ... +// }, +// ... +// } +type SdkMemberTrait interface { + // SdkPropertyName returns the name of the traits property on an sdk module. + SdkPropertyName() string +} + +var _ sdkRegisterable = (SdkMemberTrait)(nil) + +// SdkMemberTraitBase is the base struct that must be embedded within any type that implements +// SdkMemberTrait. +type SdkMemberTraitBase struct { + // PropertyName is the name of the property + PropertyName string +} + +func (b *SdkMemberTraitBase) SdkPropertyName() string { + return b.PropertyName +} + +// SdkMemberTraitSet is a set of SdkMemberTrait instances. +type SdkMemberTraitSet interface { + // Empty returns true if this set is empty. + Empty() bool + + // Contains returns true if this set contains the specified trait. + Contains(trait SdkMemberTrait) bool + + // Subtract returns a new set containing all elements of this set except for those in the + // other set. + Subtract(other SdkMemberTraitSet) SdkMemberTraitSet + + // String returns a string representation of the set and its contents. + String() string +} + +func NewSdkMemberTraitSet(traits []SdkMemberTrait) SdkMemberTraitSet { + if len(traits) == 0 { + return EmptySdkMemberTraitSet() + } + + m := sdkMemberTraitSet{} + for _, trait := range traits { + m[trait] = true + } + return m +} + +func EmptySdkMemberTraitSet() SdkMemberTraitSet { + return (sdkMemberTraitSet)(nil) +} + +type sdkMemberTraitSet map[SdkMemberTrait]bool + +var _ SdkMemberTraitSet = (sdkMemberTraitSet{}) + +func (s sdkMemberTraitSet) Empty() bool { + return len(s) == 0 +} + +func (s sdkMemberTraitSet) Contains(trait SdkMemberTrait) bool { + return s[trait] +} + +func (s sdkMemberTraitSet) Subtract(other SdkMemberTraitSet) SdkMemberTraitSet { + if other.Empty() { + return s + } + + var remainder []SdkMemberTrait + for trait, _ := range s { + if !other.Contains(trait) { + remainder = append(remainder, trait) + } + } + + return NewSdkMemberTraitSet(remainder) +} + +func (s sdkMemberTraitSet) String() string { + list := []string{} + for trait, _ := range s { + list = append(list, trait.SdkPropertyName()) + } + sort.Strings(list) + return fmt.Sprintf("[%s]", strings.Join(list, ",")) +} + +var registeredSdkMemberTraits = &sdkRegistry{} + +// RegisteredSdkMemberTraits returns a OnceKey and a sorted list of registered traits. +// +// The key uniquely identifies the array of traits and can be used with OncePer.Once() to cache +// information derived from the array of traits. +func RegisteredSdkMemberTraits() (OnceKey, []SdkMemberTrait) { + registerables := registeredSdkMemberTraits.registeredObjects() + traits := make([]SdkMemberTrait, len(registerables)) + for i, registerable := range registerables { + traits[i] = registerable.(SdkMemberTrait) + } + return registeredSdkMemberTraits.uniqueOnceKey(), traits +} + +// RegisterSdkMemberTrait registers an SdkMemberTrait object to allow them to be used in the +// module_exports, module_exports_snapshot, sdk and sdk_snapshot module types. +func RegisterSdkMemberTrait(trait SdkMemberTrait) { + registeredSdkMemberTraits = registeredSdkMemberTraits.copyAndAppend(trait) +} + // SdkMember is an individual member of the SDK. // // It includes all of the variants that the SDK depends upon. @@ -541,12 +760,25 @@ type SdkMemberType interface { // CreateVariantPropertiesStruct creates a structure into which variant specific properties can be // added. CreateVariantPropertiesStruct() SdkMemberProperties + + // SupportedTraits returns the set of traits supported by this member type. + SupportedTraits() SdkMemberTraitSet } +var _ sdkRegisterable = (SdkMemberType)(nil) + // SdkDependencyContext provides access to information needed by the SdkMemberType.AddDependencies() // implementations. type SdkDependencyContext interface { BottomUpMutatorContext + + // RequiredTraits returns the set of SdkMemberTrait instances that the sdk requires the named + // member to provide. + RequiredTraits(name string) SdkMemberTraitSet + + // RequiresTrait returns true if the sdk requires the member with the supplied name to provide the + // supplied trait. + RequiresTrait(name string, trait SdkMemberTrait) bool } // SdkMemberTypeBase is the base type for SdkMemberType implementations and must be embedded in any @@ -565,6 +797,9 @@ type SdkMemberTypeBase struct { // module type in its SdkMemberType.AddPrebuiltModule() method. That prevents the sdk snapshot // code from automatically adding a prefer: true flag. UseSourceModuleTypeInSnapshot bool + + // The list of supported traits. + Traits []SdkMemberTrait } func (b *SdkMemberTypeBase) SdkPropertyName() string { @@ -587,60 +822,52 @@ func (b *SdkMemberTypeBase) UsesSourceModuleTypeInSnapshot() bool { return b.UseSourceModuleTypeInSnapshot } -// SdkMemberTypesRegistry encapsulates the information about registered SdkMemberTypes. -type SdkMemberTypesRegistry struct { - // The list of types sorted by property name. - list []SdkMemberType +func (b *SdkMemberTypeBase) SupportedTraits() SdkMemberTraitSet { + return NewSdkMemberTraitSet(b.Traits) } -func (r *SdkMemberTypesRegistry) copyAndAppend(memberType SdkMemberType) *SdkMemberTypesRegistry { - oldList := r.list - - // Copy the slice just in case this is being read while being modified, e.g. when testing. - list := make([]SdkMemberType, 0, len(oldList)+1) - list = append(list, oldList...) - list = append(list, memberType) - - // Sort the member types by their property name to ensure that registry order has no effect - // on behavior. - sort.Slice(list, func(i1, i2 int) bool { - t1 := list[i1] - t2 := list[i2] +// registeredModuleExportsMemberTypes is the set of registered SdkMemberTypes for module_exports +// modules. +var registeredModuleExportsMemberTypes = &sdkRegistry{} - return t1.SdkPropertyName() < t2.SdkPropertyName() - }) +// registeredSdkMemberTypes is the set of registered registeredSdkMemberTypes for sdk modules. +var registeredSdkMemberTypes = &sdkRegistry{} - // Create a new registry so the pointer uniquely identifies the set of registered types. - return &SdkMemberTypesRegistry{ - list: list, +// RegisteredSdkMemberTypes returns a OnceKey and a sorted list of registered types. +// +// If moduleExports is true then the slice of types includes all registered types that can be used +// with the module_exports and module_exports_snapshot module types. Otherwise, the slice of types +// only includes those registered types that can be used with the sdk and sdk_snapshot module +// types. +// +// The key uniquely identifies the array of types and can be used with OncePer.Once() to cache +// information derived from the array of types. +func RegisteredSdkMemberTypes(moduleExports bool) (OnceKey, []SdkMemberType) { + var registry *sdkRegistry + if moduleExports { + registry = registeredModuleExportsMemberTypes + } else { + registry = registeredSdkMemberTypes } -} - -func (r *SdkMemberTypesRegistry) RegisteredTypes() []SdkMemberType { - return r.list -} -func (r *SdkMemberTypesRegistry) UniqueOnceKey() OnceKey { - // Use the pointer to the registry as the unique key. - return NewCustomOnceKey(r) + registerables := registry.registeredObjects() + types := make([]SdkMemberType, len(registerables)) + for i, registerable := range registerables { + types[i] = registerable.(SdkMemberType) + } + return registry.uniqueOnceKey(), types } -// ModuleExportsMemberTypes is the set of registered SdkMemberTypes for module_exports modules. -var ModuleExportsMemberTypes = &SdkMemberTypesRegistry{} - -// SdkMemberTypes is the set of registered SdkMemberTypes for sdk modules. -var SdkMemberTypes = &SdkMemberTypesRegistry{} - // RegisterSdkMemberType registers an SdkMemberType object to allow them to be used in the // module_exports, module_exports_snapshot and (depending on the value returned from // SdkMemberType.UsableWithSdkAndSdkSnapshot) the sdk and sdk_snapshot module types. func RegisterSdkMemberType(memberType SdkMemberType) { // All member types are usable with module_exports. - ModuleExportsMemberTypes = ModuleExportsMemberTypes.copyAndAppend(memberType) + registeredModuleExportsMemberTypes = registeredModuleExportsMemberTypes.copyAndAppend(memberType) // Only those that explicitly indicate it are usable with sdk. if memberType.UsableWithSdkAndSdkSnapshot() { - SdkMemberTypes = SdkMemberTypes.copyAndAppend(memberType) + registeredSdkMemberTypes = registeredSdkMemberTypes.copyAndAppend(memberType) } } @@ -733,6 +960,9 @@ type SdkMemberContext interface { // Provided for use by sdk members to create a member specific location within the snapshot // into which to copy the prebuilt files. Name() string + + // RequiresTrait returns true if this member is expected to provide the specified trait. + RequiresTrait(trait SdkMemberTrait) bool } // ExportedComponentsInfo contains information about the components that this module exports to an diff --git a/android/sdk_test.go b/android/sdk_test.go new file mode 100644 index 000000000..51aeb314c --- /dev/null +++ b/android/sdk_test.go @@ -0,0 +1,53 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package android + +import "testing" + +type testSdkRegisterable struct { + name string +} + +func (t *testSdkRegisterable) SdkPropertyName() string { + return t.name +} + +var _ sdkRegisterable = &testSdkRegisterable{} + +func TestSdkRegistry(t *testing.T) { + alpha := &testSdkRegisterable{"alpha"} + beta := &testSdkRegisterable{"beta"} + betaDup := &testSdkRegisterable{"beta"} + + // Make sure that an empty registry is empty. + emptyRegistry := &sdkRegistry{} + AssertDeepEquals(t, "emptyRegistry should be empty", ([]sdkRegisterable)(nil), emptyRegistry.registeredObjects()) + + // Add beta to the empty registry to create another registry, check that it contains beta and make + // sure that it does not affect the creating registry. + registry1 := emptyRegistry.copyAndAppend(beta) + AssertDeepEquals(t, "emptyRegistry should still be empty", ([]sdkRegisterable)(nil), emptyRegistry.registeredObjects()) + AssertDeepEquals(t, "registry1 should contain beta", []sdkRegisterable{beta}, registry1.registeredObjects()) + + // Add alpha to the registry containing beta to create another registry, check that it contains + // alpha,beta (in order) and make sure that it does not affect the creating registry. + registry2 := registry1.copyAndAppend(alpha) + AssertDeepEquals(t, "registry1 should still contain beta", []sdkRegisterable{beta}, registry1.registeredObjects()) + AssertDeepEquals(t, "registry2 should contain alpha,beta", []sdkRegisterable{alpha, beta}, registry2.registeredObjects()) + + AssertPanicMessageContains(t, "duplicate beta should be detected", `"beta" already exists in ["alpha" "beta"]`, func() { + registry2.copyAndAppend(betaDup) + }) +} diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go index d731f3e49..0bd71c63a 100644 --- a/bazel/cquery/request_type.go +++ b/bazel/cquery/request_type.go @@ -111,14 +111,20 @@ staticLibraries = [] rootStaticArchives = [] linker_inputs = cc_info.linking_context.linker_inputs.to_list() -for linker_input in linker_inputs: - for library in linker_input.libraries: - for object in library.objects: - ccObjectFiles += [object.path] - if library.static_library: - staticLibraries.append(library.static_library.path) - if linker_input.owner == target.label: - rootStaticArchives.append(library.static_library.path) +static_info_tag = "//build/bazel/rules:cc_library_static.bzl%CcStaticLibraryInfo" +if static_info_tag in providers(target): + static_info = providers(target)[static_info_tag] + ccObjectFiles = [f.path for f in static_info.objects] + rootStaticArchives = [static_info.root_static_archive.path] +else: + for linker_input in linker_inputs: + for library in linker_input.libraries: + for object in library.objects: + ccObjectFiles += [object.path] + if library.static_library: + staticLibraries.append(library.static_library.path) + if linker_input.owner == target.label: + rootStaticArchives.append(library.static_library.path) rootDynamicLibraries = [] @@ -141,9 +147,10 @@ returns = [ system_includes, rootStaticArchives, rootDynamicLibraries, + [toc_file] ] -return "|".join([", ".join(r) for r in returns] + [toc_file])` +return "|".join([", ".join(r) for r in returns])` } // ParseResult returns a value obtained by parsing the result of the request's Starlark function. diff --git a/bp2build/Android.bp b/bp2build/Android.bp index 40526a623..1250e92d0 100644 --- a/bp2build/Android.bp +++ b/bp2build/Android.bp @@ -33,6 +33,7 @@ bootstrap_go_package { "apex_key_conversion_test.go", "build_conversion_test.go", "bzl_conversion_test.go", + "cc_genrule_conversion_test.go", "cc_library_conversion_test.go", "cc_library_headers_conversion_test.go", "cc_library_static_conversion_test.go", diff --git a/bp2build/cc_genrule_conversion_test.go b/bp2build/cc_genrule_conversion_test.go new file mode 100644 index 000000000..a7e9cb21a --- /dev/null +++ b/bp2build/cc_genrule_conversion_test.go @@ -0,0 +1,258 @@ +// Copyright 2021 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 bp2build + +import ( + "testing" + + "android/soong/android" + "android/soong/cc" + "android/soong/genrule" +) + +var otherCcGenruleBp = map[string]string{ + "other/Android.bp": `cc_genrule { + name: "foo.tool", + out: ["foo_tool.out"], + srcs: ["foo_tool.in"], + cmd: "cp $(in) $(out)", +} +cc_genrule { + name: "other.tool", + out: ["other_tool.out"], + srcs: ["other_tool.in"], + cmd: "cp $(in) $(out)", +}`, +} + +func runCcGenruleTestCase(t *testing.T, tc bp2buildTestCase) { + t.Helper() + runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc) +} + +func TestCliVariableReplacement(t *testing.T) { + runCcGenruleTestCase(t, bp2buildTestCase{ + description: "cc_genrule with command line variable replacements", + moduleTypeUnderTest: "cc_genrule", + moduleTypeUnderTestFactory: cc.GenRuleFactory, + moduleTypeUnderTestBp2BuildMutator: genrule.CcGenruleBp2Build, + blueprint: `cc_genrule { + name: "foo.tool", + out: ["foo_tool.out"], + srcs: ["foo_tool.in"], + cmd: "cp $(in) $(out)", + bazel_module: { bp2build_available: true }, +} + +cc_genrule { + name: "foo", + out: ["foo.out"], + srcs: ["foo.in"], + tools: [":foo.tool"], + cmd: "$(location :foo.tool) --genDir=$(genDir) arg $(in) $(out)", + bazel_module: { bp2build_available: true }, +}`, + expectedBazelTargets: []string{ + `genrule( + name = "foo", + cmd = "$(location :foo.tool) --genDir=$(RULEDIR) arg $(SRCS) $(OUTS)", + outs = ["foo.out"], + srcs = ["foo.in"], + tools = [":foo.tool"], +)`, + `genrule( + name = "foo.tool", + cmd = "cp $(SRCS) $(OUTS)", + outs = ["foo_tool.out"], + srcs = ["foo_tool.in"], +)`, + }, + }) +} + +func TestUsingLocationsLabel(t *testing.T) { + runCcGenruleTestCase(t, bp2buildTestCase{ + description: "cc_genrule using $(locations :label)", + moduleTypeUnderTest: "cc_genrule", + moduleTypeUnderTestFactory: cc.GenRuleFactory, + moduleTypeUnderTestBp2BuildMutator: genrule.CcGenruleBp2Build, + blueprint: `cc_genrule { + name: "foo.tools", + out: ["foo_tool.out", "foo_tool2.out"], + srcs: ["foo_tool.in"], + cmd: "cp $(in) $(out)", + bazel_module: { bp2build_available: true }, +} + +cc_genrule { + name: "foo", + out: ["foo.out"], + srcs: ["foo.in"], + tools: [":foo.tools"], + cmd: "$(locations :foo.tools) -s $(out) $(in)", + bazel_module: { bp2build_available: true }, +}`, + expectedBazelTargets: []string{`genrule( + name = "foo", + cmd = "$(locations :foo.tools) -s $(OUTS) $(SRCS)", + outs = ["foo.out"], + srcs = ["foo.in"], + tools = [":foo.tools"], +)`, + `genrule( + name = "foo.tools", + cmd = "cp $(SRCS) $(OUTS)", + outs = [ + "foo_tool.out", + "foo_tool2.out", + ], + srcs = ["foo_tool.in"], +)`, + }, + }) +} + +func TestUsingLocationsAbsoluteLabel(t *testing.T) { + runCcGenruleTestCase(t, bp2buildTestCase{ + description: "cc_genrule using $(locations //absolute:label)", + moduleTypeUnderTest: "cc_genrule", + moduleTypeUnderTestFactory: cc.GenRuleFactory, + moduleTypeUnderTestBp2BuildMutator: genrule.CcGenruleBp2Build, + blueprint: `cc_genrule { + name: "foo", + out: ["foo.out"], + srcs: ["foo.in"], + tool_files: [":foo.tool"], + cmd: "$(locations :foo.tool) -s $(out) $(in)", + bazel_module: { bp2build_available: true }, +}`, + expectedBazelTargets: []string{`genrule( + name = "foo", + cmd = "$(locations //other:foo.tool) -s $(OUTS) $(SRCS)", + outs = ["foo.out"], + srcs = ["foo.in"], + tools = ["//other:foo.tool"], +)`, + }, + filesystem: otherCcGenruleBp, + }) +} + +func TestSrcsUsingAbsoluteLabel(t *testing.T) { + runCcGenruleTestCase(t, bp2buildTestCase{ + description: "cc_genrule srcs using $(locations //absolute:label)", + moduleTypeUnderTest: "cc_genrule", + moduleTypeUnderTestFactory: cc.GenRuleFactory, + moduleTypeUnderTestBp2BuildMutator: genrule.CcGenruleBp2Build, + blueprint: `cc_genrule { + name: "foo", + out: ["foo.out"], + srcs: [":other.tool"], + tool_files: [":foo.tool"], + cmd: "$(locations :foo.tool) -s $(out) $(location :other.tool)", + bazel_module: { bp2build_available: true }, +}`, + expectedBazelTargets: []string{`genrule( + name = "foo", + cmd = "$(locations //other:foo.tool) -s $(OUTS) $(location //other:other.tool)", + outs = ["foo.out"], + srcs = ["//other:other.tool"], + tools = ["//other:foo.tool"], +)`, + }, + filesystem: otherCcGenruleBp, + }) +} + +func TestLocationsLabelUsesFirstToolFile(t *testing.T) { + runCcGenruleTestCase(t, bp2buildTestCase{ + description: "cc_genrule using $(location) label should substitute first tool label automatically", + moduleTypeUnderTest: "cc_genrule", + moduleTypeUnderTestFactory: cc.GenRuleFactory, + moduleTypeUnderTestBp2BuildMutator: genrule.CcGenruleBp2Build, + blueprint: `cc_genrule { + name: "foo", + out: ["foo.out"], + srcs: ["foo.in"], + tool_files: [":foo.tool", ":other.tool"], + cmd: "$(location) -s $(out) $(in)", + bazel_module: { bp2build_available: true }, +}`, + expectedBazelTargets: []string{`genrule( + name = "foo", + cmd = "$(location //other:foo.tool) -s $(OUTS) $(SRCS)", + outs = ["foo.out"], + srcs = ["foo.in"], + tools = [ + "//other:foo.tool", + "//other:other.tool", + ], +)`, + }, + filesystem: otherCcGenruleBp, + }) +} + +func TestLocationsLabelUsesFirstTool(t *testing.T) { + runCcGenruleTestCase(t, bp2buildTestCase{ + description: "cc_genrule using $(locations) label should substitute first tool label automatically", + moduleTypeUnderTest: "cc_genrule", + moduleTypeUnderTestFactory: cc.GenRuleFactory, + moduleTypeUnderTestBp2BuildMutator: genrule.CcGenruleBp2Build, + blueprint: `cc_genrule { + name: "foo", + out: ["foo.out"], + srcs: ["foo.in"], + tools: [":foo.tool", ":other.tool"], + cmd: "$(locations) -s $(out) $(in)", + bazel_module: { bp2build_available: true }, +}`, + expectedBazelTargets: []string{`genrule( + name = "foo", + cmd = "$(locations //other:foo.tool) -s $(OUTS) $(SRCS)", + outs = ["foo.out"], + srcs = ["foo.in"], + tools = [ + "//other:foo.tool", + "//other:other.tool", + ], +)`, + }, + filesystem: otherCcGenruleBp, + }) +} + +func TestWithoutToolsOrToolFiles(t *testing.T) { + runCcGenruleTestCase(t, bp2buildTestCase{ + description: "cc_genrule without tools or tool_files can convert successfully", + moduleTypeUnderTest: "cc_genrule", + moduleTypeUnderTestFactory: cc.GenRuleFactory, + moduleTypeUnderTestBp2BuildMutator: genrule.CcGenruleBp2Build, + blueprint: `cc_genrule { + name: "foo", + out: ["foo.out"], + srcs: ["foo.in"], + cmd: "cp $(in) $(out)", + bazel_module: { bp2build_available: true }, +}`, + expectedBazelTargets: []string{`genrule( + name = "foo", + cmd = "cp $(SRCS) $(OUTS)", + outs = ["foo.out"], + srcs = ["foo.in"], +)`, + }, + }) +} diff --git a/cc/Android.bp b/cc/Android.bp index bff27610b..190d55ec6 100644 --- a/cc/Android.bp +++ b/cc/Android.bp @@ -63,6 +63,7 @@ bootstrap_go_package { "library.go", "library_headers.go", "library_sdk_member.go", + "native_bridge_sdk_trait.go", "object.go", "test.go", "toolchain_library.go", @@ -1711,7 +1711,9 @@ func (c *Module) setSubnameProperty(actx android.ModuleContext) { func (c *Module) maybeGenerateBazelActions(actx android.ModuleContext) bool { bazelModuleLabel := c.GetBazelLabel(actx, c) bazelActionsUsed := false - if c.MixedBuildsEnabled(actx) && c.bazelHandler != nil { + // Mixed builds mode is disabled for modules outside of device OS. + // TODO(b/200841190): Support non-device OS in mixed builds. + if c.MixedBuildsEnabled(actx) && c.bazelHandler != nil && actx.Os().Class == android.Device { bazelActionsUsed = c.bazelHandler.GenerateBazelBuildActions(actx, bazelModuleLabel) } return bazelActionsUsed diff --git a/cc/config/vndk.go b/cc/config/vndk.go index d4fcf7cac..e72efae87 100644 --- a/cc/config/vndk.go +++ b/cc/config/vndk.go @@ -47,17 +47,29 @@ var VndkMustUseVendorVariantList = []string{ "android.hardware.power-V1-ndk", "android.hardware.power-V1-ndk_platform", "android.hardware.power-ndk_platform", - "android.hardware.rebootescrow-V1-ndk", - "android.hardware.rebootescrow-V1-ndk_platform", "android.hardware.power.stats-V1-ndk", "android.hardware.power.stats-V1-ndk_platform", "android.hardware.power.stats-ndk_platform", "android.hardware.power.stats-unstable-ndk_platform", + "android.hardware.rebootescrow-V1-ndk", + "android.hardware.rebootescrow-V1-ndk_platform", + "android.hardware.rebootescrow-ndk_platform", "android.hardware.radio-V1-ndk", "android.hardware.radio-V1-ndk_platform", "android.hardware.radio.config-V1-ndk", "android.hardware.radio.config-V1-ndk_platform", - "android.hardware.rebootescrow-ndk_platform", + "android.hardware.radio.data-V1-ndk", + "android.hardware.radio.data-V1-ndk_platform", + "android.hardware.radio.messaging-V1-ndk", + "android.hardware.radio.messaging-V1-ndk_platform", + "android.hardware.radio.modem-V1-ndk", + "android.hardware.radio.modem-V1-ndk_platform", + "android.hardware.radio.network-V1-ndk", + "android.hardware.radio.network-V1-ndk_platform", + "android.hardware.radio.sim-V1-ndk", + "android.hardware.radio.sim-V1-ndk_platform", + "android.hardware.radio.voice-V1-ndk", + "android.hardware.radio.voice-V1-ndk_platform", "android.hardware.security.keymint-V1-ndk", "android.hardware.security.keymint-V1-ndk_platform", "android.hardware.security.keymint-ndk_platform", diff --git a/cc/genrule.go b/cc/genrule.go index 0ca901e61..9df52280e 100644 --- a/cc/genrule.go +++ b/cc/genrule.go @@ -21,7 +21,7 @@ import ( ) func init() { - android.RegisterModuleType("cc_genrule", genRuleFactory) + android.RegisterModuleType("cc_genrule", GenRuleFactory) } type GenruleExtraProperties struct { @@ -37,7 +37,7 @@ type GenruleExtraProperties struct { // cc_genrule is a genrule that can depend on other cc_* objects. // The cmd may be run multiple times, once for each of the different arch/etc // variations. -func genRuleFactory() android.Module { +func GenRuleFactory() android.Module { module := genrule.NewGenRule() extra := &GenruleExtraProperties{} @@ -48,6 +48,7 @@ func genRuleFactory() android.Module { android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibBoth) android.InitApexModule(module) + android.InitBazelModule(module) return module } diff --git a/cc/genrule_test.go b/cc/genrule_test.go index 45b343b5a..b6afb05a7 100644 --- a/cc/genrule_test.go +++ b/cc/genrule_test.go @@ -23,7 +23,7 @@ import ( func testGenruleContext(config android.Config) *android.TestContext { ctx := android.NewTestArchContext(config) - ctx.RegisterModuleType("cc_genrule", genRuleFactory) + ctx.RegisterModuleType("cc_genrule", GenRuleFactory) ctx.Register() return ctx diff --git a/cc/library_headers.go b/cc/library_headers.go index 14b90c1e5..b335035c3 100644 --- a/cc/library_headers.go +++ b/cc/library_headers.go @@ -33,6 +33,9 @@ var headersLibrarySdkMemberType = &librarySdkMemberType{ PropertyName: "native_header_libs", SupportsSdk: true, HostOsDependent: true, + Traits: []android.SdkMemberTrait{ + nativeBridgeSdkTrait, + }, }, prebuiltModuleType: "cc_prebuilt_library_headers", noOutputFiles: true, diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go index 559e94004..bed7954b2 100644 --- a/cc/library_sdk_member.go +++ b/cc/library_sdk_member.go @@ -75,8 +75,33 @@ type librarySdkMemberType struct { } func (mt *librarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) { - targets := ctx.MultiTargets() + // The base set of targets which does not include native bridge targets. + defaultTargets := ctx.MultiTargets() + + // The lazily created list of native bridge targets. + var includeNativeBridgeTargets []android.Target + for _, lib := range names { + targets := defaultTargets + + // If native bridge support is required in the sdk snapshot then add native bridge targets to + // the basic list of targets that are required. + nativeBridgeSupport := ctx.RequiresTrait(lib, nativeBridgeSdkTrait) + if nativeBridgeSupport && ctx.Device() { + // If not already computed then compute the list of native bridge targets. + if includeNativeBridgeTargets == nil { + includeNativeBridgeTargets = append([]android.Target{}, defaultTargets...) + allAndroidTargets := ctx.Config().Targets[android.Android] + for _, possibleNativeBridgeTarget := range allAndroidTargets { + if possibleNativeBridgeTarget.NativeBridge == android.NativeBridgeEnabled { + includeNativeBridgeTargets = append(includeNativeBridgeTargets, possibleNativeBridgeTarget) + } + } + } + + // Include the native bridge targets as well. + targets = includeNativeBridgeTargets + } for _, target := range targets { name, version := StubsLibNameAndVersion(lib) if version == "" { @@ -122,6 +147,10 @@ func (mt *librarySdkMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, ccModule := member.Variants()[0].(*Module) + if ctx.RequiresTrait(nativeBridgeSdkTrait) { + pbm.AddProperty("native_bridge_supported", true) + } + if proptools.Bool(ccModule.Properties.Recovery_available) { pbm.AddProperty("recovery_available", true) } @@ -436,7 +465,11 @@ func (p *nativeLibInfoProperties) PopulateFromVariant(ctx android.SdkMemberConte exportedIncludeDirs, exportedGeneratedIncludeDirs := android.FilterPathListPredicate( exportedInfo.IncludeDirs, isGeneratedHeaderDirectory) - p.archSubDir = ccModule.Target().Arch.ArchType.String() + target := ccModule.Target() + p.archSubDir = target.Arch.ArchType.String() + if target.NativeBridge == android.NativeBridgeEnabled { + p.archSubDir += "_native_bridge" + } // Make sure that the include directories are unique. p.ExportedIncludeDirs = android.FirstUniquePaths(exportedIncludeDirs) diff --git a/cc/native_bridge_sdk_trait.go b/cc/native_bridge_sdk_trait.go new file mode 100644 index 000000000..1326d5750 --- /dev/null +++ b/cc/native_bridge_sdk_trait.go @@ -0,0 +1,33 @@ +// Copyright 2021 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" + +// This file contains support for the native bridge sdk trait. + +func init() { + android.RegisterSdkMemberTrait(nativeBridgeSdkTrait) +} + +type nativeBridgeSdkTraitStruct struct { + android.SdkMemberTraitBase +} + +var nativeBridgeSdkTrait android.SdkMemberTrait = &nativeBridgeSdkTraitStruct{ + SdkMemberTraitBase: android.SdkMemberTraitBase{ + PropertyName: "native_bridge_support", + }, +} diff --git a/cc/testing.go b/cc/testing.go index d0dca6b53..b0a220cff 100644 --- a/cc/testing.go +++ b/cc/testing.go @@ -33,7 +33,7 @@ func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) { ctx.RegisterModuleType("toolchain_library", ToolchainLibraryFactory) ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory) ctx.RegisterModuleType("cc_object", ObjectFactory) - ctx.RegisterModuleType("cc_genrule", genRuleFactory) + ctx.RegisterModuleType("cc_genrule", GenRuleFactory) ctx.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory) ctx.RegisterModuleType("ndk_prebuilt_static_stl", NdkPrebuiltStaticStlFactory) ctx.RegisterModuleType("ndk_prebuilt_object", NdkPrebuiltObjectFactory) diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go index fa63b465d..3c9cac190 100644 --- a/cmd/multiproduct_kati/main.go +++ b/cmd/multiproduct_kati/main.go @@ -218,10 +218,16 @@ func distDir(outDir string) string { } } +func forceAnsiOutput() bool { + value := os.Getenv("SOONG_UI_ANSI_OUTPUT") + return value == "1" || value == "y" || value == "yes" || value == "on" || value == "true" +} + func main() { stdio := terminal.StdioImpl{} - output := terminal.NewStatusOutput(stdio.Stdout(), "", false, false) + output := terminal.NewStatusOutput(stdio.Stdout(), "", false, false, + forceAnsiOutput()) log := logger.New(output) defer log.Cleanup() diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp index 703a8759a..e85163e20 100644 --- a/cmd/soong_build/Android.bp +++ b/cmd/soong_build/Android.bp @@ -22,6 +22,7 @@ blueprint_go_binary { "blueprint", "blueprint-bootstrap", "golang-protobuf-proto", + "golang-protobuf-android", "soong", "soong-android", "soong-bp2build", diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go index 09a223473..c5e889664 100644 --- a/cmd/soong_build/main.go +++ b/cmd/soong_build/main.go @@ -23,14 +23,14 @@ import ( "strings" "time" + "android/soong/android" "android/soong/bp2build" "android/soong/shared" "github.com/google/blueprint/bootstrap" "github.com/google/blueprint/deptools" "github.com/google/blueprint/pathtools" - - "android/soong/android" + androidProtobuf "google.golang.org/protobuf/android" ) var ( @@ -85,6 +85,12 @@ func init() { // Flags that probably shouldn't be flags of soong_build but we haven't found // the time to remove them yet flag.BoolVar(&runGoTests, "t", false, "build and run go tests during bootstrap") + + // Disable deterministic randomization in the protobuf package, so incremental + // builds with unrelated Soong changes don't trigger large rebuilds (since we + // write out text protos in command lines, and command line changes trigger + // rebuilds). + androidProtobuf.DisableRand() } func newNameResolver(config android.Config) *android.NameResolver { diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go index d70978727..9ee373e79 100644 --- a/cmd/soong_ui/main.go +++ b/cmd/soong_ui/main.go @@ -164,7 +164,8 @@ func main() { // Create a terminal output that mimics Ninja's. output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput, - build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD")) + build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"), + build.OsEnvironment().IsEnvTrue("SOONG_UI_ANSI_OUTPUT")) // Attach a new logger instance to the terminal output. log := logger.New(output) diff --git a/cuj/cuj.go b/cuj/cuj.go index c671f6f09..869e0f7b1 100644 --- a/cuj/cuj.go +++ b/cuj/cuj.go @@ -48,7 +48,7 @@ type TestResults struct { // Run runs a single build command. It emulates the "m" command line by calling into Soong UI directly. func (t *Test) Run(logsDir string) { - output := terminal.NewStatusOutput(os.Stdout, "", false, false) + output := terminal.NewStatusOutput(os.Stdout, "", false, false, false) log := logger.New(output) defer log.Cleanup() diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go index 7733c1b43..965b7552d 100644 --- a/dexpreopt/dexpreopt.go +++ b/dexpreopt/dexpreopt.go @@ -261,10 +261,18 @@ func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, g clcTarget = append(clcTarget, GetSystemServerDexLocation(global, lib)) } - // Copy the system server jar to a predefined location where dex2oat will find it. - dexPathHost := SystemServerDexJarHostPath(ctx, module.Name) - rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String())) - rule.Command().Text("cp -f").Input(module.DexPath).Output(dexPathHost) + if DexpreoptRunningInSoong { + // Copy the system server jar to a predefined location where dex2oat will find it. + dexPathHost := SystemServerDexJarHostPath(ctx, module.Name) + rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String())) + rule.Command().Text("cp -f").Input(module.DexPath).Output(dexPathHost) + } else { + // For Make modules the copy rule is generated in the makefiles, not in dexpreopt.sh. + // This is necessary to expose the rule to Ninja, otherwise it has rules that depend on + // the jar (namely, dexpreopt commands for all subsequent system server jars that have + // this one in their class loader context), but no rule that creates it (because Ninja + // cannot see the rule in the generated dexpreopt.sh script). + } checkSystemServerOrder(ctx, jarIndex) diff --git a/genrule/genrule.go b/genrule/genrule.go index bde6e9772..f4bde703a 100644 --- a/genrule/genrule.go +++ b/genrule/genrule.go @@ -69,6 +69,7 @@ func RegisterGenruleBuildComponents(ctx android.RegistrationContext) { }) android.RegisterBp2BuildMutator("genrule", GenruleBp2Build) + android.RegisterBp2BuildMutator("cc_genrule", CcGenruleBp2Build) } func RegisterGenruleBp2BuildDeps(ctx android.RegisterMutatorsContext) { @@ -826,6 +827,22 @@ type bazelGenruleAttributes struct { Cmd string } +// CcGenruleBp2Build is for cc_genrule. +func CcGenruleBp2Build(ctx android.TopDownMutatorContext) { + m, ok := ctx.Module().(*Module) + if !ok || !m.ConvertWithBp2build(ctx) { + return + } + + if ctx.ModuleType() != "cc_genrule" { + // Not a cc_genrule. + return + } + + genruleBp2Build(ctx) +} + +// GenruleBp2Build is used for genrule. func GenruleBp2Build(ctx android.TopDownMutatorContext) { m, ok := ctx.Module().(*Module) if !ok || !m.ConvertWithBp2build(ctx) { @@ -833,10 +850,15 @@ func GenruleBp2Build(ctx android.TopDownMutatorContext) { } if ctx.ModuleType() != "genrule" { - // Not a regular genrule. Could be a cc_genrule or java_genrule. + // Not a regular genrule. return } + genruleBp2Build(ctx) +} + +func genruleBp2Build(ctx android.TopDownMutatorContext) { + m, _ := ctx.Module().(*Module) // Bazel only has the "tools" attribute. tools_prop := android.BazelLabelForModuleDeps(ctx, m.properties.Tools) tool_files_prop := android.BazelLabelForModuleSrc(ctx, m.properties.Tool_files) @@ -854,7 +876,11 @@ func GenruleBp2Build(ctx android.TopDownMutatorContext) { if m.properties.Cmd != nil { cmd = strings.Replace(*m.properties.Cmd, "$(in)", "$(SRCS)", -1) cmd = strings.Replace(cmd, "$(out)", "$(OUTS)", -1) - cmd = strings.Replace(cmd, "$(genDir)", "$(GENDIR)", -1) + genDir := "$(GENDIR)" + if ctx.ModuleType() == "cc_genrule" { + genDir = "$(RULEDIR)" + } + cmd = strings.Replace(cmd, "$(genDir)", genDir, -1) if len(tools.Value.Includes) > 0 { cmd = strings.Replace(cmd, "$(location)", fmt.Sprintf("$(location %s)", tools.Value.Includes[0].Label), -1) cmd = strings.Replace(cmd, "$(locations)", fmt.Sprintf("$(locations %s)", tools.Value.Includes[0].Label), -1) diff --git a/java/droidstubs.go b/java/droidstubs.go index ec1b04a06..7fd88fceb 100644 --- a/java/droidstubs.go +++ b/java/droidstubs.go @@ -156,6 +156,7 @@ type ApiStubsSrcProvider interface { // Provider of information about API stubs, used by java_sdk_library. type ApiStubsProvider interface { + AnnotationsZip() android.Path ApiFilePath RemovedApiFilePath() android.Path @@ -210,6 +211,10 @@ func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) { } } +func (d *Droidstubs) AnnotationsZip() android.Path { + return d.annotationsZip +} + func (d *Droidstubs) ApiFilePath() android.Path { return d.apiFilePath } diff --git a/java/java.go b/java/java.go index 2ca4ac8f0..4a4486658 100644 --- a/java/java.go +++ b/java/java.go @@ -533,6 +533,10 @@ func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"), j.Stem()+".jar", j.outputFile, extraInstallDeps...) } + + if ctx.Windows() { + j.HideFromMake() + } } func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) { @@ -1030,14 +1034,14 @@ func InitTestHost(th *TestHost, installable *bool, testSuites []string, autoGenC type binaryProperties struct { // installable script to execute the resulting jar - Wrapper *string `android:"path"` + Wrapper *string `android:"path,arch_variant"` // Name of the class containing main to be inserted into the manifest as Main-Class. Main_class *string // Names of modules containing JNI libraries that should be installed alongside the host // variant of the binary. - Jni_libs []string + Jni_libs []string `android:"arch_variant"` } type Binary struct { @@ -1075,14 +1079,27 @@ func (j *Binary) GenerateAndroidBuildActions(ctx android.ModuleContext) { if j.binaryProperties.Wrapper != nil { j.wrapperFile = android.PathForModuleSrc(ctx, *j.binaryProperties.Wrapper) } else { + if ctx.Windows() { + ctx.PropertyErrorf("wrapper", "wrapper is required for Windows") + } + j.wrapperFile = android.PathForSource(ctx, "build/soong/scripts/jar-wrapper.sh") } + ext := "" + if ctx.Windows() { + ext = ".bat" + } + // The host installation rules make the installed wrapper depend on all the dependencies // of the wrapper variant, which will include the common variant's jar file and any JNI // libraries. This is verified by TestBinary. j.binaryFile = ctx.InstallExecutable(android.PathForModuleInstall(ctx, "bin"), - ctx.ModuleName(), j.wrapperFile) + ctx.ModuleName()+ext, j.wrapperFile) + } + + if ctx.Windows() { + j.HideFromMake() } } @@ -1283,6 +1300,10 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.hideApexVariantFromMake = true } + if ctx.Windows() { + j.HideFromMake() + } + jars := android.PathsForModuleSrc(ctx, j.properties.Jars) jarName := j.Stem() + ".jar" diff --git a/java/robolectric.go b/java/robolectric.go index a0c9c7fcd..5d62aee6c 100644 --- a/java/robolectric.go +++ b/java/robolectric.go @@ -417,10 +417,10 @@ func (r *robolectricRuntimes) GenerateAndroidBuildActions(ctx android.ModuleCont } runtimeFromSourceJar := android.OutputFileForModule(ctx, runtimeFromSourceModule, "") - // TODO(murj) Update this to ctx.Config().PlatformSdkCodename() once the platform - // classes like android.os.Build are updated to S. - runtimeName := fmt.Sprintf("android-all-%s-robolectric-r0.jar", - "R") + // "TREE" name is essential here because it hooks into the "TREE" name in + // Robolectric's SdkConfig.java that will always correspond to the NEWEST_SDK + // in Robolectric configs. + runtimeName := "android-all-current-robolectric-r0.jar" installedRuntime := ctx.InstallFile(androidAllDir, runtimeName, runtimeFromSourceJar) r.runtimes = append(r.runtimes, installedRuntime) } diff --git a/java/sdk_library.go b/java/sdk_library.go index 2d8aef74c..d46952233 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -550,6 +550,9 @@ type scopePaths struct { // The stubs source jar. stubsSrcJar android.OptionalPath + + // Extracted annotations. + annotationsZip android.OptionalPath } func (paths *scopePaths) extractStubsLibraryInfoFromDependency(ctx android.ModuleContext, dep android.Module) error { @@ -585,6 +588,7 @@ func (paths *scopePaths) treatDepAsApiStubsSrcProvider(dep android.Module, actio } func (paths *scopePaths) extractApiInfoFromApiStubsProvider(provider ApiStubsProvider) { + paths.annotationsZip = android.OptionalPathForPath(provider.AnnotationsZip()) paths.currentApiFilePath = android.OptionalPathForPath(provider.ApiFilePath()) paths.removedApiFilePath = android.OptionalPathForPath(provider.RemovedApiFilePath()) } @@ -739,6 +743,8 @@ const ( apiTxtComponentName = "api.txt" removedApiTxtComponentName = "removed-api.txt" + + annotationsComponentName = "annotations.zip" ) // A regular expression to match tags that reference a specific stubs component. @@ -757,7 +763,7 @@ var tagSplitter = func() *regexp.Regexp { scopesRegexp := choice(allScopeNames...) // Regular expression to match one of the components. - componentsRegexp := choice(stubsSourceComponentName, apiTxtComponentName, removedApiTxtComponentName) + componentsRegexp := choice(stubsSourceComponentName, apiTxtComponentName, removedApiTxtComponentName, annotationsComponentName) // Regular expression to match any combination of one scope and one component. return regexp.MustCompile(fmt.Sprintf(`^\.(%s)\.(%s)$`, scopesRegexp, componentsRegexp)) @@ -765,9 +771,7 @@ var tagSplitter = func() *regexp.Regexp { // For OutputFileProducer interface // -// .<scope>.stubs.source -// .<scope>.api.txt -// .<scope>.removed-api.txt +// .<scope>.<component name>, for all ComponentNames (for example: .public.removed-api.txt) func (c *commonToSdkLibraryAndImport) commonOutputFiles(tag string) (android.Paths, error) { if groups := tagSplitter.FindStringSubmatch(tag); groups != nil { scopeName := groups[1] @@ -794,6 +798,11 @@ func (c *commonToSdkLibraryAndImport) commonOutputFiles(tag string) (android.Pat if paths.removedApiFilePath.Valid() { return android.Paths{paths.removedApiFilePath.Path()}, nil } + + case annotationsComponentName: + if paths.annotationsZip.Valid() { + return android.Paths{paths.annotationsZip.Path()}, nil + } } return nil, fmt.Errorf("%s not available for api scope %s", component, scopeName) @@ -1888,6 +1897,9 @@ type sdkLibraryScopeProperties struct { // The removed.txt Removed_api *string `android:"path"` + + // Annotation zip + Annotations *string `android:"path"` } type sdkLibraryImportProperties struct { @@ -2201,6 +2213,7 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo } paths := module.getScopePathsCreateIfNeeded(apiScope) + paths.annotationsZip = android.OptionalPathForModuleSrc(ctx, scopeProperties.Annotations) paths.currentApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Current_api) paths.removedApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Removed_api) } @@ -2551,6 +2564,7 @@ type scopeProperties struct { StubsSrcJar android.Path CurrentApiFile android.Path RemovedApiFile android.Path + AnnotationsZip android.Path SdkVersion string } @@ -2576,6 +2590,10 @@ func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMembe if paths.removedApiFilePath.Valid() { properties.RemovedApiFile = paths.removedApiFilePath.Path() } + // The annotations zip is only available for modules that set annotations_enabled: true. + if paths.annotationsZip.Valid() { + properties.AnnotationsZip = paths.annotationsZip.Path() + } s.Scopes[apiScope] = properties } } @@ -2640,6 +2658,12 @@ func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberCo scopeSet.AddProperty("removed_api", removedApiSnapshotPath) } + if properties.AnnotationsZip != nil { + annotationsSnapshotPath := filepath.Join(scopeDir, ctx.Name()+"_annotations.zip") + ctx.SnapshotBuilder().CopyToSnapshot(properties.AnnotationsZip, annotationsSnapshotPath) + scopeSet.AddProperty("annotations", annotationsSnapshotPath) + } + if properties.SdkVersion != "" { scopeSet.AddProperty("sdk_version", properties.SdkVersion) } diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go index d6c0946b1..be23536ea 100644 --- a/java/sdk_library_test.go +++ b/java/sdk_library_test.go @@ -248,7 +248,7 @@ func TestJavaSdkLibrary_DoNotAccessImplWhenItIsNotBuilt(t *testing.T) { } } -func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) { +func TestJavaSdkLibrary_AccessOutputFiles(t *testing.T) { android.GroupFixturePreparers( prepareForJavaTest, PrepareForTestWithJavaSdkLibraryFiles, @@ -258,6 +258,31 @@ func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) { name: "foo", srcs: ["a.java"], api_packages: ["foo"], + annotations_enabled: true, + public: { + enabled: true, + }, + } + java_library { + name: "bar", + srcs: ["b.java", ":foo{.public.stubs.source}"], + java_resources: [":foo{.public.annotations.zip}"], + } + `) +} + +func TestJavaSdkLibrary_AccessOutputFiles_NoAnnotations(t *testing.T) { + android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis("foo"), + ). + ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": path dependency ":foo{.public.annotations.zip}": annotations.zip not available for api scope public`)). + RunTestWithBp(t, ` + java_sdk_library { + name: "foo", + srcs: ["a.java"], + api_packages: ["foo"], public: { enabled: true, }, @@ -266,6 +291,7 @@ func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) { java_library { name: "bar", srcs: ["b.java", ":foo{.public.stubs.source}"], + java_resources: [":foo{.public.annotations.zip}"], } `) } @@ -329,6 +355,7 @@ func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) { stub_srcs: ["a.java"], current_api: "api/current.txt", removed_api: "api/removed.txt", + annotations: "x/annotations.zip", }, } @@ -338,6 +365,7 @@ func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) { java_resources: [ ":foo{.public.api.txt}", ":foo{.public.removed-api.txt}", + ":foo{.public.annotations.zip}", ], } `) diff --git a/java/testing.go b/java/testing.go index a642753c4..99d55a054 100644 --- a/java/testing.go +++ b/java/testing.go @@ -300,6 +300,7 @@ func gatherRequiredDepsForTest() string { "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8", "kotlin-annotations", + "stub-annotations", } for _, extra := range extraModules { diff --git a/rust/compiler.go b/rust/compiler.go index 7bd9af4a8..1ce71f60b 100644 --- a/rust/compiler.go +++ b/rust/compiler.go @@ -231,6 +231,7 @@ func (compiler *baseCompiler) cfgsToFlags() []string { for _, cfg := range compiler.Properties.Cfgs { flags = append(flags, "--cfg '"+cfg+"'") } + return flags } @@ -239,6 +240,24 @@ func (compiler *baseCompiler) featuresToFlags() []string { for _, feature := range compiler.Properties.Features { flags = append(flags, "--cfg 'feature=\""+feature+"\"'") } + + return flags +} + +func (compiler *baseCompiler) featureFlags(ctx ModuleContext, flags Flags) Flags { + flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags()...) + flags.RustdocFlags = append(flags.RustdocFlags, compiler.featuresToFlags()...) + + return flags +} + +func (compiler *baseCompiler) cfgFlags(ctx ModuleContext, flags Flags) Flags { + if ctx.RustModule().UseVndk() { + compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_vndk") + } + + flags.RustFlags = append(flags.RustFlags, compiler.cfgsToFlags()...) + flags.RustdocFlags = append(flags.RustdocFlags, compiler.cfgsToFlags()...) return flags } @@ -269,10 +288,6 @@ func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags) Flag flags.RustFlags = append(flags.RustFlags, lintFlags) flags.RustFlags = append(flags.RustFlags, compiler.Properties.Flags...) - flags.RustFlags = append(flags.RustFlags, compiler.cfgsToFlags()...) - flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags()...) - flags.RustdocFlags = append(flags.RustdocFlags, compiler.cfgsToFlags()...) - flags.RustdocFlags = append(flags.RustdocFlags, compiler.featuresToFlags()...) flags.RustFlags = append(flags.RustFlags, "--edition="+compiler.edition()) flags.RustdocFlags = append(flags.RustdocFlags, "--edition="+compiler.edition()) flags.LinkFlags = append(flags.LinkFlags, compiler.Properties.Ld_flags...) @@ -296,10 +311,6 @@ func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags) Flag flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpathPrefix+"../"+rpath) } - if ctx.RustModule().UseVndk() { - flags.RustFlags = append(flags.RustFlags, "--cfg 'android_vndk'") - } - return flags } diff --git a/rust/library.go b/rust/library.go index 8c10e298b..38dae4d33 100644 --- a/rust/library.go +++ b/rust/library.go @@ -430,15 +430,25 @@ func (library *libraryDecorator) sharedLibFilename(ctx ModuleContext) string { return library.getStem(ctx) + ctx.toolchain().SharedLibSuffix() } -func (library *libraryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags { - flags.RustFlags = append(flags.RustFlags, "-C metadata="+ctx.ModuleName()) +func (library *libraryDecorator) cfgFlags(ctx ModuleContext, flags Flags) Flags { + flags = library.baseCompiler.cfgFlags(ctx, flags) if library.dylib() { // We need to add a dependency on std in order to link crates as dylibs. // The hack to add this dependency is guarded by the following cfg so // that we don't force a dependency when it isn't needed. library.baseCompiler.Properties.Cfgs = append(library.baseCompiler.Properties.Cfgs, "android_dylib") } + + flags.RustFlags = append(flags.RustFlags, library.baseCompiler.cfgsToFlags()...) + flags.RustdocFlags = append(flags.RustdocFlags, library.baseCompiler.cfgsToFlags()...) + + return flags +} + +func (library *libraryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags { flags = library.baseCompiler.compilerFlags(ctx, flags) + + flags.RustFlags = append(flags.RustFlags, "-C metadata="+ctx.ModuleName()) if library.shared() || library.static() { library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Include_dirs)...) } diff --git a/rust/rust.go b/rust/rust.go index 0cd299dc1..0a7d68dee 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -436,6 +436,8 @@ type RustLibrary struct { type compiler interface { initialize(ctx ModuleContext) compilerFlags(ctx ModuleContext, flags Flags) Flags + cfgFlags(ctx ModuleContext, flags Flags) Flags + featureFlags(ctx ModuleContext, flags Flags) Flags compilerProps() []interface{} compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path compilerDeps(ctx DepsContext, deps Deps) Deps @@ -847,8 +849,11 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { Toolchain: toolchain, } + // Calculate rustc flags if mod.compiler != nil { flags = mod.compiler.compilerFlags(ctx, flags) + flags = mod.compiler.cfgFlags(ctx, flags) + flags = mod.compiler.featureFlags(ctx, flags) } if mod.coverage != nil { flags, deps = mod.coverage.flags(ctx, flags, deps) diff --git a/sdk/Android.bp b/sdk/Android.bp index 0c9bf27fd..c6544d68a 100644 --- a/sdk/Android.bp +++ b/sdk/Android.bp @@ -16,6 +16,7 @@ bootstrap_go_package { srcs: [ "bp.go", "exports.go", + "member_trait.go", "member_type.go", "sdk.go", "update.go", @@ -28,6 +29,7 @@ bootstrap_go_package { "exports_test.go", "java_sdk_test.go", "license_sdk_test.go", + "member_trait_test.go", "sdk_test.go", "testing.go", ], diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go index 25e35fcdb..da90c6dfc 100644 --- a/sdk/cc_sdk_test.go +++ b/sdk/cc_sdk_test.go @@ -32,6 +32,23 @@ var ccTestFs = android.MockFS{ "some/where/stubslib.map.txt": nil, } +// Adds a native bridge target to the configured list of targets. +var prepareForTestWithNativeBridgeTarget = android.FixtureModifyConfig(func(config android.Config) { + config.Targets[android.Android] = append(config.Targets[android.Android], android.Target{ + Os: android.Android, + Arch: android.Arch{ + ArchType: android.Arm64, + ArchVariant: "armv8-a", + CpuVariant: "cpu", + Abi: nil, + ArchFeatures: nil, + }, + NativeBridge: android.NativeBridgeEnabled, + NativeBridgeHostArchName: "x86_64", + NativeBridgeRelativePath: "native_bridge", + }) +}) + func testSdkWithCc(t *testing.T, bp string) *android.TestResult { t.Helper() return testSdkWithFs(t, bp, ccTestFs) @@ -1979,6 +1996,91 @@ myinclude/Test.h -> include/myinclude/Test.h ) } +func TestSnapshotWithCcHeadersLibraryAndNativeBridgeSupport(t *testing.T) { + result := android.GroupFixturePreparers( + cc.PrepareForTestWithCcDefaultModules, + PrepareForTestWithSdkBuildComponents, + ccTestFs.AddToFixture(), + prepareForTestWithNativeBridgeTarget, + ).RunTestWithBp(t, ` + sdk { + name: "mysdk", + native_header_libs: ["mynativeheaders"], + traits: { + native_bridge_support: ["mynativeheaders"], + }, + } + + cc_library_headers { + name: "mynativeheaders", + export_include_dirs: ["myinclude"], + stl: "none", + system_shared_libs: [], + native_bridge_supported: true, + } + `) + + CheckSnapshot(t, result, "mysdk", "", + checkUnversionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. + +cc_prebuilt_library_headers { + name: "mynativeheaders", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + native_bridge_supported: true, + stl: "none", + compile_multilib: "both", + system_shared_libs: [], + export_include_dirs: ["include/myinclude"], +} +`), + checkAllCopyRules(` +myinclude/Test.h -> include/myinclude/Test.h +`), + ) +} + +// TestSnapshotWithCcHeadersLibrary_DetectsNativeBridgeSpecificProperties verifies that when a +// module that has different output files for a native bridge target requests the native bridge +// variants are copied into the sdk snapshot that it reports an error. +func TestSnapshotWithCcHeadersLibrary_DetectsNativeBridgeSpecificProperties(t *testing.T) { + android.GroupFixturePreparers( + cc.PrepareForTestWithCcDefaultModules, + PrepareForTestWithSdkBuildComponents, + ccTestFs.AddToFixture(), + prepareForTestWithNativeBridgeTarget, + ).ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern( + `\QArchitecture variant "arm64_native_bridge" of sdk member "mynativeheaders" has properties distinct from other variants; this is not yet supported. The properties are: + export_include_dirs: [ + "arm64_native_bridge/include/myinclude_nativebridge", + "arm64_native_bridge/include/myinclude", + ],\E`)). + RunTestWithBp(t, ` + sdk { + name: "mysdk", + native_header_libs: ["mynativeheaders"], + traits: { + native_bridge_support: ["mynativeheaders"], + }, + } + + cc_library_headers { + name: "mynativeheaders", + export_include_dirs: ["myinclude"], + stl: "none", + system_shared_libs: [], + native_bridge_supported: true, + target: { + native_bridge: { + export_include_dirs: ["myinclude_nativebridge"], + }, + }, + } + `) +} + func TestHostSnapshotWithCcHeadersLibrary(t *testing.T) { result := testSdkWithCc(t, ` sdk { diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go index 9efb3a49a..43542cbdc 100644 --- a/sdk/java_sdk_test.go +++ b/sdk/java_sdk_test.go @@ -1205,6 +1205,55 @@ java_sdk_library_import { ) } +func TestSnapshotWithJavaSdkLibrary_AnnotationsZip(t *testing.T) { + result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, ` + sdk { + name: "mysdk", + java_sdk_libs: ["myjavalib"], + } + + java_sdk_library { + name: "myjavalib", + srcs: ["Test.java"], + sdk_version: "current", + shared_library: false, + annotations_enabled: true, + public: { + enabled: true, + }, + } + `) + + CheckSnapshot(t, result, "mysdk", "", + checkUnversionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. + +java_sdk_library_import { + name: "myjavalib", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + shared_library: false, + public: { + jars: ["sdk_library/public/myjavalib-stubs.jar"], + stub_srcs: ["sdk_library/public/myjavalib_stub_sources"], + current_api: "sdk_library/public/myjavalib.txt", + removed_api: "sdk_library/public/myjavalib-removed.txt", + annotations: "sdk_library/public/myjavalib_annotations.zip", + sdk_version: "current", + }, +} + `), + checkAllCopyRules(` +.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar +.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt +.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt +.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_annotations.zip -> sdk_library/public/myjavalib_annotations.zip + `), + checkMergeZips(".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip"), + ) +} + func TestSnapshotWithJavaSdkLibrary_CompileDex(t *testing.T) { result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, ` sdk { diff --git a/sdk/member_trait.go b/sdk/member_trait.go new file mode 100644 index 000000000..4229ca82b --- /dev/null +++ b/sdk/member_trait.go @@ -0,0 +1,126 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sdk + +import ( + "reflect" + + "android/soong/android" + "github.com/google/blueprint/proptools" +) + +// Contains information about the sdk properties that list sdk members by trait, e.g. +// native_bridge. +type sdkMemberTraitListProperty struct { + // getter for the list of member names + getter func(properties interface{}) []string + + // the trait of member referenced in the list + memberTrait android.SdkMemberTrait +} + +// Cache of dynamically generated dynamicSdkMemberTraits objects. The key is the pointer +// to a slice of SdkMemberTrait instances returned by android.RegisteredSdkMemberTraits(). +var dynamicSdkMemberTraitsMap android.OncePer + +// A dynamically generated set of member list properties and associated structure type. +// +// Instances of this are created by createDynamicSdkMemberTraits. +type dynamicSdkMemberTraits struct { + // The dynamically generated structure type. + // + // Contains one []string exported field for each SdkMemberTrait returned by android.RegisteredSdkMemberTraits(). The name of + // the field is the exported form of the value returned by SdkMemberTrait.SdkPropertyName(). + propertiesStructType reflect.Type + + // Information about each of the member trait specific list properties. + memberTraitListProperties []*sdkMemberTraitListProperty +} + +func (d *dynamicSdkMemberTraits) createMemberTraitListProperties() interface{} { + return reflect.New(d.propertiesStructType).Interface() +} + +func getDynamicSdkMemberTraits(key android.OnceKey, registeredTraits []android.SdkMemberTrait) *dynamicSdkMemberTraits { + // Get the cached value, creating new instance if necessary. + return dynamicSdkMemberTraitsMap.Once(key, func() interface{} { + return createDynamicSdkMemberTraits(registeredTraits) + }).(*dynamicSdkMemberTraits) +} + +// Create the dynamicSdkMemberTraits from the list of registered member traits. +// +// A struct is created which contains one exported field per member trait corresponding to +// the SdkMemberTrait.SdkPropertyName() value. +// +// A list of sdkMemberTraitListProperty instances is created, one per member trait that provides: +// * a reference to the member trait. +// * a getter for the corresponding field in the properties struct. +// +func createDynamicSdkMemberTraits(sdkMemberTraits []android.SdkMemberTrait) *dynamicSdkMemberTraits { + + var listProperties []*sdkMemberTraitListProperty + memberTraitToProperty := map[android.SdkMemberTrait]*sdkMemberTraitListProperty{} + var fields []reflect.StructField + + // Iterate over the member traits creating StructField and sdkMemberTraitListProperty objects. + nextFieldIndex := 0 + for _, memberTrait := range sdkMemberTraits { + + p := memberTrait.SdkPropertyName() + + var getter func(properties interface{}) []string + + // Create a dynamic exported field for the member trait's property. + fields = append(fields, reflect.StructField{ + Name: proptools.FieldNameForProperty(p), + Type: reflect.TypeOf([]string{}), + }) + + // Copy the field index for use in the getter func as using the loop variable directly will + // cause all funcs to use the last value. + fieldIndex := nextFieldIndex + nextFieldIndex += 1 + + getter = func(properties interface{}) []string { + // The properties is expected to be of the following form (where + // <Module_traits> is the name of an SdkMemberTrait.SdkPropertyName(). + // properties *struct {<Module_traits> []string, ....} + // + // Although it accesses the field by index the following reflection code is equivalent to: + // *properties.<Module_traits> + // + list := reflect.ValueOf(properties).Elem().Field(fieldIndex).Interface().([]string) + return list + } + + // Create an sdkMemberTraitListProperty for the member trait. + memberListProperty := &sdkMemberTraitListProperty{ + getter: getter, + memberTrait: memberTrait, + } + + memberTraitToProperty[memberTrait] = memberListProperty + listProperties = append(listProperties, memberListProperty) + } + + // Create a dynamic struct from the collated fields. + propertiesStructType := reflect.StructOf(fields) + + return &dynamicSdkMemberTraits{ + memberTraitListProperties: listProperties, + propertiesStructType: propertiesStructType, + } +} diff --git a/sdk/member_trait_test.go b/sdk/member_trait_test.go new file mode 100644 index 000000000..a3db189b2 --- /dev/null +++ b/sdk/member_trait_test.go @@ -0,0 +1,287 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sdk + +import ( + "fmt" + "path/filepath" + "testing" + + "android/soong/android" + "android/soong/java" + "github.com/google/blueprint" +) + +type fakeMemberTrait struct { + android.SdkMemberTraitBase +} + +type fakeMemberType struct { + android.SdkMemberTypeBase +} + +func (t *fakeMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) { + for _, name := range names { + ctx.AddVariationDependencies(nil, dependencyTag, name) + + if ctx.RequiresTrait(name, extraTrait) { + ctx.AddVariationDependencies(nil, dependencyTag, name+"_extra") + } + if ctx.RequiresTrait(name, specialTrait) { + ctx.AddVariationDependencies(nil, dependencyTag, name+"_special") + } + } +} + +func (t *fakeMemberType) IsInstance(module android.Module) bool { + return true +} + +func (t *fakeMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule { + moduleType := "java_import" + if ctx.RequiresTrait(extraTrait) { + moduleType = "java_test_import" + } + return ctx.SnapshotBuilder().AddPrebuiltModule(member, moduleType) +} + +func (t *fakeMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties { + return &fakeMemberTypeProperties{} +} + +type fakeMemberTypeProperties struct { + android.SdkMemberPropertiesBase + + path android.Path +} + +func (t *fakeMemberTypeProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) { + headerJars := variant.(java.ApexDependency).HeaderJars() + if len(headerJars) != 1 { + panic(fmt.Errorf("there must be only one header jar from %q", variant.Name())) + } + + t.path = headerJars[0] +} + +func (t *fakeMemberTypeProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) { + if t.path != nil { + relative := filepath.Join("javalibs", t.path.Base()) + ctx.SnapshotBuilder().CopyToSnapshot(t.path, relative) + propertySet.AddProperty("jars", []string{relative}) + } +} + +var ( + extraTrait = &fakeMemberTrait{ + SdkMemberTraitBase: android.SdkMemberTraitBase{ + PropertyName: "extra", + }, + } + + specialTrait = &fakeMemberTrait{ + SdkMemberTraitBase: android.SdkMemberTraitBase{ + PropertyName: "special", + }, + } + + fakeType = &fakeMemberType{ + SdkMemberTypeBase: android.SdkMemberTypeBase{ + PropertyName: "fake_members", + SupportsSdk: true, + Traits: []android.SdkMemberTrait{ + extraTrait, + specialTrait, + }, + }, + } +) + +func init() { + android.RegisterSdkMemberTrait(extraTrait) + android.RegisterSdkMemberTrait(specialTrait) + android.RegisterSdkMemberType(fakeType) +} + +func TestBasicTrait_WithoutTrait(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForSdkTestWithJava, + android.FixtureWithRootAndroidBp(` + sdk { + name: "mysdk", + fake_members: ["myjavalib"], + } + + java_library { + name: "myjavalib", + srcs: ["Test.java"], + system_modules: "none", + sdk_version: "none", + } + `), + ).RunTest(t) + + CheckSnapshot(t, result, "mysdk", "", + checkUnversionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. + +java_import { + name: "myjavalib", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + jars: ["javalibs/myjavalib.jar"], +} +`), + checkVersionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. + +java_import { + name: "mysdk_myjavalib@current", + sdk_member_name: "myjavalib", + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + jars: ["javalibs/myjavalib.jar"], +} + +sdk_snapshot { + name: "mysdk@current", + visibility: ["//visibility:public"], + fake_members: ["mysdk_myjavalib@current"], +} +`), + ) +} + +func TestBasicTrait_MultipleTraits(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForSdkTestWithJava, + android.FixtureWithRootAndroidBp(` + sdk { + name: "mysdk", + fake_members: ["myjavalib", "anotherjavalib"], + traits: { + extra: ["myjavalib"], + special: ["myjavalib", "anotherjavalib"], + }, + } + + java_library { + name: "myjavalib", + srcs: ["Test.java"], + system_modules: "none", + sdk_version: "none", + } + + java_library { + name: "myjavalib_extra", + srcs: ["Test.java"], + system_modules: "none", + sdk_version: "none", + } + + java_library { + name: "myjavalib_special", + srcs: ["Test.java"], + system_modules: "none", + sdk_version: "none", + } + + java_library { + name: "anotherjavalib", + srcs: ["Test.java"], + system_modules: "none", + sdk_version: "none", + } + + java_library { + name: "anotherjavalib_special", + srcs: ["Test.java"], + system_modules: "none", + sdk_version: "none", + } + `), + ).RunTest(t) + + CheckSnapshot(t, result, "mysdk", "", + checkUnversionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. + +java_test_import { + name: "myjavalib", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + jars: ["javalibs/myjavalib.jar"], +} + +java_import { + name: "myjavalib_extra", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + jars: ["javalibs/myjavalib_extra.jar"], +} + +java_import { + name: "myjavalib_special", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + jars: ["javalibs/myjavalib_special.jar"], +} + +java_import { + name: "anotherjavalib", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + jars: ["javalibs/anotherjavalib.jar"], +} + +java_import { + name: "anotherjavalib_special", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + jars: ["javalibs/anotherjavalib_special.jar"], +} +`), + ) +} + +func TestTraitUnsupportedByMemberType(t *testing.T) { + android.GroupFixturePreparers( + prepareForSdkTestWithJava, + android.FixtureWithRootAndroidBp(` + sdk { + name: "mysdk", + java_header_libs: ["myjavalib"], + traits: { + extra: ["myjavalib"], + }, + } + + java_library { + name: "myjavalib", + srcs: ["Test.java"], + system_modules: "none", + sdk_version: "none", + } + `), + ).ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern( + `\Qsdk member "myjavalib" has traits [extra] that are unsupported by its member type "java_header_libs"\E`)). + RunTest(t) +} diff --git a/sdk/member_type.go b/sdk/member_type.go index 9aab61dd0..10669fe23 100644 --- a/sdk/member_type.go +++ b/sdk/member_type.go @@ -64,14 +64,7 @@ func (d *dynamicSdkMemberTypes) createMemberTypeListProperties() interface{} { return reflect.New(d.propertiesStructType).Interface() } -func getDynamicSdkMemberTypes(registry *android.SdkMemberTypesRegistry) *dynamicSdkMemberTypes { - - // Get a key that uniquely identifies the registry contents. - key := registry.UniqueOnceKey() - - // Get the registered types. - registeredTypes := registry.RegisteredTypes() - +func getDynamicSdkMemberTypes(key android.OnceKey, registeredTypes []android.SdkMemberType) *dynamicSdkMemberTypes { // Get the cached value, creating new instance if necessary. return dynamicSdkMemberTypesMap.Once(key, func() interface{} { return createDynamicSdkMemberTypes(registeredTypes) diff --git a/sdk/sdk.go b/sdk/sdk.go index 6dea752b3..84c9a96e4 100644 --- a/sdk/sdk.go +++ b/sdk/sdk.go @@ -53,6 +53,13 @@ type sdk struct { // list properties, e.g. java_libs. dynamicMemberTypeListProperties interface{} + // The dynamically generated information about the registered SdkMemberTrait + dynamicSdkMemberTraits *dynamicSdkMemberTraits + + // The dynamically created instance of the properties struct containing the sdk member trait + // list properties. + dynamicMemberTraitListProperties interface{} + // Information about the OsType specific member variants depended upon by this variant. // // Set by OsType specific variants in the collectMembers() method and used by the @@ -104,17 +111,25 @@ func newSdkModule(moduleExports bool) *sdk { s := &sdk{} s.properties.Module_exports = moduleExports // Get the dynamic sdk member type data for the currently registered sdk member types. - var typeRegistry *android.SdkMemberTypesRegistry - if moduleExports { - typeRegistry = android.ModuleExportsMemberTypes - } else { - typeRegistry = android.SdkMemberTypes - } - s.dynamicSdkMemberTypes = getDynamicSdkMemberTypes(typeRegistry) + sdkMemberTypeKey, sdkMemberTypes := android.RegisteredSdkMemberTypes(moduleExports) + s.dynamicSdkMemberTypes = getDynamicSdkMemberTypes(sdkMemberTypeKey, sdkMemberTypes) // Create an instance of the dynamically created struct that contains all the // properties for the member type specific list properties. s.dynamicMemberTypeListProperties = s.dynamicSdkMemberTypes.createMemberTypeListProperties() - s.AddProperties(&s.properties, s.dynamicMemberTypeListProperties) + + sdkMemberTraitsKey, sdkMemberTraits := android.RegisteredSdkMemberTraits() + s.dynamicSdkMemberTraits = getDynamicSdkMemberTraits(sdkMemberTraitsKey, sdkMemberTraits) + // Create an instance of the dynamically created struct that contains all the properties for the + // member trait specific list properties. + s.dynamicMemberTraitListProperties = s.dynamicSdkMemberTraits.createMemberTraitListProperties() + + // Create a wrapper around the dynamic trait specific properties so that they have to be + // specified within a traits:{} section in the .bp file. + traitsWrapper := struct { + Traits interface{} + }{s.dynamicMemberTraitListProperties} + + s.AddProperties(&s.properties, s.dynamicMemberTypeListProperties, &traitsWrapper) // Make sure that the prebuilt visibility property is verified for errors. android.AddVisibilityProperty(s, "prebuilt_visibility", &s.properties.Prebuilt_visibility) @@ -145,6 +160,11 @@ func (s *sdk) memberTypeListProperty(memberType android.SdkMemberType) *sdkMembe return s.dynamicSdkMemberTypes.memberTypeToProperty[memberType] } +// memberTraitListProperties returns the list of *sdkMemberTraitListProperty instances for this sdk. +func (s *sdk) memberTraitListProperties() []*sdkMemberTraitListProperty { + return s.dynamicSdkMemberTraits.memberTraitListProperties +} + func (s *sdk) snapshot() bool { return s.properties.Snapshot } @@ -198,15 +218,53 @@ func (s *sdk) AndroidMkEntries() []android.AndroidMkEntries { }} } +// gatherTraits gathers the traits from the dynamically generated trait specific properties. +// +// Returns a map from member name to the set of required traits. +func (s *sdk) gatherTraits() map[string]android.SdkMemberTraitSet { + traitListByMember := map[string][]android.SdkMemberTrait{} + for _, memberListProperty := range s.memberTraitListProperties() { + names := memberListProperty.getter(s.dynamicMemberTraitListProperties) + for _, name := range names { + traitListByMember[name] = append(traitListByMember[name], memberListProperty.memberTrait) + } + } + + traitSetByMember := map[string]android.SdkMemberTraitSet{} + for name, list := range traitListByMember { + traitSetByMember[name] = android.NewSdkMemberTraitSet(list) + } + + return traitSetByMember +} + // newDependencyContext creates a new SdkDependencyContext for this sdk. func (s *sdk) newDependencyContext(mctx android.BottomUpMutatorContext) android.SdkDependencyContext { + traits := s.gatherTraits() + return &dependencyContext{ BottomUpMutatorContext: mctx, + requiredTraits: traits, } } type dependencyContext struct { android.BottomUpMutatorContext + + // Map from member name to the set of traits that the sdk requires the member provides. + requiredTraits map[string]android.SdkMemberTraitSet +} + +func (d *dependencyContext) RequiredTraits(name string) android.SdkMemberTraitSet { + if s, ok := d.requiredTraits[name]; ok { + return s + } else { + return android.EmptySdkMemberTraitSet() + } +} + +func (d *dependencyContext) RequiresTrait(name string, trait android.SdkMemberTrait) bool { + return d.RequiredTraits(name).Contains(trait) } var _ android.SdkDependencyContext = (*dependencyContext)(nil) @@ -287,8 +345,21 @@ func memberMutator(mctx android.BottomUpMutatorContext) { } names := memberListProperty.getter(s.dynamicMemberTypeListProperties) if len(names) > 0 { + memberType := memberListProperty.memberType + + // Verify that the member type supports the specified traits. + supportedTraits := memberType.SupportedTraits() + for _, name := range names { + requiredTraits := ctx.RequiredTraits(name) + unsupportedTraits := requiredTraits.Subtract(supportedTraits) + if !unsupportedTraits.Empty() { + ctx.ModuleErrorf("sdk member %q has traits %s that are unsupported by its member type %q", name, unsupportedTraits, memberType.SdkPropertyName()) + } + } + + // Add dependencies using the appropriate tag. tag := memberListProperty.dependencyTag - memberListProperty.memberType.AddDependencies(ctx, tag, names) + memberType.AddDependencies(ctx, tag, names) } } } diff --git a/sdk/update.go b/sdk/update.go index 89a5c9269..02e61fb2e 100644 --- a/sdk/update.go +++ b/sdk/update.go @@ -393,10 +393,18 @@ be unnecessary as every module in the sdk already has its own licenses property. members := s.groupMemberVariantsByMemberThenType(ctx, memberVariantDeps) // Create the prebuilt modules for each of the member modules. + traits := s.gatherTraits() for _, member := range members { memberType := member.memberType - memberCtx := &memberContext{ctx, builder, memberType, member.name} + name := member.name + requiredTraits := traits[name] + if requiredTraits == nil { + requiredTraits = android.EmptySdkMemberTraitSet() + } + + // Create the snapshot for the member. + memberCtx := &memberContext{ctx, builder, memberType, name, requiredTraits} prebuiltModule := memberType.AddPrebuiltModule(memberCtx, member) s.createMemberSnapshot(memberCtx, member, prebuiltModule.(*bpModule)) @@ -1385,19 +1393,19 @@ func newOsTypeSpecificInfo(ctx android.SdkMemberContext, osType android.OsType, osInfo.Properties = osSpecificVariantPropertiesFactory() // Group the variants by arch type. - var variantsByArchName = make(map[string][]android.Module) - var archTypes []android.ArchType + var variantsByArchId = make(map[archId][]android.Module) + var archIds []archId for _, variant := range osTypeVariants { - archType := variant.Target().Arch.ArchType - archTypeName := archType.Name - if _, ok := variantsByArchName[archTypeName]; !ok { - archTypes = append(archTypes, archType) + target := variant.Target() + id := archIdFromTarget(target) + if _, ok := variantsByArchId[id]; !ok { + archIds = append(archIds, id) } - variantsByArchName[archTypeName] = append(variantsByArchName[archTypeName], variant) + variantsByArchId[id] = append(variantsByArchId[id], variant) } - if commonVariants, ok := variantsByArchName["common"]; ok { + if commonVariants, ok := variantsByArchId[commonArchId]; ok { if len(osTypeVariants) != 1 { panic(fmt.Errorf("Expected to only have 1 variant when arch type is common but found %d", len(osTypeVariants))) } @@ -1407,11 +1415,9 @@ func newOsTypeSpecificInfo(ctx android.SdkMemberContext, osType android.OsType, osInfo.Properties.PopulateFromVariant(ctx, commonVariants[0]) } else { // Create an arch specific info for each supported architecture type. - for _, archType := range archTypes { - archTypeName := archType.Name - - archVariants := variantsByArchName[archTypeName] - archInfo := newArchSpecificInfo(ctx, archType, osType, osSpecificVariantPropertiesFactory, archVariants) + for _, id := range archIds { + archVariants := variantsByArchId[id] + archInfo := newArchSpecificInfo(ctx, id, osType, osSpecificVariantPropertiesFactory, archVariants) osInfo.archInfos = append(osInfo.archInfos, archInfo) } @@ -1430,7 +1436,7 @@ func (osInfo *osTypeSpecificInfo) optimizeProperties(ctx *memberContext, commonV multilib := multilibNone for _, archInfo := range osInfo.archInfos { - multilib = multilib.addArchType(archInfo.archType) + multilib = multilib.addArchType(archInfo.archId.archType) // Optimize the arch properties first. archInfo.optimizeProperties(ctx, commonValueExtractor) @@ -1523,11 +1529,55 @@ func (osInfo *osTypeSpecificInfo) String() string { return fmt.Sprintf("OsType{%s}", osInfo.osType) } +// archId encapsulates the information needed to identify a combination of arch type and native +// bridge support. +// +// Conceptually, native bridge support is a facet of an android.Target, not an android.Arch as it is +// essentially using one android.Arch to implement another. However, in terms of the handling of +// the variants native bridge is treated as part of the arch variation. See the ArchVariation method +// on android.Target. +// +// So, it makes sense when optimizing the variants to combine native bridge with the arch type. +type archId struct { + // The arch type of the variant's target. + archType android.ArchType + + // True if the variants is for the native bridge, false otherwise. + nativeBridge bool +} + +// propertyName returns the name of the property corresponding to use for this arch id. +func (i *archId) propertyName() string { + name := i.archType.Name + if i.nativeBridge { + // Note: This does not result in a valid property because there is no architecture specific + // native bridge property, only a generic "native_bridge" property. However, this will be used + // in error messages if there is an attempt to use this in a generated bp file. + name += "_native_bridge" + } + return name +} + +func (i *archId) String() string { + return fmt.Sprintf("ArchType{%s}, NativeBridge{%t}", i.archType, i.nativeBridge) +} + +// archIdFromTarget returns an archId initialized from information in the supplied target. +func archIdFromTarget(target android.Target) archId { + return archId{ + archType: target.Arch.ArchType, + nativeBridge: target.NativeBridge == android.NativeBridgeEnabled, + } +} + +// commonArchId is the archId for the common architecture. +var commonArchId = archId{archType: android.Common} + type archTypeSpecificInfo struct { baseInfo - archType android.ArchType - osType android.OsType + archId archId + osType android.OsType linkInfos []*linkTypeSpecificInfo } @@ -1536,10 +1586,10 @@ var _ propertiesContainer = (*archTypeSpecificInfo)(nil) // Create a new archTypeSpecificInfo for the specified arch type and its properties // structures populated with information from the variants. -func newArchSpecificInfo(ctx android.SdkMemberContext, archType android.ArchType, osType android.OsType, variantPropertiesFactory variantPropertiesFactoryFunc, archVariants []android.Module) *archTypeSpecificInfo { +func newArchSpecificInfo(ctx android.SdkMemberContext, archId archId, osType android.OsType, variantPropertiesFactory variantPropertiesFactoryFunc, archVariants []android.Module) *archTypeSpecificInfo { // Create an arch specific info into which the variant properties can be copied. - archInfo := &archTypeSpecificInfo{archType: archType, osType: osType} + archInfo := &archTypeSpecificInfo{archId: archId, osType: osType} // Create the properties into which the arch type specific properties will be // added. @@ -1597,8 +1647,9 @@ func (archInfo *archTypeSpecificInfo) optimizeProperties(ctx *memberContext, com // Add the properties for an arch type to a property set. func (archInfo *archTypeSpecificInfo) addToPropertySet(ctx *memberContext, archPropertySet android.BpPropertySet, archOsPrefix string) { - archTypeName := archInfo.archType.Name - archTypePropertySet := archPropertySet.AddPropertySet(archOsPrefix + archTypeName) + archPropertySuffix := archInfo.archId.propertyName() + propertySetName := archOsPrefix + archPropertySuffix + archTypePropertySet := archPropertySet.AddPropertySet(propertySetName) // Enable the <os>_<arch> variant explicitly when we've disabled it by default on host. if ctx.memberType.IsHostOsDependent() && archInfo.osType.Class == android.Host { archTypePropertySet.AddProperty("enabled", true) @@ -1608,10 +1659,36 @@ func (archInfo *archTypeSpecificInfo) addToPropertySet(ctx *memberContext, archP for _, linkInfo := range archInfo.linkInfos { linkInfo.addToPropertySet(ctx, archTypePropertySet) } + + // If this is for a native bridge architecture then make sure that the property set does not + // contain any properties as providing native bridge specific properties is not currently + // supported. + if archInfo.archId.nativeBridge { + propertySetContents := getPropertySetContents(archTypePropertySet) + if propertySetContents != "" { + ctx.SdkModuleContext().ModuleErrorf("Architecture variant %q of sdk member %q has properties distinct from other variants; this is not yet supported. The properties are:\n%s", + propertySetName, ctx.name, propertySetContents) + } + } +} + +// getPropertySetContents returns the string representation of the contents of a property set, after +// recursively pruning any empty nested property sets. +func getPropertySetContents(propertySet android.BpPropertySet) string { + set := propertySet.(*bpPropertySet) + set.transformContents(pruneEmptySetTransformer{}) + if len(set.properties) != 0 { + contents := &generatedContents{} + contents.Indent() + outputPropertySet(contents, set) + setAsString := contents.content.String() + return setAsString + } + return "" } func (archInfo *archTypeSpecificInfo) String() string { - return fmt.Sprintf("ArchType{%s}", archInfo.archType) + return archInfo.archId.String() } type linkTypeSpecificInfo struct { @@ -1651,6 +1728,9 @@ type memberContext struct { builder *snapshotBuilder memberType android.SdkMemberType name string + + // The set of traits required of this member. + requiredTraits android.SdkMemberTraitSet } func (m *memberContext) SdkModuleContext() android.ModuleContext { @@ -1669,6 +1749,10 @@ func (m *memberContext) Name() string { return m.name } +func (m *memberContext) RequiresTrait(trait android.SdkMemberTrait) bool { + return m.requiredTraits.Contains(trait) +} + func (s *sdk) createMemberSnapshot(ctx *memberContext, member *sdkMember, bpModule *bpModule) { memberType := member.memberType diff --git a/ui/terminal/simple_status.go b/ui/terminal/simple_status.go index 4e8c56804..936b275a1 100644 --- a/ui/terminal/simple_status.go +++ b/ui/terminal/simple_status.go @@ -24,15 +24,17 @@ import ( type simpleStatusOutput struct { writer io.Writer formatter formatter + keepANSI bool } // NewSimpleStatusOutput returns a StatusOutput that represents the // current build status similarly to Ninja's built-in terminal // output. -func NewSimpleStatusOutput(w io.Writer, formatter formatter) status.StatusOutput { +func NewSimpleStatusOutput(w io.Writer, formatter formatter, keepANSI bool) status.StatusOutput { return &simpleStatusOutput{ writer: w, formatter: formatter, + keepANSI: keepANSI, } } @@ -54,7 +56,9 @@ func (s *simpleStatusOutput) FinishAction(result status.ActionResult, counts sta progress := s.formatter.progress(counts) + str output := s.formatter.result(result) - output = string(stripAnsiEscapes([]byte(output))) + if !s.keepANSI { + output = string(stripAnsiEscapes([]byte(output))) + } if output != "" { fmt.Fprint(s.writer, progress, "\n", output) diff --git a/ui/terminal/smart_status.go b/ui/terminal/smart_status.go index 6bdf14074..06a4064ff 100644 --- a/ui/terminal/smart_status.go +++ b/ui/terminal/smart_status.go @@ -77,7 +77,12 @@ func NewSmartStatusOutput(w io.Writer, formatter formatter) status.StatusOutput s.requestedTableHeight = h } - s.updateTermSize() + if w, h, ok := termSize(s.writer); ok { + s.termWidth, s.termHeight = w, h + s.computeTableHeight() + } else { + s.tableMode = false + } if s.tableMode { // Add empty lines at the bottom of the screen to scroll back the existing history @@ -296,40 +301,44 @@ func (s *smartStatusOutput) stopSigwinch() { close(s.sigwinch) } +// computeTableHeight recomputes s.tableHeight based on s.termHeight and s.requestedTableHeight. +func (s *smartStatusOutput) computeTableHeight() { + tableHeight := s.requestedTableHeight + if tableHeight == 0 { + tableHeight = s.termHeight / 4 + if tableHeight < 1 { + tableHeight = 1 + } else if tableHeight > 10 { + tableHeight = 10 + } + } + if tableHeight > s.termHeight-1 { + tableHeight = s.termHeight - 1 + } + s.tableHeight = tableHeight +} + +// updateTermSize recomputes the table height after a SIGWINCH and pans any existing text if +// necessary. func (s *smartStatusOutput) updateTermSize() { if w, h, ok := termSize(s.writer); ok { - firstUpdate := s.termHeight == 0 && s.termWidth == 0 oldScrollingHeight := s.termHeight - s.tableHeight s.termWidth, s.termHeight = w, h if s.tableMode { - tableHeight := s.requestedTableHeight - if tableHeight == 0 { - tableHeight = s.termHeight / 4 - if tableHeight < 1 { - tableHeight = 1 - } else if tableHeight > 10 { - tableHeight = 10 - } - } - if tableHeight > s.termHeight-1 { - tableHeight = s.termHeight - 1 - } - s.tableHeight = tableHeight + s.computeTableHeight() scrollingHeight := s.termHeight - s.tableHeight - if !firstUpdate { - // If the scrolling region has changed, attempt to pan the existing text so that it is - // not overwritten by the table. - if scrollingHeight < oldScrollingHeight { - pan := oldScrollingHeight - scrollingHeight - if pan > s.tableHeight { - pan = s.tableHeight - } - fmt.Fprint(s.writer, ansi.panDown(pan)) + // If the scrolling region has changed, attempt to pan the existing text so that it is + // not overwritten by the table. + if scrollingHeight < oldScrollingHeight { + pan := oldScrollingHeight - scrollingHeight + if pan > s.tableHeight { + pan = s.tableHeight } + fmt.Fprint(s.writer, ansi.panDown(pan)) } } } diff --git a/ui/terminal/status.go b/ui/terminal/status.go index d8e739211..2ad174fee 100644 --- a/ui/terminal/status.go +++ b/ui/terminal/status.go @@ -26,12 +26,12 @@ import ( // // statusFormat takes nearly all the same options as NINJA_STATUS. // %c is currently unsupported. -func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild bool) status.StatusOutput { +func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild, forceKeepANSI bool) status.StatusOutput { formatter := newFormatter(statusFormat, quietBuild) if !forceSimpleOutput && isSmartTerminal(w) { return NewSmartStatusOutput(w, formatter) } else { - return NewSimpleStatusOutput(w, formatter) + return NewSimpleStatusOutput(w, formatter, forceKeepANSI) } } diff --git a/ui/terminal/status_test.go b/ui/terminal/status_test.go index aa69dff53..810e31d1b 100644 --- a/ui/terminal/status_test.go +++ b/ui/terminal/status_test.go @@ -94,7 +94,7 @@ func TestStatusOutput(t *testing.T) { t.Run("smart", func(t *testing.T) { smart := &fakeSmartTerminal{termWidth: 40} - stat := NewStatusOutput(smart, "", false, false) + stat := NewStatusOutput(smart, "", false, false, false) tt.calls(stat) stat.Flush() @@ -105,7 +105,7 @@ func TestStatusOutput(t *testing.T) { t.Run("simple", func(t *testing.T) { simple := &bytes.Buffer{} - stat := NewStatusOutput(simple, "", false, false) + stat := NewStatusOutput(simple, "", false, false, false) tt.calls(stat) stat.Flush() @@ -116,7 +116,7 @@ func TestStatusOutput(t *testing.T) { t.Run("force simple", func(t *testing.T) { smart := &fakeSmartTerminal{termWidth: 40} - stat := NewStatusOutput(smart, "", true, false) + stat := NewStatusOutput(smart, "", true, false, false) tt.calls(stat) stat.Flush() @@ -269,7 +269,7 @@ func TestSmartStatusOutputWidthChange(t *testing.T) { os.Setenv(tableHeightEnVar, "") smart := &fakeSmartTerminal{termWidth: 40} - stat := NewStatusOutput(smart, "", false, false) + stat := NewStatusOutput(smart, "", false, false, false) smartStat := stat.(*smartStatusOutput) smartStat.sigwinchHandled = make(chan bool) |