diff options
44 files changed, 1368 insertions, 357 deletions
diff --git a/Android.bp b/Android.bp index a9eceb248..342ca4ca9 100644 --- a/Android.bp +++ b/Android.bp @@ -391,6 +391,7 @@ bootstrap_go_package { srcs: [ "rust/androidmk.go", "rust/compiler.go", + "rust/coverage.go", "rust/binary.go", "rust/builder.go", "rust/library.go", @@ -403,6 +404,7 @@ bootstrap_go_package { testSrcs: [ "rust/binary_test.go", "rust/compiler_test.go", + "rust/coverage_test.go", "rust/library_test.go", "rust/rust_test.go", "rust/test_test.go", diff --git a/android/defaults.go b/android/defaults.go index 6a908ea83..81e340e8e 100644 --- a/android/defaults.go +++ b/android/defaults.go @@ -35,6 +35,9 @@ type DefaultableModuleBase struct { defaultsProperties defaultsProperties defaultableProperties []interface{} defaultableVariableProperties interface{} + + // The optional hook to call after any defaults have been applied. + hook DefaultableHook } func (d *DefaultableModuleBase) defaults() *defaultsProperties { @@ -46,6 +49,16 @@ func (d *DefaultableModuleBase) setProperties(props []interface{}, variablePrope d.defaultableVariableProperties = variableProperties } +func (d *DefaultableModuleBase) SetDefaultableHook(hook DefaultableHook) { + d.hook = hook +} + +func (d *DefaultableModuleBase) callHookIfAvailable(ctx DefaultableHookContext) { + if d.hook != nil { + d.hook(ctx) + } +} + // Interface that must be supported by any module to which defaults can be applied. type Defaultable interface { // Get a pointer to the struct containing the Defaults property. @@ -57,6 +70,15 @@ type Defaultable interface { // Apply defaults from the supplied Defaults to the property structures supplied to // setProperties(...). applyDefaults(TopDownMutatorContext, []Defaults) + + // Set the hook to be called after any defaults have been applied. + // + // Should be used in preference to a AddLoadHook when the behavior of the load + // hook is dependent on properties supplied in the Android.bp file. + SetDefaultableHook(hook DefaultableHook) + + // Call the hook if specified. + callHookIfAvailable(context DefaultableHookContext) } type DefaultableModule interface { @@ -75,6 +97,15 @@ func InitDefaultableModule(module DefaultableModule) { module.AddProperties(module.defaults()) } +// A restricted subset of context methods, similar to LoadHookContext. +type DefaultableHookContext interface { + EarlyModuleContext + + CreateModule(ModuleFactory, ...interface{}) Module +} + +type DefaultableHook func(ctx DefaultableHookContext) + // The Defaults_visibility property. type DefaultsVisibilityProperties struct { @@ -268,25 +299,29 @@ func defaultsDepsMutator(ctx BottomUpMutatorContext) { } func defaultsMutator(ctx TopDownMutatorContext) { - if defaultable, ok := ctx.Module().(Defaultable); ok && len(defaultable.defaults().Defaults) > 0 { - var defaultsList []Defaults - seen := make(map[Defaults]bool) - - ctx.WalkDeps(func(module, parent Module) bool { - if ctx.OtherModuleDependencyTag(module) == DefaultsDepTag { - if defaults, ok := module.(Defaults); ok { - if !seen[defaults] { - seen[defaults] = true - defaultsList = append(defaultsList, defaults) - return len(defaults.defaults().Defaults) > 0 + if defaultable, ok := ctx.Module().(Defaultable); ok { + if len(defaultable.defaults().Defaults) > 0 { + var defaultsList []Defaults + seen := make(map[Defaults]bool) + + ctx.WalkDeps(func(module, parent Module) bool { + if ctx.OtherModuleDependencyTag(module) == DefaultsDepTag { + if defaults, ok := module.(Defaults); ok { + if !seen[defaults] { + seen[defaults] = true + defaultsList = append(defaultsList, defaults) + return len(defaults.defaults().Defaults) > 0 + } + } else { + ctx.PropertyErrorf("defaults", "module %s is not an defaults module", + ctx.OtherModuleName(module)) } - } else { - ctx.PropertyErrorf("defaults", "module %s is not an defaults module", - ctx.OtherModuleName(module)) } - } - return false - }) - defaultable.applyDefaults(ctx, defaultsList) + return false + }) + defaultable.applyDefaults(ctx, defaultsList) + } + + defaultable.callHookIfAvailable(ctx) } } diff --git a/android/hooks.go b/android/hooks.go index 47f69d1e8..f43a007a0 100644 --- a/android/hooks.go +++ b/android/hooks.go @@ -39,6 +39,12 @@ type LoadHookContext interface { moduleFactories() map[string]blueprint.ModuleFactory } +// Add a hook that will be called once the module has been loaded, i.e. its +// properties have been initialized from the Android.bp file. +// +// Consider using SetDefaultableHook to register a hook for any module that implements +// DefaultableModule as the hook is called after any defaults have been applied to the +// module which could reduce duplication and make it easier to use. func AddLoadHook(m blueprint.Module, hook func(LoadHookContext)) { blueprint.AddLoadHook(m, func(ctx blueprint.LoadHookContext) { actx := &loadHookContext{ diff --git a/android/mutator.go b/android/mutator.go index 79a85062a..77d5f433e 100644 --- a/android/mutator.go +++ b/android/mutator.go @@ -82,10 +82,6 @@ type RegisterMutatorFunc func(RegisterMutatorsContext) var preArch = []RegisterMutatorFunc{ RegisterNamespaceMutator, - // Create an association between prebuilt modules and their corresponding source - // modules (if any). - RegisterPrebuiltsPreArchMutators, - // Check the visibility rules are valid. // // This must run after the package renamer mutators so that any issues found during @@ -114,8 +110,19 @@ var preArch = []RegisterMutatorFunc{ RegisterVisibilityRuleChecker, // Apply properties from defaults modules to the referencing modules. + // + // Any mutators that are added before this will not see any modules created by + // a DefaultableHook. RegisterDefaultsPreArchMutators, + // Create an association between prebuilt modules and their corresponding source + // modules (if any). + // + // Must be run after defaults mutators to ensure that any modules created by + // a DefaultableHook can be either a prebuilt or a source module with a matching + // prebuilt. + RegisterPrebuiltsPreArchMutators, + // Gather the visibility rules for all modules for us during visibility enforcement. // // This must come after the defaults mutators to ensure that any visibility supplied diff --git a/android/sdk.go b/android/sdk.go index 6f62f552c..e823106e8 100644 --- a/android/sdk.go +++ b/android/sdk.go @@ -331,13 +331,7 @@ type SdkMemberType interface { // Add a prebuilt module that the sdk will populate. // - // Returning nil from this will cause the sdk module type to use the deprecated BuildSnapshot - // method to build the snapshot. That method is deprecated because it requires the SdkMemberType - // implementation to do all the word. - // - // Otherwise, returning a non-nil value from this will cause the sdk module type to do the - // majority of the work to generate the snapshot. The sdk module code generates the snapshot - // as follows: + // The sdk module code generates the snapshot as follows: // // * A properties struct of type SdkMemberProperties is created for each variant and // populated with information from the variant by calling PopulateFromVariant(SdkAware) @@ -348,9 +342,24 @@ type SdkMemberType interface { // // * The variant property structs are analysed to find exported (capitalized) fields which // have common values. Those fields are cleared and the common value added to the common - // properties. A field annotated with a tag of `sdk:"keep"` will be treated as if it + // properties. + // + // A field annotated with a tag of `sdk:"keep"` will be treated as if it // was not capitalized, i.e. not optimized for common values. // + // A field annotated with a tag of `android:"arch_variant"` will be allowed to have + // values that differ by arch, fields not tagged as such must have common values across + // all variants. + // + // * Additional field tags can be specified on a field that will ignore certain values + // for the purpose of common value optimization. A value that is ignored must have the + // default value for the property type. This is to ensure that significant value are not + // ignored by accident. The purpose of this is to allow the snapshot generation to reflect + // the behavior of the runtime. e.g. if a property is ignored on the host then a property + // that is common for android can be treated as if it was common for android and host as + // the setting for host is ignored anyway. + // * `sdk:"ignored-on-host" - this indicates the property is ignored on the host variant. + // // * The sdk module type populates the BpModule structure, creating the arch specific // structure and calls AddToPropertySet(...) on the properties struct to add the member // specific properties in the correct place in the structure. diff --git a/android/util.go b/android/util.go index e74b64e61..8dbf21459 100644 --- a/android/util.go +++ b/android/util.go @@ -141,6 +141,16 @@ func PrefixInList(list []string, prefix string) bool { return false } +// Returns true if any string in the given list has the given suffix. +func SuffixInList(list []string, suffix string) bool { + for _, s := range list { + if strings.HasSuffix(s, suffix) { + return true + } + } + return false +} + // IndexListPred returns the index of the element which in the given `list` satisfying the predicate, or -1 if there is no such element. func IndexListPred(pred func(s string) bool, list []string) int { for i, l := range list { diff --git a/android/visibility_test.go b/android/visibility_test.go index 8dd6a8f8c..4cf41a6cc 100644 --- a/android/visibility_test.go +++ b/android/visibility_test.go @@ -903,6 +903,69 @@ var visibilityTests = []struct { }`), }, }, + { + name: "ensure visibility properties are checked for correctness", + fs: map[string][]byte{ + "top/Blueprints": []byte(` + mock_parent { + name: "parent", + visibility: ["//top/nested"], + child: { + name: "libchild", + visibility: ["top/other"], + }, + }`), + }, + expectedErrors: []string{ + `module "parent": child.visibility: invalid visibility pattern "top/other"`, + }, + }, + { + name: "invalid visibility added to child detected during gather phase", + fs: map[string][]byte{ + "top/Blueprints": []byte(` + mock_parent { + name: "parent", + visibility: ["//top/nested"], + child: { + name: "libchild", + invalid_visibility: ["top/other"], + }, + }`), + }, + expectedErrors: []string{ + // That this error is reported against the child not the parent shows it was + // not being detected in the parent which is correct as invalid_visibility is + // purposely not added to the list of visibility properties to check, and was + // in fact detected in the child in the gather phase. Contrast this error message + // with the preceding one. + `module "libchild" \(created by module "parent"\): visibility: invalid visibility pattern "top/other"`, + }, + }, + { + name: "automatic visibility inheritance enabled", + fs: map[string][]byte{ + "top/Blueprints": []byte(` + mock_parent { + name: "parent", + visibility: ["//top/nested"], + child: { + name: "libchild", + visibility: ["//top/other"], + }, + }`), + "top/nested/Blueprints": []byte(` + mock_library { + name: "libnested", + deps: ["libchild"], + }`), + "top/other/Blueprints": []byte(` + mock_library { + name: "libother", + deps: ["libchild"], + }`), + }, + }, } func TestVisibility(t *testing.T) { @@ -936,6 +999,7 @@ func testVisibility(buildDir string, fs map[string][]byte) (*TestContext, []erro ctx := NewTestArchContext() ctx.RegisterModuleType("mock_library", newMockLibraryModule) + ctx.RegisterModuleType("mock_parent", newMockParentFactory) ctx.RegisterModuleType("mock_defaults", defaultsFactory) // Order of the following method calls is significant. @@ -996,3 +1060,42 @@ func defaultsFactory() Module { InitDefaultsModule(m) return m } + +type mockParentProperties struct { + Child struct { + Name *string + + // Visibility to pass to the child module. + Visibility []string + + // Purposely not validated visibility to pass to the child. + Invalid_visibility []string + } +} + +type mockParent struct { + ModuleBase + DefaultableModuleBase + properties mockParentProperties +} + +func (p *mockParent) GenerateAndroidBuildActions(ModuleContext) { +} + +func newMockParentFactory() Module { + m := &mockParent{} + m.AddProperties(&m.properties) + InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon) + InitDefaultableModule(m) + AddVisibilityProperty(m, "child.visibility", &m.properties.Child.Visibility) + + m.SetDefaultableHook(func(ctx DefaultableHookContext) { + visibility := m.properties.Child.Visibility + visibility = append(visibility, m.properties.Child.Invalid_visibility...) + ctx.CreateModule(newMockLibraryModule, &struct { + Name *string + Visibility []string + }{m.properties.Child.Name, visibility}) + }) + return m +} diff --git a/apex/apex.go b/apex/apex.go index 93ceb8387..fa71ffa5c 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -227,7 +227,6 @@ func makeApexAvailableWhitelist() map[string][]string { "netd_aidl_interface-unstable-java", "netd_event_listener_interface-java", "netlink-client", - "networkstack-aidl-interfaces-unstable-java", "networkstack-client", "sap-api-java-static", "services.net", @@ -679,7 +678,6 @@ func makeApexAvailableWhitelist() map[string][]string { "netd_aidl_interface-unstable-java", "netd_event_listener_interface-java", "netlink-client", - "networkstack-aidl-interfaces-unstable-java", "networkstack-client", "services.net", "wifi-lite-protos", @@ -1604,6 +1602,8 @@ func (a *apexBundle) IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizer return android.InList(sanitizerName, globalSanitizerNames) } +var _ cc.Coverage = (*apexBundle)(nil) + func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool { return ctx.Device() && (ctx.DeviceConfig().NativeCoverageEnabled() || ctx.DeviceConfig().ClangCoverageEnabled()) } @@ -1620,6 +1620,8 @@ func (a *apexBundle) MarkAsCoverageVariant(coverage bool) { a.properties.IsCoverageVariant = coverage } +func (a *apexBundle) EnableCoverageIfNeeded() {} + // TODO(jiyong) move apexFileFor* close to the apexFile type definition func apexFileForNativeLibrary(ctx android.BaseModuleContext, ccMod *cc.Module, handleSpecialLibs bool) apexFile { // Decide the APEX-local directory by the multilib of the library @@ -2073,8 +2075,8 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { // // Always include if we are a host-apex however since those won't have any // system libraries. - if !android.DirectlyInAnyApex(ctx, cc.Name()) && !android.InList(cc.Name(), a.requiredDeps) { - a.requiredDeps = append(a.requiredDeps, cc.Name()) + if !android.DirectlyInAnyApex(ctx, cc.Name()) && !android.InList(cc.BaseModuleName(), a.requiredDeps) { + a.requiredDeps = append(a.requiredDeps, cc.BaseModuleName()) } requireNativeLibs = append(requireNativeLibs, cc.OutputFile().Path().Base()) // Don't track further diff --git a/apex/apex_test.go b/apex/apex_test.go index ce39b39e4..dc6986244 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -4530,12 +4530,12 @@ func testNoUpdatableJarsInBootImage(t *testing.T, errmsg, bp string, transformDe ctx.RegisterModuleType("apex", BundleFactory) ctx.RegisterModuleType("apex_key", ApexKeyFactory) ctx.RegisterModuleType("filegroup", android.FileGroupFactory) + ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators) cc.RegisterRequiredBuildComponentsForTest(ctx) java.RegisterJavaBuildComponents(ctx) java.RegisterSystemModulesBuildComponents(ctx) java.RegisterAppBuildComponents(ctx) java.RegisterDexpreoptBootJarsComponents(ctx) - ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators) ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators) ctx.PreDepsMutators(RegisterPreDepsMutators) ctx.PostDepsMutators(RegisterPostDepsMutators) diff --git a/apex/vndk_test.go b/apex/vndk_test.go index 523ac2630..05cdfcd92 100644 --- a/apex/vndk_test.go +++ b/apex/vndk_test.go @@ -117,44 +117,7 @@ func TestVndkApexUsesVendorVariant(t *testing.T) { }) t.Run("VNDK APEX supports coverage variants", func(t *testing.T) { - ctx, _ := testApex(t, bp+` - cc_library { - name: "libprofile-extras", - vendor_available: true, - recovery_available: true, - native_coverage: false, - system_shared_libs: [], - stl: "none", - notice: "custom_notice", - } - cc_library { - name: "libprofile-clang-extras", - vendor_available: true, - recovery_available: true, - native_coverage: false, - system_shared_libs: [], - stl: "none", - notice: "custom_notice", - } - cc_library { - name: "libprofile-extras_ndk", - vendor_available: true, - native_coverage: false, - system_shared_libs: [], - stl: "none", - notice: "custom_notice", - sdk_version: "current", - } - cc_library { - name: "libprofile-clang-extras_ndk", - vendor_available: true, - native_coverage: false, - system_shared_libs: [], - stl: "none", - notice: "custom_notice", - sdk_version: "current", - } - `, func(fs map[string][]byte, config android.Config) { + ctx, _ := testApex(t, bp, func(fs map[string][]byte, config android.Config) { config.TestProductVariables.Native_coverage = proptools.BoolPtr(true) }) @@ -437,7 +437,6 @@ var ( ndkLateStubDepTag = DependencyTag{Name: "ndk late stub", Library: true} vndkExtDepTag = DependencyTag{Name: "vndk extends", Library: true} runtimeDepTag = DependencyTag{Name: "runtime lib"} - coverageDepTag = DependencyTag{Name: "coverage"} testPerSrcDepTag = DependencyTag{Name: "test_per_src"} ) @@ -745,6 +744,15 @@ func (c *Module) OutputFile() android.OptionalPath { return c.outputFile } +func (c *Module) CoverageFiles() android.Paths { + if c.linker != nil { + if library, ok := c.linker.(libraryInterface); ok { + return library.objs().coverageFiles + } + } + panic(fmt.Errorf("CoverageFiles called on non-library module: %q", c.BaseModuleName())) +} + var _ LinkableInterface = (*Module)(nil) func (c *Module) UnstrippedOutputFile() android.Path { @@ -2493,13 +2501,16 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { // When combining coverage files for shared libraries and executables, coverage files // in static libraries act as if they were whole static libraries. The same goes for // source based Abi dump files. - // This should only be done for cc.Modules if c, ok := ccDep.(*Module); ok { staticLib := c.linker.(libraryInterface) depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles, staticLib.objs().coverageFiles...) depPaths.StaticLibObjs.sAbiDumpFiles = append(depPaths.StaticLibObjs.sAbiDumpFiles, staticLib.objs().sAbiDumpFiles...) + } else if c, ok := ccDep.(LinkableInterface); ok { + // Handle non-CC modules here + depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles, + c.CoverageFiles()...) } } diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go index 5575baaab..93834630c 100644 --- a/cc/config/arm64_device.go +++ b/cc/config/arm64_device.go @@ -92,7 +92,6 @@ func init() { pctx.StaticVariable("Arm64Ldflags", strings.Join(arm64Ldflags, " ")) pctx.StaticVariable("Arm64Lldflags", strings.Join(arm64Lldflags, " ")) - pctx.StaticVariable("Arm64IncludeFlags", bionicHeaders("arm64")) pctx.StaticVariable("Arm64ClangCflags", strings.Join(ClangFilterUnknownCflags(arm64Cflags), " ")) pctx.StaticVariable("Arm64ClangLdflags", strings.Join(ClangFilterUnknownCflags(arm64Ldflags), " ")) @@ -164,7 +163,7 @@ func (t *toolchainArm64) GccVersion() string { } func (t *toolchainArm64) IncludeFlags() string { - return "${config.Arm64IncludeFlags}" + return "" } func (t *toolchainArm64) ClangTriple() string { diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go index d37e48620..f01c63882 100644 --- a/cc/config/arm_device.go +++ b/cc/config/arm_device.go @@ -175,7 +175,6 @@ func init() { pctx.StaticVariable("ArmLdflags", strings.Join(armLdflags, " ")) pctx.StaticVariable("ArmLldflags", strings.Join(armLldflags, " ")) - pctx.StaticVariable("ArmIncludeFlags", bionicHeaders("arm")) // Clang cflags pctx.StaticVariable("ArmToolchainClangCflags", strings.Join(ClangFilterUnknownCflags(armToolchainCflags), " ")) @@ -269,7 +268,7 @@ func (t *toolchainArm) GccVersion() string { } func (t *toolchainArm) IncludeFlags() string { - return "${config.ArmIncludeFlags}" + return "" } func (t *toolchainArm) ClangTriple() string { diff --git a/cc/config/global.go b/cc/config/global.go index 923dd2956..4e51ae924 100644 --- a/cc/config/global.go +++ b/cc/config/global.go @@ -265,16 +265,6 @@ func init() { var HostPrebuiltTag = pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS) -func bionicHeaders(kernelArch string) string { - return strings.Join([]string{ - "-isystem bionic/libc/include", - "-isystem bionic/libc/kernel/uapi", - "-isystem bionic/libc/kernel/uapi/asm-" + kernelArch, - "-isystem bionic/libc/kernel/android/scsi", - "-isystem bionic/libc/kernel/android/uapi", - }, " ") -} - func envOverrideFunc(envVar, defaultVal string) func(ctx android.PackageVarContext) string { return func(ctx android.PackageVarContext) string { if override := ctx.Config().Getenv(envVar); override != "" { diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go index bcfae5d4d..1e25a3b8f 100644 --- a/cc/config/x86_64_device.go +++ b/cc/config/x86_64_device.go @@ -103,7 +103,6 @@ func init() { pctx.StaticVariable("X86_64Ldflags", strings.Join(x86_64Ldflags, " ")) pctx.StaticVariable("X86_64Lldflags", strings.Join(x86_64Lldflags, " ")) - pctx.StaticVariable("X86_64IncludeFlags", bionicHeaders("x86")) // Clang cflags pctx.StaticVariable("X86_64ClangCflags", strings.Join(ClangFilterUnknownCflags(x86_64Cflags), " ")) @@ -145,7 +144,7 @@ func (t *toolchainX86_64) GccVersion() string { } func (t *toolchainX86_64) IncludeFlags() string { - return "${config.X86_64IncludeFlags}" + return "" } func (t *toolchainX86_64) ClangTriple() string { diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go index 64392dc86..fe830982f 100644 --- a/cc/config/x86_device.go +++ b/cc/config/x86_device.go @@ -114,7 +114,6 @@ func init() { pctx.StaticVariable("X86Ldflags", strings.Join(x86Ldflags, " ")) pctx.StaticVariable("X86Lldflags", strings.Join(x86Lldflags, " ")) - pctx.StaticVariable("X86IncludeFlags", bionicHeaders("x86")) // Clang cflags pctx.StaticVariable("X86ClangCflags", strings.Join(ClangFilterUnknownCflags(x86ClangCflags), " ")) @@ -156,7 +155,7 @@ func (t *toolchainX86) GccVersion() string { } func (t *toolchainX86) IncludeFlags() string { - return "${config.X86IncludeFlags}" + return "" } func (t *toolchainX86) ClangTriple() string { diff --git a/cc/config/x86_linux_bionic_host.go b/cc/config/x86_linux_bionic_host.go index fb1cdebe4..fa625e34a 100644 --- a/cc/config/x86_linux_bionic_host.go +++ b/cc/config/x86_linux_bionic_host.go @@ -70,8 +70,6 @@ func init() { pctx.StaticVariable("LinuxBionicLdflags", strings.Join(linuxBionicLdflags, " ")) pctx.StaticVariable("LinuxBionicLldflags", strings.Join(linuxBionicLldflags, " ")) - pctx.StaticVariable("LinuxBionicIncludeFlags", bionicHeaders("x86")) - // Use the device gcc toolchain for now pctx.StaticVariable("LinuxBionicGccRoot", "${X86_64GccRoot}") } @@ -97,7 +95,7 @@ func (t *toolchainLinuxBionic) GccVersion() string { } func (t *toolchainLinuxBionic) IncludeFlags() string { - return "${config.LinuxBionicIncludeFlags}" + return "" } func (t *toolchainLinuxBionic) ClangTriple() string { diff --git a/cc/coverage.go b/cc/coverage.go index bde07fd63..cc9a1adab 100644 --- a/cc/coverage.go +++ b/cc/coverage.go @@ -65,10 +65,10 @@ func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps { if cov.Properties.NeedCoverageVariant { ctx.AddVariationDependencies([]blueprint.Variation{ {Mutator: "link", Variation: "static"}, - }, coverageDepTag, getGcovProfileLibraryName(ctx)) + }, CoverageDepTag, getGcovProfileLibraryName(ctx)) ctx.AddVariationDependencies([]blueprint.Variation{ {Mutator: "link", Variation: "static"}, - }, coverageDepTag, getClangProfileLibraryName(ctx)) + }, CoverageDepTag, getClangProfileLibraryName(ctx)) } return deps } @@ -134,14 +134,14 @@ func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags if gcovCoverage { flags.Local.LdFlags = append(flags.Local.LdFlags, "--coverage") - coverage := ctx.GetDirectDepWithTag(getGcovProfileLibraryName(ctx), coverageDepTag).(*Module) + coverage := ctx.GetDirectDepWithTag(getGcovProfileLibraryName(ctx), CoverageDepTag).(*Module) deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path()) flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv") } else if clangCoverage { flags.Local.LdFlags = append(flags.Local.LdFlags, "-fprofile-instr-generate") - coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), coverageDepTag).(*Module) + coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), CoverageDepTag).(*Module) deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path()) } } @@ -150,25 +150,30 @@ func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags } func (cov *coverage) begin(ctx BaseModuleContext) { + if ctx.Host() { + // TODO(dwillemsen): because of -nodefaultlibs, we must depend on libclang_rt.profile-*.a + // Just turn off for now. + } else { + cov.Properties = SetCoverageProperties(ctx, cov.Properties, ctx.nativeCoverage(), ctx.useSdk(), ctx.sdkVersion()) + } +} + +func SetCoverageProperties(ctx android.BaseModuleContext, properties CoverageProperties, moduleTypeHasCoverage bool, + useSdk bool, sdkVersion string) CoverageProperties { // Coverage is disabled globally if !ctx.DeviceConfig().NativeCoverageEnabled() && !ctx.DeviceConfig().ClangCoverageEnabled() { - return + return properties } var needCoverageVariant bool var needCoverageBuild bool - if ctx.Host() { - // TODO(dwillemsen): because of -nodefaultlibs, we must depend on libclang_rt.profile-*.a - // Just turn off for now. - } else if !ctx.nativeCoverage() { - // Native coverage is not supported for this module type. - } else { + if moduleTypeHasCoverage { // Check if Native_coverage is set to false. This property defaults to true. - needCoverageVariant = BoolDefault(cov.Properties.Native_coverage, true) - if sdk_version := ctx.sdkVersion(); ctx.useSdk() && sdk_version != "current" { + needCoverageVariant = BoolDefault(properties.Native_coverage, true) + if useSdk && sdkVersion != "current" { // Native coverage is not supported for SDK versions < 23 - if fromApi, err := strconv.Atoi(sdk_version); err == nil && fromApi < 23 { + if fromApi, err := strconv.Atoi(sdkVersion); err == nil && fromApi < 23 { needCoverageVariant = false } } @@ -179,8 +184,10 @@ func (cov *coverage) begin(ctx BaseModuleContext) { } } - cov.Properties.NeedCoverageBuild = needCoverageBuild - cov.Properties.NeedCoverageVariant = needCoverageVariant + properties.NeedCoverageBuild = needCoverageBuild + properties.NeedCoverageVariant = needCoverageVariant + + return properties } // Coverage is an interface for non-CC modules to implement to be mutated for coverage @@ -190,6 +197,7 @@ type Coverage interface { PreventInstall() HideFromMake() MarkAsCoverageVariant(bool) + EnableCoverageIfNeeded() } func coverageMutator(mctx android.BottomUpMutatorContext) { @@ -212,14 +220,17 @@ func coverageMutator(mctx android.BottomUpMutatorContext) { m[1].(*Module).coverage.Properties.IsCoverageVariant = true } } else if cov, ok := mctx.Module().(Coverage); ok && cov.IsNativeCoverageNeeded(mctx) { - // APEX modules fall here + // APEX and Rust modules fall here // Note: variant "" is also created because an APEX can be depended on by another // module which are split into "" and "cov" variants. e.g. when cc_test refers // to an APEX via 'data' property. m := mctx.CreateVariations("", "cov") - m[0].(Coverage).MarkAsCoverageVariant(true) + m[0].(Coverage).MarkAsCoverageVariant(false) m[0].(Coverage).PreventInstall() m[0].(Coverage).HideFromMake() + + m[1].(Coverage).MarkAsCoverageVariant(true) + m[1].(Coverage).EnableCoverageIfNeeded() } } diff --git a/cc/fuzz.go b/cc/fuzz.go index 948595bb2..58c1888ad 100644 --- a/cc/fuzz.go +++ b/cc/fuzz.go @@ -35,6 +35,9 @@ type FuzzConfig struct { Componentid *int64 `json:"componentid,omitempty"` // Hotlists in Google's bug tracking system that bugs should be marked with. Hotlists []string `json:"hotlists,omitempty"` + // Specify whether this fuzz target was submitted by a researcher. Defaults + // to false. + Researcher_submitted *bool `json:"researcher_submitted,omitempty"` } func (f *FuzzConfig) String() string { diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go index 2c8e31158..a7a1de251 100644 --- a/cc/library_sdk_member.go +++ b/cc/library_sdk_member.go @@ -307,7 +307,7 @@ type nativeLibInfoProperties struct { // The list of possibly common exported include dirs. // // This field is exported as its contents may not be arch specific. - ExportedIncludeDirs android.Paths + ExportedIncludeDirs android.Paths `android:"arch_variant"` // The list of arch specific exported generated include dirs. // @@ -322,27 +322,31 @@ type nativeLibInfoProperties struct { // The list of possibly common exported system include dirs. // // This field is exported as its contents may not be arch specific. - ExportedSystemIncludeDirs android.Paths + ExportedSystemIncludeDirs android.Paths `android:"arch_variant"` // The list of possibly common exported flags. // // This field is exported as its contents may not be arch specific. - ExportedFlags []string + ExportedFlags []string `android:"arch_variant"` // The set of shared libraries // // This field is exported as its contents may not be arch specific. - SharedLibs []string + SharedLibs []string `android:"arch_variant"` // The set of system shared libraries. Note nil and [] are semantically // distinct - see BaseLinkerProperties.System_shared_libs. // // This field is exported as its contents may not be arch specific. - SystemSharedLibs []string + SystemSharedLibs []string `android:"arch_variant"` // The specific stubs version for the lib variant, or empty string if stubs // are not in use. - StubsVersion string + // + // Marked 'ignored-on-host' as the StubsVersion() from which this is initialized is + // not set on host and the stubs.versions property which this is written to is does + // not vary by arch so cannot be android specific. + StubsVersion string `sdk:"ignored-on-host"` // outputFile is not exported as it is always arch specific. outputFile android.Path diff --git a/cc/linkable.go b/cc/linkable.go index 4a70d48f7..de36f9017 100644 --- a/cc/linkable.go +++ b/cc/linkable.go @@ -12,6 +12,7 @@ type LinkableInterface interface { CcLibraryInterface() bool OutputFile() android.OptionalPath + CoverageFiles() android.Paths IncludeDirs() android.Paths SetDepsInLinkOrder([]android.Path) @@ -83,4 +84,5 @@ var ( CrtBeginDepTag = DependencyTag{Name: "crtbegin"} CrtEndDepTag = DependencyTag{Name: "crtend"} + CoverageDepTag = DependencyTag{Name: "coverage"} ) diff --git a/cc/testing.go b/cc/testing.go index 53f09955a..be020c596 100644 --- a/cc/testing.go +++ b/cc/testing.go @@ -192,6 +192,45 @@ func GatherRequiredDepsForTest(oses ...android.OsType) string { symbol_file: "", sdk_version: "current", } + + // Coverage libraries + cc_library { + name: "libprofile-extras", + vendor_available: true, + recovery_available: true, + native_coverage: false, + system_shared_libs: [], + stl: "none", + notice: "custom_notice", + } + cc_library { + name: "libprofile-clang-extras", + vendor_available: true, + recovery_available: true, + native_coverage: false, + system_shared_libs: [], + stl: "none", + notice: "custom_notice", + } + cc_library { + name: "libprofile-extras_ndk", + vendor_available: true, + native_coverage: false, + system_shared_libs: [], + stl: "none", + notice: "custom_notice", + sdk_version: "current", + } + cc_library { + name: "libprofile-clang-extras_ndk", + vendor_available: true, + native_coverage: false, + system_shared_libs: [], + stl: "none", + notice: "custom_notice", + sdk_version: "current", + } + cc_library { name: "libdl", no_libcrt: true, @@ -485,8 +524,8 @@ func CreateTestContext() *android.TestContext { ctx.RegisterModuleType("filegroup", android.FileGroupFactory) ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory) ctx.RegisterModuleType("vndk_libraries_txt", VndkLibrariesTxtFactory) - RegisterRequiredBuildComponentsForTest(ctx) ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators) + RegisterRequiredBuildComponentsForTest(ctx) ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton) ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton) diff --git a/java/app.go b/java/app.go index d25575c99..43bdc91de 100755 --- a/java/app.go +++ b/java/app.go @@ -80,10 +80,14 @@ type appProperties struct { // list of native libraries that will be provided in or alongside the resulting jar Jni_libs []string `android:"arch_variant"` - // if true, allow JNI libraries that link against platform APIs even if this module sets + // if true, use JNI libraries that link against platform APIs even if this module sets // sdk_version. Jni_uses_platform_apis *bool + // if true, use JNI libraries that link against SDK APIs even if this module does not set + // sdk_version. + Jni_uses_sdk_apis *bool + // STL library to use for JNI libraries. Stl *string `android:"arch_variant"` @@ -234,7 +238,8 @@ func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) { // If the app builds against an Android SDK use the SDK variant of JNI dependencies // unless jni_uses_platform_apis is set. if a.sdkVersion().specified() && a.sdkVersion().kind != sdkCorePlatform && - !Bool(a.appProperties.Jni_uses_platform_apis) { + !Bool(a.appProperties.Jni_uses_platform_apis) || + Bool(a.appProperties.Jni_uses_sdk_apis) { variation = append(variation, blueprint.Variation{Mutator: "sdk", Variation: "sdk"}) } ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...) @@ -714,6 +719,8 @@ func (a *AndroidApp) MarkAsCoverageVariant(coverage bool) { a.appProperties.IsCoverageVariant = coverage } +func (a *AndroidApp) EnableCoverageIfNeeded() {} + var _ cc.Coverage = (*AndroidApp)(nil) // android_app compiles sources and Android resources into an Android application package `.apk` file. @@ -1391,6 +1398,8 @@ func AndroidTestImportFactory() android.Module { module.processVariants(ctx) }) + module.dexpreopter.isTest = true + android.InitApexModule(module) android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) android.InitDefaultableModule(module) diff --git a/java/droiddoc.go b/java/droiddoc.go index 1c46a2b0d..879353d8b 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -1473,46 +1473,16 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { cmd.ImplicitOutput(android.PathForModuleGen(ctx, o)) } - if generateStubs { - rule.Command(). - BuiltTool(ctx, "soong_zip"). - Flag("-write_if_changed"). - Flag("-jar"). - FlagWithOutput("-o ", d.Javadoc.stubsSrcJar). - FlagWithArg("-C ", stubsDir.String()). - FlagWithArg("-D ", stubsDir.String()) - } - - if Bool(d.properties.Write_sdk_values) { - d.metadataZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-metadata.zip") - rule.Command(). - BuiltTool(ctx, "soong_zip"). - Flag("-write_if_changed"). - Flag("-d"). - FlagWithOutput("-o ", d.metadataZip). - FlagWithArg("-C ", d.metadataDir.String()). - FlagWithArg("-D ", d.metadataDir.String()) - } + // Add options for the other optional tasks: API-lint and check-released. + // We generate separate timestamp files for them. - rule.Restat() + doApiLint := false + doCheckReleased := false - zipSyncCleanupCmd(rule, srcJarDir) - - rule.Build(pctx, ctx, "metalava", "metalava") - - // Create rule for apicheck + // Add API lint options. if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) && !ctx.Config().IsPdkBuild() { - rule := android.NewRuleBuilder() - rule.Command().Text("( true") - - srcJarDir := android.PathForModuleOut(ctx, "api_lint", "srcjars") - srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars) - - cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList, - deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths) - - cmd.Flag(d.Javadoc.args).Implicits(d.Javadoc.argFiles) + doApiLint = true newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since) if newSince.Valid() { @@ -1521,35 +1491,39 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { cmd.Flag("--api-lint") } d.apiLintReport = android.PathForModuleOut(ctx, "api_lint_report.txt") - cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) - - d.inclusionAnnotationsFlags(ctx, cmd) - d.mergeAnnoDirFlags(ctx, cmd) + cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) // TODO: Change to ":api-lint" baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file) updatedBaselineOutput := android.PathForModuleOut(ctx, "api_lint_baseline.txt") d.apiLintTimestamp = android.PathForModuleOut(ctx, "api_lint.timestamp") - msg := `` + + // Note this string includes a special shell quote $' ... ', which decodes the "\n"s. + // However, because $' ... ' doesn't expand environmental variables, we can't just embed + // $PWD, so we have to terminate $'...', use "$PWD", then start $' ... ' again, + // which is why we have '"$PWD"$' in it. + // + // TODO: metalava also has a slightly different message hardcoded. Should we unify this + // message and metalava's one? + msg := `$'` + // Enclose with $' ... ' `************************************************************\n` + `Your API changes are triggering API Lint warnings or errors.\n` + `To make these errors go away, fix the code according to the\n` + `error and/or warning messages above.\n` + `\n` + - `If it's not possible to do so, there are workarounds:\n` + + `If it is not possible to do so, there are workarounds:\n` + `\n` + - `1. You can suppress the errors with @SuppressLint(\"<id>\")\n` + `1. You can suppress the errors with @SuppressLint("<id>")\n` if baselineFile.Valid() { - cmd.FlagWithInput("--baseline ", baselineFile.Path()) - cmd.FlagWithOutput("--update-baseline ", updatedBaselineOutput) + cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path()) + cmd.FlagWithOutput("--update-baseline:api-lint ", updatedBaselineOutput) msg += fmt.Sprintf(``+ `2. You can update the baseline by executing the following\n`+ ` command:\n`+ ` cp \\ \n`+ - ` \"$PWD/%s\" \\ \n`+ - ` \"$PWD/%s\" \n`+ + ` "'"$PWD"$'/%s" \\ \n`+ + ` "'"$PWD"$'/%s" \n`+ ` To submit the revised baseline.txt to the main Android\n`+ ` repository, you will need approval.\n`, updatedBaselineOutput, baselineFile.Path()) } else { @@ -1557,20 +1531,81 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { `2. You can add a baseline file of existing lint failures\n`+ ` to the build rule of %s.\n`, d.Name()) } - msg += `************************************************************\n` + // Note the message ends with a ' (single quote), to close the $' ... ' . + msg += `************************************************************\n'` - zipSyncCleanupCmd(rule, srcJarDir) + cmd.FlagWithArg("--error-message:api-lint ", msg) + } + + // Add "check released" options. (Detect incompatible API changes from the last public release) + + if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") && + !ctx.Config().IsPdkBuild() { + doCheckReleased = true + + if len(d.Javadoc.properties.Out) > 0 { + ctx.PropertyErrorf("out", "out property may not be combined with check_api") + } + apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file)) + removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file)) + baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file) + updatedBaselineOutput := android.PathForModuleOut(ctx, "last_released_baseline.txt") + + d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "check_last_released_api.timestamp") + + cmd.FlagWithInput("--check-compatibility:api:released ", apiFile) + cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile) + + if baselineFile.Valid() { + cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path()) + cmd.FlagWithOutput("--update-baseline:compatibility:released ", updatedBaselineOutput) + } + + // Note this string includes quote ($' ... '), which decodes the "\n"s. + msg := `$'\n******************************\n` + + `You have tried to change the API from what has been previously released in\n` + + `an SDK. Please fix the errors listed above.\n` + + `******************************\n'` + + cmd.FlagWithArg("--error-message:compatibility:released ", msg) + } + + if generateStubs { rule.Command(). - Text("touch").Output(d.apiLintTimestamp). - Text(") || ("). - Text("echo").Flag("-e").Flag(`"` + msg + `"`). - Text("; exit 38"). - Text(")") + BuiltTool(ctx, "soong_zip"). + Flag("-write_if_changed"). + Flag("-jar"). + FlagWithOutput("-o ", d.Javadoc.stubsSrcJar). + FlagWithArg("-C ", stubsDir.String()). + FlagWithArg("-D ", stubsDir.String()) + } - rule.Build(pctx, ctx, "metalavaApiLint", "metalava API lint") + if Bool(d.properties.Write_sdk_values) { + d.metadataZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-metadata.zip") + rule.Command(). + BuiltTool(ctx, "soong_zip"). + Flag("-write_if_changed"). + Flag("-d"). + FlagWithOutput("-o ", d.metadataZip). + FlagWithArg("-C ", d.metadataDir.String()). + FlagWithArg("-D ", d.metadataDir.String()) + } + // TODO: We don't really need two separate API files, but this is a reminiscence of how + // we used to run metalava separately for API lint and the "last_released" check. Unify them. + if doApiLint { + rule.Command().Text("touch").Output(d.apiLintTimestamp) } + if doCheckReleased { + rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp) + } + + rule.Restat() + + zipSyncCleanupCmd(rule, srcJarDir) + + rule.Build(pctx, ctx, "metalava", "metalava merged") if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") && !ctx.Config().IsPdkBuild() { @@ -1584,7 +1619,7 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file) if baselineFile.Valid() { - ctx.PropertyErrorf("current API check can't have a baseline file. (module %s)", ctx.ModuleName()) + ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName()) } d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "check_current_api.timestamp") @@ -1592,8 +1627,8 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { rule := android.NewRuleBuilder() // Diff command line. - // -F matches the closest "opening" line, such as "package xxx{" - // and " public class Yyy {". + // -F matches the closest "opening" line, such as "package android {" + // and " public class Intent {". diff := `diff -u -F '{ *$'` rule.Command().Text("( true") @@ -1652,60 +1687,6 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { rule.Build(pctx, ctx, "metalavaCurrentApiUpdate", "update current API") } - if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") && - !ctx.Config().IsPdkBuild() { - - if len(d.Javadoc.properties.Out) > 0 { - ctx.PropertyErrorf("out", "out property may not be combined with check_api") - } - - apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file)) - removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file)) - baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file) - updatedBaselineOutput := android.PathForModuleOut(ctx, "last_released_baseline.txt") - - d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "check_last_released_api.timestamp") - - rule := android.NewRuleBuilder() - - rule.Command().Text("( true") - - srcJarDir := android.PathForModuleOut(ctx, "last-apicheck", "srcjars") - srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars) - - cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList, - deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths) - - cmd.Flag(d.Javadoc.args).Implicits(d.Javadoc.argFiles). - FlagWithInput("--check-compatibility:api:released ", apiFile) - - d.inclusionAnnotationsFlags(ctx, cmd) - - cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile) - - d.mergeAnnoDirFlags(ctx, cmd) - - if baselineFile.Valid() { - cmd.FlagWithInput("--baseline ", baselineFile.Path()) - cmd.FlagWithOutput("--update-baseline ", updatedBaselineOutput) - } - - zipSyncCleanupCmd(rule, srcJarDir) - - msg := `\n******************************\n` + - `You have tried to change the API from what has been previously released in\n` + - `an SDK. Please fix the errors listed above.\n` + - `******************************\n` - rule.Command(). - Text("touch").Output(d.checkLastReleasedApiTimestamp). - Text(") || ("). - Text("echo").Flag("-e").Flag(`"` + msg + `"`). - Text("; exit 38"). - Text(")") - - rule.Build(pctx, ctx, "metalavaLastApiCheck", "metalava check last API") - } - if String(d.properties.Check_nullability_warnings) != "" { if d.nullabilityWarningsFile == nil { ctx.PropertyErrorf("check_nullability_warnings", diff --git a/java/java.go b/java/java.go index 472d3dac4..9d75c74c7 100644 --- a/java/java.go +++ b/java/java.go @@ -1906,7 +1906,7 @@ func (mt *librarySdkMemberType) CreateVariantPropertiesStruct() android.SdkMembe type librarySdkMemberProperties struct { android.SdkMemberPropertiesBase - JarToExport android.Path + JarToExport android.Path `android:"arch_variant"` AidlIncludeDirs android.Paths } diff --git a/java/sdk_library.go b/java/sdk_library.go index d70f6320d..39c118d34 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -67,6 +67,9 @@ type apiScope struct { // The name of the api scope, e.g. public, system, test name string + // The api scope that this scope extends. + extends *apiScope + // The name of the field in the dynamically created structure. fieldName string @@ -134,6 +137,7 @@ var ( }) apiScopeSystem = initApiScope(&apiScope{ name: "system", + extends: apiScopePublic, apiFilePrefix: "system-", moduleSuffix: sdkSystemApiSuffix, sdkVersion: "system_current", @@ -141,6 +145,7 @@ var ( }) apiScopeTest = initApiScope(&apiScope{ name: "test", + extends: apiScopePublic, apiFilePrefix: "test-", moduleSuffix: sdkTestApiSuffix, sdkVersion: "test_current", @@ -382,7 +387,7 @@ func (module *SdkLibrary) apiDistPath(apiScope *apiScope) string { } // Get the sdk version for use when compiling the stubs library. -func (module *SdkLibrary) sdkVersionForStubsLibrary(mctx android.LoadHookContext, apiScope *apiScope) string { +func (module *SdkLibrary) sdkVersionForStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) string { sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library)) if sdkDep.hasStandardLibs() { // If building against a standard sdk then use the sdk version appropriate for the scope. @@ -402,7 +407,7 @@ func (module *SdkLibrary) latestRemovedApiFilegroupName(apiScope *apiScope) stri } // Creates a static java library that has API stubs -func (module *SdkLibrary) createStubsLibrary(mctx android.LoadHookContext, apiScope *apiScope) { +func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) { props := struct { Name *string Srcs []string @@ -473,7 +478,7 @@ func (module *SdkLibrary) createStubsLibrary(mctx android.LoadHookContext, apiSc // Creates a droidstubs module that creates stubs source files from the given full source // files -func (module *SdkLibrary) createStubsSources(mctx android.LoadHookContext, apiScope *apiScope) { +func (module *SdkLibrary) createStubsSources(mctx android.DefaultableHookContext, apiScope *apiScope) { props := struct { Name *string Srcs []string @@ -592,7 +597,7 @@ func (module *SdkLibrary) DepIsInSameApex(mctx android.BaseModuleContext, dep an } // Creates the xml file that publicizes the runtime library -func (module *SdkLibrary) createXmlFile(mctx android.LoadHookContext) { +func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) { props := struct { Name *string Lib_name *string @@ -713,7 +718,12 @@ func (module *SdkLibrary) getApiDir() string { // For a java_sdk_library module, create internal modules for stubs, docs, // runtime libs and xml file. If requested, the stubs and docs are created twice // once for public API level and once for system API level -func (module *SdkLibrary) CreateInternalModules(mctx android.LoadHookContext) { +func (module *SdkLibrary) CreateInternalModules(mctx android.DefaultableHookContext) { + // If the module has been disabled then don't create any child modules. + if !module.Enabled() { + return + } + if len(module.Library.Module.properties.Srcs) == 0 { mctx.PropertyErrorf("srcs", "java_sdk_library must specify srcs") return @@ -799,7 +809,7 @@ func SdkLibraryFactory() android.Module { module.InitSdkLibraryProperties() android.InitApexModule(module) InitJavaModule(module, android.HostAndDeviceSupported) - android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.CreateInternalModules(ctx) }) + module.SetDefaultableHook(func(ctx android.DefaultableHookContext) { module.CreateInternalModules(ctx) }) return module } diff --git a/rust/androidmk.go b/rust/androidmk.go index 0fba739a2..0e2bea340 100644 --- a/rust/androidmk.go +++ b/rust/androidmk.go @@ -46,6 +46,12 @@ func (mod *Module) subAndroidMk(data *android.AndroidMkData, obj interface{}) { } func (mod *Module) AndroidMk() android.AndroidMkData { + if mod.Properties.HideFromMake { + return android.AndroidMkData{ + Disabled: true, + } + } + ret := android.AndroidMkData{ OutputFile: mod.outputFile, Include: "$(BUILD_SYSTEM)/soong_rust_prebuilt.mk", @@ -84,6 +90,9 @@ func (binary *binaryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.Andr ret.DistFile = binary.distFile ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) { fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", binary.unstrippedOutputFile.String()) + if binary.coverageOutputZipFile.Valid() { + fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE := "+binary.coverageOutputZipFile.String()) + } }) } @@ -124,6 +133,10 @@ func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.An if !library.rlib() { fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", library.unstrippedOutputFile.String()) } + if library.coverageOutputZipFile.Valid() { + fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE := "+library.coverageOutputZipFile.String()) + } + }) } diff --git a/rust/binary.go b/rust/binary.go index fda056e43..c25ae0969 100644 --- a/rust/binary.go +++ b/rust/binary.go @@ -35,9 +35,10 @@ type BinaryCompilerProperties struct { type binaryDecorator struct { *baseCompiler - Properties BinaryCompilerProperties - distFile android.OptionalPath - unstrippedOutputFile android.Path + Properties BinaryCompilerProperties + distFile android.OptionalPath + coverageOutputZipFile android.OptionalPath + unstrippedOutputFile android.Path } var _ compiler = (*binaryDecorator)(nil) @@ -104,6 +105,10 @@ func (binary *binaryDecorator) compilerProps() []interface{} { &binary.Properties) } +func (binary *binaryDecorator) nativeCoverage() bool { + return true +} + func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path { fileName := binary.getStem(ctx) + ctx.toolchain().ExecutableSuffix() @@ -114,7 +119,21 @@ func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps Path flags.RustFlags = append(flags.RustFlags, deps.depFlags...) - TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile, deps.linkDirs) + outputs := TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile, deps.linkDirs) + binary.coverageFile = outputs.coverageFile + + var coverageFiles android.Paths + if outputs.coverageFile != nil { + coverageFiles = append(coverageFiles, binary.coverageFile) + } + if len(deps.coverageFiles) > 0 { + coverageFiles = append(coverageFiles, deps.coverageFiles...) + } + binary.coverageOutputZipFile = TransformCoverageFilesToZip(ctx, coverageFiles, binary.getStem(ctx)) return outputFile } + +func (binary *binaryDecorator) coverageOutputZipPath() android.OptionalPath { + return binary.coverageOutputZipFile +} diff --git a/rust/builder.go b/rust/builder.go index 2d5e602ca..fbe0e5372 100644 --- a/rust/builder.go +++ b/rust/builder.go @@ -18,6 +18,7 @@ import ( "strings" "github.com/google/blueprint" + "github.com/google/blueprint/pathtools" "android/soong/android" ) @@ -36,44 +37,57 @@ var ( Depfile: "$out.d", }, "rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd") + + zip = pctx.AndroidStaticRule("zip", + blueprint.RuleParams{ + Command: "cat $out.rsp | tr ' ' '\\n' | tr -d \\' | sort -u > ${out}.tmp && ${SoongZipCmd} -o ${out} -C $$OUT_DIR -l ${out}.tmp", + CommandDeps: []string{"${SoongZipCmd}"}, + Rspfile: "$out.rsp", + RspfileContent: "$in", + }) ) -func init() { +type buildOutput struct { + outputFile android.Path + coverageFile android.Path +} +func init() { + pctx.HostBinToolVariable("SoongZipCmd", "soong_zip") } func TransformSrcToBinary(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, - outputFile android.WritablePath, includeDirs []string) { + outputFile android.WritablePath, includeDirs []string) buildOutput { flags.RustFlags = append(flags.RustFlags, "-C lto") - transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin", includeDirs) + return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin", includeDirs) } func TransformSrctoRlib(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, - outputFile android.WritablePath, includeDirs []string) { - transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib", includeDirs) + outputFile android.WritablePath, includeDirs []string) buildOutput { + return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib", includeDirs) } func TransformSrctoDylib(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, - outputFile android.WritablePath, includeDirs []string) { - transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib", includeDirs) + outputFile android.WritablePath, includeDirs []string) buildOutput { + return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib", includeDirs) } func TransformSrctoStatic(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, - outputFile android.WritablePath, includeDirs []string) { + outputFile android.WritablePath, includeDirs []string) buildOutput { flags.RustFlags = append(flags.RustFlags, "-C lto") - transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib", includeDirs) + return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib", includeDirs) } func TransformSrctoShared(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, - outputFile android.WritablePath, includeDirs []string) { + outputFile android.WritablePath, includeDirs []string) buildOutput { flags.RustFlags = append(flags.RustFlags, "-C lto") - transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib", includeDirs) + return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib", includeDirs) } func TransformSrctoProcMacro(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, - flags Flags, outputFile android.WritablePath, includeDirs []string) { - transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro", includeDirs) + flags Flags, outputFile android.WritablePath, includeDirs []string) buildOutput { + return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro", includeDirs) } func rustLibsToPaths(libs RustLibraries) android.Paths { @@ -85,11 +99,15 @@ func rustLibsToPaths(libs RustLibraries) android.Paths { } func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps PathDeps, flags Flags, - outputFile android.WritablePath, crate_type string, includeDirs []string) { + outputFile android.WritablePath, crate_type string, includeDirs []string) buildOutput { var inputs android.Paths var implicits android.Paths + var output buildOutput var libFlags, rustcFlags, linkFlags []string + var implicitOutputs android.WritablePaths + + output.outputFile = outputFile crate_name := ctx.(ModuleContext).CrateName() targetTriple := ctx.(ModuleContext).toolchain().RustTriple() @@ -141,12 +159,26 @@ func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps Path implicits = append(implicits, deps.CrtBegin.Path(), deps.CrtEnd.Path()) } + if flags.Coverage { + var gcnoFile android.WritablePath + + if outputFile.Ext() != "" { + gcnoFile = android.PathForModuleOut(ctx, pathtools.ReplaceExtension(outputFile.Base(), "gcno")) + } else { + gcnoFile = android.PathForModuleOut(ctx, outputFile.Base()+".gcno") + } + + implicitOutputs = append(implicitOutputs, gcnoFile) + output.coverageFile = gcnoFile + } + ctx.Build(pctx, android.BuildParams{ - Rule: rustc, - Description: "rustc " + main.Rel(), - Output: outputFile, - Inputs: inputs, - Implicits: implicits, + Rule: rustc, + Description: "rustc " + main.Rel(), + Output: outputFile, + ImplicitOutputs: implicitOutputs, + Inputs: inputs, + Implicits: implicits, Args: map[string]string{ "rustcFlags": strings.Join(rustcFlags, " "), "linkFlags": strings.Join(linkFlags, " "), @@ -156,4 +188,23 @@ func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps Path }, }) + return output +} + +func TransformCoverageFilesToZip(ctx android.ModuleContext, + covFiles android.Paths, baseName string) android.OptionalPath { + if len(covFiles) > 0 { + + outputFile := android.PathForModuleOut(ctx, baseName+".zip") + + ctx.Build(pctx, android.BuildParams{ + Rule: zip, + Description: "zip " + outputFile.Base(), + Inputs: covFiles, + Output: outputFile, + }) + + return android.OptionalPathForPath(outputFile) + } + return android.OptionalPath{} } diff --git a/rust/compiler.go b/rust/compiler.go index 74997761b..5f098bc04 100644 --- a/rust/compiler.go +++ b/rust/compiler.go @@ -110,6 +110,7 @@ type baseCompiler struct { linkDirs []string edition string src android.Path //rustc takes a single src file + coverageFile android.Path //rustc generates a single gcno file // Install related dir string @@ -120,6 +121,10 @@ type baseCompiler struct { location installLocation } +func (compiler *baseCompiler) coverageOutputZipPath() android.OptionalPath { + panic("baseCompiler does not implement coverageOutputZipPath()") +} + var _ compiler = (*baseCompiler)(nil) func (compiler *baseCompiler) inData() bool { @@ -235,6 +240,10 @@ func (compiler *baseCompiler) installDir(ctx ModuleContext) android.InstallPath compiler.relativeInstallPath(), compiler.relative) } +func (compiler *baseCompiler) nativeCoverage() bool { + return false +} + func (compiler *baseCompiler) install(ctx ModuleContext, file android.Path) { compiler.path = ctx.InstallFile(compiler.installDir(ctx), file.Base(), file) } diff --git a/rust/coverage.go b/rust/coverage.go new file mode 100644 index 000000000..9be57dccf --- /dev/null +++ b/rust/coverage.go @@ -0,0 +1,72 @@ +// Copyright 2020 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 rust + +import ( + "github.com/google/blueprint" + + "android/soong/cc" +) + +var CovLibraryName = "libprofile-extras" + +type coverage struct { + Properties cc.CoverageProperties + + // Whether binaries containing this module need --coverage added to their ldflags + linkCoverage bool +} + +func (cov *coverage) props() []interface{} { + return []interface{}{&cov.Properties} +} + +func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps { + if cov.Properties.NeedCoverageVariant { + ctx.AddVariationDependencies([]blueprint.Variation{ + {Mutator: "link", Variation: "static"}, + }, cc.CoverageDepTag, CovLibraryName) + } + + return deps +} + +func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) { + + if !ctx.DeviceConfig().NativeCoverageEnabled() && !ctx.DeviceConfig().ClangCoverageEnabled() { + return flags, deps + } + + if cov.Properties.CoverageEnabled { + flags.Coverage = true + coverage := ctx.GetDirectDepWithTag(CovLibraryName, cc.CoverageDepTag).(cc.LinkableInterface) + flags.RustFlags = append(flags.RustFlags, + "-Z profile", "-g", "-C opt-level=0", "-C link-dead-code", "-Z no-landing-pads") + flags.LinkFlags = append(flags.LinkFlags, + "--coverage", "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,getenv") + deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path()) + } + + return flags, deps +} + +func (cov *coverage) begin(ctx BaseModuleContext) { + if ctx.Host() { + // Host coverage not yet supported. + } else { + // Update useSdk and sdkVersion args if Rust modules become SDK aware. + cov.Properties = cc.SetCoverageProperties(ctx, cov.Properties, ctx.nativeCoverage(), false, "") + } +} diff --git a/rust/coverage_test.go b/rust/coverage_test.go new file mode 100644 index 000000000..27acad325 --- /dev/null +++ b/rust/coverage_test.go @@ -0,0 +1,181 @@ +// Copyright 2020 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 rust + +import ( + "strings" + "testing" + + "android/soong/android" +) + +// Test that coverage flags are being correctly generated. +func TestCoverageFlags(t *testing.T) { + ctx := testRustCov(t, ` + rust_library { + name: "libfoo_cov", + srcs: ["foo.rs"], + crate_name: "foo", + } + rust_binary { + name: "fizz_cov", + srcs: ["foo.rs"], + } + rust_binary { + name: "buzzNoCov", + srcs: ["foo.rs"], + native_coverage: false, + } + rust_library { + name: "libbar_nocov", + srcs: ["foo.rs"], + crate_name: "bar", + native_coverage: false, + }`) + + // Make sure native_coverage: false isn't creating a coverage variant. + if android.InList("android_arm64_armv8-a_dylib_cov", ctx.ModuleVariantsForTests("libbar_nocov")) { + t.Fatalf("coverage variant created for module 'libbar_nocov' with native coverage disabled") + } + + // Just test the dylib variants unless the library coverage logic changes to distinguish between the types. + libfooCov := ctx.ModuleForTests("libfoo_cov", "android_arm64_armv8-a_dylib_cov").Rule("rustc") + libbarNoCov := ctx.ModuleForTests("libbar_nocov", "android_arm64_armv8-a_dylib").Rule("rustc") + fizzCov := ctx.ModuleForTests("fizz_cov", "android_arm64_armv8-a_cov").Rule("rustc") + buzzNoCov := ctx.ModuleForTests("buzzNoCov", "android_arm64_armv8-a").Rule("rustc") + + rustcCoverageFlags := []string{"-Z profile", " -g ", "-C opt-level=0", "-C link-dead-code", "-Z no-landing-pads"} + for _, flag := range rustcCoverageFlags { + missingErrorStr := "missing rustc flag '%s' for '%s' module with coverage enabled; rustcFlags: %#v" + containsErrorStr := "contains rustc flag '%s' for '%s' module with coverage disabled; rustcFlags: %#v" + + if !strings.Contains(fizzCov.Args["rustcFlags"], flag) { + t.Fatalf(missingErrorStr, flag, "fizz_cov", fizzCov.Args["rustcFlags"]) + } + if !strings.Contains(libfooCov.Args["rustcFlags"], flag) { + t.Fatalf(missingErrorStr, flag, "libfoo_cov dylib", libfooCov.Args["rustcFlags"]) + } + if strings.Contains(buzzNoCov.Args["rustcFlags"], flag) { + t.Fatalf(containsErrorStr, flag, "buzzNoCov", buzzNoCov.Args["rustcFlags"]) + } + if strings.Contains(libbarNoCov.Args["rustcFlags"], flag) { + t.Fatalf(containsErrorStr, flag, "libbar_cov", libbarNoCov.Args["rustcFlags"]) + } + } + + linkCoverageFlags := []string{"--coverage", " -g "} + for _, flag := range linkCoverageFlags { + missingErrorStr := "missing rust linker flag '%s' for '%s' module with coverage enabled; rustcFlags: %#v" + containsErrorStr := "contains rust linker flag '%s' for '%s' module with coverage disabled; rustcFlags: %#v" + + if !strings.Contains(fizzCov.Args["linkFlags"], flag) { + t.Fatalf(missingErrorStr, flag, "fizz_cov", fizzCov.Args["linkFlags"]) + } + if !strings.Contains(libfooCov.Args["linkFlags"], flag) { + t.Fatalf(missingErrorStr, flag, "libfoo_cov dylib", libfooCov.Args["linkFlags"]) + } + if strings.Contains(buzzNoCov.Args["linkFlags"], flag) { + t.Fatalf(containsErrorStr, flag, "buzzNoCov", buzzNoCov.Args["linkFlags"]) + } + if strings.Contains(libbarNoCov.Args["linkFlags"], flag) { + t.Fatalf(containsErrorStr, flag, "libbar_cov", libbarNoCov.Args["linkFlags"]) + } + } + +} + +// Test coverage files are included correctly +func TestCoverageZip(t *testing.T) { + ctx := testRustCov(t, ` + rust_library { + name: "libfoo", + srcs: ["foo.rs"], + rlibs: ["librlib"], + crate_name: "foo", + } + rust_library_rlib { + name: "librlib", + srcs: ["foo.rs"], + crate_name: "rlib", + } + rust_binary { + name: "fizz", + rlibs: ["librlib"], + static_libs: ["libfoo"], + srcs: ["foo.rs"], + } + cc_binary { + name: "buzz", + static_libs: ["libfoo"], + srcs: ["foo.c"], + } + cc_library { + name: "libbar", + static_libs: ["libfoo"], + compile_multilib: "64", + srcs: ["foo.c"], + }`) + + fizzZipInputs := ctx.ModuleForTests("fizz", "android_arm64_armv8-a_cov").Rule("zip").Inputs.Strings() + libfooZipInputs := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib_cov").Rule("zip").Inputs.Strings() + buzzZipInputs := ctx.ModuleForTests("buzz", "android_arm64_armv8-a_cov").Rule("zip").Inputs.Strings() + libbarZipInputs := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared_cov").Rule("zip").Inputs.Strings() + + // Make sure the expected number of input files are included. + if len(fizzZipInputs) != 3 { + t.Fatalf("expected only 3 coverage inputs for rust 'fizz' binary, got %#v: %#v", len(fizzZipInputs), fizzZipInputs) + } + if len(libfooZipInputs) != 2 { + t.Fatalf("expected only 2 coverage inputs for rust 'libfoo' library, got %#v: %#v", len(libfooZipInputs), libfooZipInputs) + } + if len(buzzZipInputs) != 2 { + t.Fatalf("expected only 2 coverage inputs for cc 'buzz' binary, got %#v: %#v", len(buzzZipInputs), buzzZipInputs) + } + if len(libbarZipInputs) != 2 { + t.Fatalf("expected only 2 coverage inputs for cc 'libbar' library, got %#v: %#v", len(libbarZipInputs), libbarZipInputs) + } + + // Make sure the expected inputs are provided to the zip rule. + if !android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_rlib_cov/librlib.gcno") || + !android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_static_cov/libfoo.gcno") || + !android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_cov/fizz.gcno") { + t.Fatalf("missing expected coverage files for rust 'fizz' binary: %#v", fizzZipInputs) + } + if !android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_rlib_cov/librlib.gcno") || + !android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_dylib_cov/libfoo.dylib.gcno") { + t.Fatalf("missing expected coverage files for rust 'fizz' binary: %#v", libfooZipInputs) + } + if !android.SuffixInList(buzzZipInputs, "android_arm64_armv8-a_cov/obj/foo.gcno") || + !android.SuffixInList(buzzZipInputs, "android_arm64_armv8-a_static_cov/libfoo.gcno") { + t.Fatalf("missing expected coverage files for cc 'buzz' binary: %#v", buzzZipInputs) + } + if !android.SuffixInList(libbarZipInputs, "android_arm64_armv8-a_static_cov/obj/foo.gcno") || + !android.SuffixInList(libbarZipInputs, "android_arm64_armv8-a_static_cov/libfoo.gcno") { + t.Fatalf("missing expected coverage files for cc 'libbar' library: %#v", libbarZipInputs) + } +} + +func TestCoverageDeps(t *testing.T) { + ctx := testRustCov(t, ` + rust_binary { + name: "fizz", + srcs: ["foo.rs"], + }`) + + fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a_cov").Rule("rustc") + if !strings.Contains(fizz.Args["linkFlags"], "libprofile-extras.a") { + t.Fatalf("missing expected coverage 'libprofile-extras' dependency in linkFlags: %#v", fizz.Args["linkFlags"]) + } +} diff --git a/rust/library.go b/rust/library.go index 87e816dba..8aa033c0f 100644 --- a/rust/library.go +++ b/rust/library.go @@ -75,11 +75,12 @@ type LibraryMutatedProperties struct { type libraryDecorator struct { *baseCompiler - Properties LibraryCompilerProperties - MutatedProperties LibraryMutatedProperties - distFile android.OptionalPath - unstrippedOutputFile android.Path - includeDirs android.Paths + Properties LibraryCompilerProperties + MutatedProperties LibraryMutatedProperties + distFile android.OptionalPath + coverageOutputZipFile android.OptionalPath + unstrippedOutputFile android.Path + includeDirs android.Paths } type libraryInterface interface { @@ -107,6 +108,10 @@ type libraryInterface interface { BuildOnlyShared() } +func (library *libraryDecorator) nativeCoverage() bool { + return true +} + func (library *libraryDecorator) exportedDirs() []string { return library.linkDirs } @@ -351,23 +356,36 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa fileName := library.getStem(ctx) + ctx.toolchain().RlibSuffix() outputFile = android.PathForModuleOut(ctx, fileName) - TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs) + outputs := TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs) + library.coverageFile = outputs.coverageFile } else if library.dylib() { fileName := library.getStem(ctx) + ctx.toolchain().DylibSuffix() outputFile = android.PathForModuleOut(ctx, fileName) - TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs) + outputs := TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs) + library.coverageFile = outputs.coverageFile } else if library.static() { fileName := library.getStem(ctx) + ctx.toolchain().StaticLibSuffix() outputFile = android.PathForModuleOut(ctx, fileName) - TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile, deps.linkDirs) + outputs := TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile, deps.linkDirs) + library.coverageFile = outputs.coverageFile } else if library.shared() { fileName := library.getStem(ctx) + ctx.toolchain().SharedLibSuffix() outputFile = android.PathForModuleOut(ctx, fileName) - TransformSrctoShared(ctx, srcPath, deps, flags, outputFile, deps.linkDirs) + outputs := TransformSrctoShared(ctx, srcPath, deps, flags, outputFile, deps.linkDirs) + library.coverageFile = outputs.coverageFile + } + + var coverageFiles android.Paths + if library.coverageFile != nil { + coverageFiles = append(coverageFiles, library.coverageFile) + } + if len(deps.coverageFiles) > 0 { + coverageFiles = append(coverageFiles, deps.coverageFiles...) } + library.coverageOutputZipFile = TransformCoverageFilesToZip(ctx, coverageFiles, library.getStem(ctx)) if library.rlib() || library.dylib() { library.reexportDirs(deps.linkDirs...) diff --git a/rust/prebuilt.go b/rust/prebuilt.go index 45bef9ea3..1d9765053 100644 --- a/rust/prebuilt.go +++ b/rust/prebuilt.go @@ -69,3 +69,7 @@ func (prebuilt *prebuiltLibraryDecorator) compilerDeps(ctx DepsContext, deps Dep deps = prebuilt.baseCompiler.compilerDeps(ctx, deps) return deps } + +func (prebuilt *prebuiltLibraryDecorator) nativeCoverage() bool { + return false +} diff --git a/rust/rust.go b/rust/rust.go index 5cc884572..8cf2e6df1 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -40,6 +40,7 @@ func init() { android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) { ctx.BottomUp("rust_libraries", LibraryMutator).Parallel() ctx.BottomUp("rust_unit_tests", TestPerSrcMutator).Parallel() + ctx.BottomUp("rust_begin", BeginMutator).Parallel() }) pctx.Import("android/soong/rust/config") } @@ -51,6 +52,7 @@ type Flags struct { LinkFlags []string // Flags that apply to linker RustFlagsDeps android.Paths // Files depended on by compiler flags Toolchain config.Toolchain + Coverage bool } type BaseProperties struct { @@ -60,6 +62,8 @@ type BaseProperties struct { AndroidMkSharedLibs []string AndroidMkStaticLibs []string SubName string `blueprint:"mutated"` + PreventInstall bool + HideFromMake bool } type Module struct { @@ -72,6 +76,7 @@ type Module struct { multilib android.Multilib compiler compiler + coverage *coverage cachedToolchain config.Toolchain subAndroidMkOnce map[subAndroidMkProvider]bool outputFile android.OptionalPath @@ -224,6 +229,8 @@ type PathDeps struct { depFlags []string //ReexportedDeps android.Paths + coverageFiles android.Paths + CrtBegin android.OptionalPath CrtEnd android.OptionalPath } @@ -245,6 +252,34 @@ type compiler interface { inData() bool install(ctx ModuleContext, path android.Path) relativeInstallPath() string + + nativeCoverage() bool +} + +func (mod *Module) isCoverageVariant() bool { + return mod.coverage.Properties.IsCoverageVariant +} + +var _ cc.Coverage = (*Module)(nil) + +func (mod *Module) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool { + return mod.coverage != nil && mod.coverage.Properties.NeedCoverageVariant +} + +func (mod *Module) PreventInstall() { + mod.Properties.PreventInstall = true +} + +func (mod *Module) HideFromMake() { + mod.Properties.HideFromMake = true +} + +func (mod *Module) MarkAsCoverageVariant(coverage bool) { + mod.coverage.Properties.IsCoverageVariant = coverage +} + +func (mod *Module) EnableCoverageIfNeeded() { + mod.coverage.Properties.CoverageEnabled = mod.coverage.Properties.NeedCoverageBuild } func defaultsFactory() android.Module { @@ -268,6 +303,7 @@ func DefaultsFactory(props ...interface{}) android.Module { &ProcMacroCompilerProperties{}, &PrebuiltProperties{}, &TestProperties{}, + &cc.CoverageProperties{}, ) android.InitDefaultsModule(module) @@ -395,6 +431,18 @@ func (mod *Module) HasStaticVariant() bool { return false } +func (mod *Module) CoverageFiles() android.Paths { + if mod.compiler != nil { + if library, ok := mod.compiler.(*libraryDecorator); ok { + if library.coverageFile != nil { + return android.Paths{library.coverageFile} + } + return android.Paths{} + } + } + panic(fmt.Errorf("CoverageFiles called on non-library module: %q", mod.BaseModuleName())) +} + var _ cc.LinkableInterface = (*Module)(nil) func (mod *Module) Init() android.Module { @@ -403,6 +451,10 @@ func (mod *Module) Init() android.Module { if mod.compiler != nil { mod.AddProperties(mod.compiler.compilerProps()...) } + if mod.coverage != nil { + mod.AddProperties(mod.coverage.props()...) + } + android.InitAndroidArchModule(mod, mod.hod, mod.multilib) android.InitDefaultableModule(mod) @@ -432,6 +484,7 @@ func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) } func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module { module := newBaseModule(hod, multilib) + module.coverage = &coverage{} return module } @@ -454,6 +507,7 @@ type ModuleContextIntf interface { toolchain() config.Toolchain baseModuleName() string CrateName() string + nativeCoverage() bool } type depsContext struct { @@ -466,6 +520,14 @@ type moduleContext struct { moduleContextImpl } +func (ctx *moduleContextImpl) nativeCoverage() bool { + return ctx.mod.nativeCoverage() +} + +func (mod *Module) nativeCoverage() bool { + return mod.compiler != nil && mod.compiler.nativeCoverage() +} + type moduleContextImpl struct { mod *Module ctx BaseModuleContext @@ -508,9 +570,17 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { if mod.compiler != nil { flags = mod.compiler.compilerFlags(ctx, flags) + } + if mod.coverage != nil { + flags, deps = mod.coverage.flags(ctx, flags, deps) + } + + if mod.compiler != nil { outputFile := mod.compiler.compile(ctx, flags, deps) mod.outputFile = android.OptionalPathForPath(outputFile) - mod.compiler.install(ctx, mod.outputFile.Path()) + if !mod.Properties.PreventInstall { + mod.compiler.install(ctx, mod.outputFile.Path()) + } } } @@ -521,6 +591,10 @@ func (mod *Module) deps(ctx DepsContext) Deps { deps = mod.compiler.compilerDeps(ctx, deps) } + if mod.coverage != nil { + deps = mod.coverage.deps(ctx, deps) + } + deps.Rlibs = android.LastUniqueStrings(deps.Rlibs) deps.Dylibs = android.LastUniqueStrings(deps.Dylibs) deps.ProcMacros = android.LastUniqueStrings(deps.ProcMacros) @@ -553,6 +627,12 @@ var ( testPerSrcDepTag = dependencyTag{name: "rust_unit_tests"} ) +func (mod *Module) begin(ctx BaseModuleContext) { + if mod.coverage != nil { + mod.coverage.begin(ctx) + } +} + func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { var depPaths PathDeps @@ -588,6 +668,7 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { ctx.ModuleErrorf("mod %q not an rlib library", depName) return } + depPaths.coverageFiles = append(depPaths.coverageFiles, rustDep.CoverageFiles()...) directRlibDeps = append(directRlibDeps, rustDep) mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, depName) case procMacroDepTag: @@ -642,6 +723,7 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { depFlag = "-lstatic=" + libName depPaths.linkDirs = append(depPaths.linkDirs, linkPath) depPaths.depFlags = append(depPaths.depFlags, depFlag) + depPaths.coverageFiles = append(depPaths.coverageFiles, ccDep.CoverageFiles()...) directStaticLibDeps = append(directStaticLibDeps, ccDep) mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, depName) case cc.SharedDepTag: @@ -772,6 +854,29 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), procMacroDepTag, deps.ProcMacros...) } +func BeginMutator(ctx android.BottomUpMutatorContext) { + if mod, ok := ctx.Module().(*Module); ok && mod.Enabled() { + mod.beginMutator(ctx) + } +} + +type baseModuleContext struct { + android.BaseModuleContext + moduleContextImpl +} + +func (mod *Module) beginMutator(actx android.BottomUpMutatorContext) { + ctx := &baseModuleContext{ + BaseModuleContext: actx, + moduleContextImpl: moduleContextImpl{ + mod: mod, + }, + } + ctx.ctx = ctx + + mod.begin(ctx) +} + func (mod *Module) Name() string { name := mod.ModuleBase.Name() if p, ok := mod.compiler.(interface { diff --git a/rust/rust_test.go b/rust/rust_test.go index 32eddc161..d658ee201 100644 --- a/rust/rust_test.go +++ b/rust/rust_test.go @@ -21,6 +21,8 @@ import ( "strings" "testing" + "github.com/google/blueprint/proptools" + "android/soong/android" "android/soong/cc" ) @@ -57,6 +59,7 @@ func testConfig(bp string) android.Config { fs := map[string][]byte{ "foo.rs": nil, + "foo.c": nil, "src/bar.rs": nil, "liby.so": nil, "libz.so": nil, @@ -68,6 +71,14 @@ func testConfig(bp string) android.Config { } func testRust(t *testing.T, bp string) *android.TestContext { + return testRustContext(t, bp, false) +} + +func testRustCov(t *testing.T, bp string) *android.TestContext { + return testRustContext(t, bp, true) +} + +func testRustContext(t *testing.T, bp string, coverage bool) *android.TestContext { // TODO (b/140435149) if runtime.GOOS != "linux" { t.Skip("Only the Linux toolchain is supported for Rust") @@ -76,6 +87,11 @@ func testRust(t *testing.T, bp string) *android.TestContext { t.Helper() config := testConfig(bp) + if coverage { + config.TestProductVariables.Native_coverage = proptools.BoolPtr(true) + config.TestProductVariables.CoveragePaths = []string{"*"} + } + t.Helper() ctx := CreateTestContext() ctx.Register(config) diff --git a/rust/test.go b/rust/test.go index 04f844cbc..94568c1cc 100644 --- a/rust/test.go +++ b/rust/test.go @@ -50,6 +50,10 @@ type testDecorator struct { testConfig android.Path } +func (test *testDecorator) nativeCoverage() bool { + return true +} + func NewRustTest(hod android.HostOrDeviceSupported) (*Module, *testDecorator) { module := newModule(hod, android.MultilibFirst) diff --git a/rust/testing.go b/rust/testing.go index aad4ffe35..09008a85f 100644 --- a/rust/testing.go +++ b/rust/testing.go @@ -98,6 +98,7 @@ func CreateTestContext() *android.TestContext { // rust mutators ctx.BottomUp("rust_libraries", LibraryMutator).Parallel() ctx.BottomUp("rust_unit_tests", TestPerSrcMutator).Parallel() + ctx.BottomUp("rust_begin", BeginMutator).Parallel() }) return ctx diff --git a/scripts/build-aml-prebuilts.sh b/scripts/build-aml-prebuilts.sh index eef61498d..c60eaa1f6 100755 --- a/scripts/build-aml-prebuilts.sh +++ b/scripts/build-aml-prebuilts.sh @@ -1,5 +1,15 @@ #!/bin/bash -e +# This is a wrapper around "m" that builds the given modules in multi-arch mode +# for all architectures supported by Mainline modules. The make (kati) stage is +# skipped, so the build targets in the arguments can only be Soong modules or +# intermediate output files - make targets and normal installed paths are not +# supported. +# +# This script is typically used with "sdk" or "module_export" modules, which +# Soong will install in $OUT_DIR/soong/mainline-sdks (cf +# PathForMainlineSdksInstall in android/paths.go). + export OUT_DIR=${OUT_DIR:-out} if [ -e ${OUT_DIR}/soong/.soong.in_make ]; then @@ -8,11 +18,16 @@ if [ -e ${OUT_DIR}/soong/.soong.in_make ]; then # expected to be supplied by the .mk files, and that might cause errors in # "m --skip-make" below. We therefore default to a different out dir # location in that case. - AML_OUT_DIR=out-aml + AML_OUT_DIR=out/aml echo "Avoiding in-make OUT_DIR '${OUT_DIR}' - building in '${AML_OUT_DIR}' instead" OUT_DIR=${AML_OUT_DIR} fi +if [ ! -e "build/envsetup.sh" ]; then + echo "$0 must be run from the top of the tree" + exit 1 +fi + source build/envsetup.sh my_get_build_var() { @@ -22,13 +37,13 @@ my_get_build_var() { OUT_DIR=${OUT_DIR}/get_build_var get_build_var "$@" } -PLATFORM_SDK_VERSION=$(my_get_build_var PLATFORM_SDK_VERSION) -PLATFORM_VERSION=$(my_get_build_var PLATFORM_VERSION) -PLATFORM_VERSION_ALL_CODENAMES=$(my_get_build_var PLATFORM_VERSION_ALL_CODENAMES) +readonly PLATFORM_SDK_VERSION="$(my_get_build_var PLATFORM_SDK_VERSION)" +readonly PLATFORM_VERSION="$(my_get_build_var PLATFORM_VERSION)" +PLATFORM_VERSION_ALL_CODENAMES="$(my_get_build_var PLATFORM_VERSION_ALL_CODENAMES)" # PLATFORM_VERSION_ALL_CODENAMES is a comma separated list like O,P. We need to # turn this into ["O","P"]. -PLATFORM_VERSION_ALL_CODENAMES=${PLATFORM_VERSION_ALL_CODENAMES/,/'","'} +PLATFORM_VERSION_ALL_CODENAMES="${PLATFORM_VERSION_ALL_CODENAMES/,/'","'}" PLATFORM_VERSION_ALL_CODENAMES="[\"${PLATFORM_VERSION_ALL_CODENAMES}\"]" # Logic from build/make/core/goma.mk @@ -46,11 +61,16 @@ else USE_GOMA=false fi -SOONG_OUT=${OUT_DIR}/soong +readonly SOONG_OUT=${OUT_DIR}/soong mkdir -p ${SOONG_OUT} -SOONG_VARS=${SOONG_OUT}/soong.variables +readonly SOONG_VARS=${SOONG_OUT}/soong.variables -# We enable bionic linux builds as ART also needs prebuilts for it. +# Aml_abis: true +# - This flag configures Soong to compile for all architectures required for +# Mainline modules. +# CrossHost: linux_bionic +# CrossHostArch: x86_64 +# - Enable Bionic on host as ART needs prebuilts for it. cat > ${SOONG_VARS}.new << EOF { "Platform_sdk_version": ${PLATFORM_SDK_VERSION}, @@ -79,4 +99,6 @@ fi # We use force building LLVM components flag (even though we actually don't # compile them) because we don't have bionic host prebuilts # for them. -FORCE_BUILD_LLVM_COMPONENTS=true m --skip-make "$@" +export FORCE_BUILD_LLVM_COMPONENTS=true + +m --skip-make "$@" diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh index fc91c3df5..770adc98a 100755 --- a/scripts/build-mainline-modules.sh +++ b/scripts/build-mainline-modules.sh @@ -1,4 +1,4 @@ -#!/bin/bash -ex +#!/bin/bash -e # Non exhaustive list of modules where we want prebuilts. More can be added as # needed. @@ -23,42 +23,45 @@ MODULES_SDK_AND_EXPORTS=( # We want to create apex modules for all supported architectures. PRODUCTS=( - aosp_arm - aosp_arm64 - aosp_x86 - aosp_x86_64 + aosp_arm + aosp_arm64 + aosp_x86 + aosp_x86_64 ) if [ ! -e "build/make/core/Makefile" ]; then - echo "$0 must be run from the top of the tree" - exit 1 + echo "$0 must be run from the top of the tree" + exit 1 fi +echo_and_run() { + echo "$*" + "$@" +} + OUT_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var OUT_DIR) DIST_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var DIST_DIR) for product in "${PRODUCTS[@]}"; do - build/soong/soong_ui.bash --make-mode $@ \ - TARGET_PRODUCT=${product} \ - ${MAINLINE_MODULES[@]} + echo_and_run build/soong/soong_ui.bash --make-mode $@ \ + TARGET_PRODUCT=${product} \ + ${MAINLINE_MODULES[@]} - PRODUCT_OUT=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT=${product} get_build_var PRODUCT_OUT) - TARGET_ARCH=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT=${product} get_build_var TARGET_ARCH) - rm -rf ${DIST_DIR}/${TARGET_ARCH}/ - mkdir -p ${DIST_DIR}/${TARGET_ARCH}/ - for module in "${MAINLINE_MODULES[@]}"; do - cp ${PWD}/${PRODUCT_OUT}/system/apex/${module}.apex ${DIST_DIR}/${TARGET_ARCH}/ - done + PRODUCT_OUT=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT=${product} get_build_var PRODUCT_OUT) + TARGET_ARCH=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT=${product} get_build_var TARGET_ARCH) + rm -rf ${DIST_DIR}/${TARGET_ARCH}/ + mkdir -p ${DIST_DIR}/${TARGET_ARCH}/ + for module in "${MAINLINE_MODULES[@]}"; do + echo_and_run cp ${PWD}/${PRODUCT_OUT}/system/apex/${module}.apex ${DIST_DIR}/${TARGET_ARCH}/ + done done # Create multi-archs SDKs in a different out directory. The multi-arch script -# uses soong directly and therefore needs its own directory that doesn't clash -# with make. -export OUT_DIR=${OUT_DIR}/aml/ -for sdk in "${MODULES_SDK_AND_EXPORTS[@]}"; do - build/soong/scripts/build-aml-prebuilts.sh ${sdk} -done +# uses Soong in --skip-make mode which cannot use the same directory as normal +# mode with make. +export OUT_DIR=${OUT_DIR}/aml +echo_and_run build/soong/scripts/build-aml-prebuilts.sh ${MODULES_SDK_AND_EXPORTS[@]} rm -rf ${DIST_DIR}/mainline-sdks -cp -R ${OUT_DIR}/soong/mainline-sdks ${DIST_DIR} +echo_and_run cp -R ${OUT_DIR}/soong/mainline-sdks ${DIST_DIR} diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go index 733f7ac22..b77447aea 100644 --- a/sdk/cc_sdk_test.go +++ b/sdk/cc_sdk_test.go @@ -1805,3 +1805,86 @@ sdk_snapshot { } `)) } + +func TestDeviceAndHostSnapshotWithStubsLibrary(t *testing.T) { + result := testSdkWithCc(t, ` + sdk { + name: "mysdk", + host_supported: true, + native_shared_libs: ["stubslib"], + } + + cc_library { + name: "internaldep", + host_supported: true, + } + + cc_library { + name: "stubslib", + host_supported: true, + shared_libs: ["internaldep"], + stubs: { + symbol_file: "some/where/stubslib.map.txt", + versions: ["1", "2", "3"], + }, + } + `) + + result.CheckSnapshot("mysdk", "", + checkAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. + +cc_prebuilt_library_shared { + name: "mysdk_stubslib@current", + sdk_member_name: "stubslib", + host_supported: true, + installable: false, + stubs: { + versions: ["3"], + }, + target: { + android_arm64: { + srcs: ["android/arm64/lib/stubslib.so"], + }, + android_arm: { + srcs: ["android/arm/lib/stubslib.so"], + }, + linux_glibc_x86_64: { + srcs: ["linux_glibc/x86_64/lib/stubslib.so"], + }, + linux_glibc_x86: { + srcs: ["linux_glibc/x86/lib/stubslib.so"], + }, + }, +} + +cc_prebuilt_library_shared { + name: "stubslib", + prefer: false, + host_supported: true, + stubs: { + versions: ["3"], + }, + target: { + android_arm64: { + srcs: ["android/arm64/lib/stubslib.so"], + }, + android_arm: { + srcs: ["android/arm/lib/stubslib.so"], + }, + linux_glibc_x86_64: { + srcs: ["linux_glibc/x86_64/lib/stubslib.so"], + }, + linux_glibc_x86: { + srcs: ["linux_glibc/x86/lib/stubslib.so"], + }, + }, +} + +sdk_snapshot { + name: "mysdk@current", + host_supported: true, + native_shared_libs: ["mysdk_stubslib@current"], +} +`)) +} diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index fde92307f..ae1a4923a 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -226,26 +226,38 @@ func TestSDkInstall(t *testing.T) { } type EmbeddedPropertiesStruct struct { - S_Embedded_Common string - S_Embedded_Different string + S_Embedded_Common string `android:"arch_variant"` + S_Embedded_Different string `android:"arch_variant"` } type testPropertiesStruct struct { + name string private string Public_Kept string `sdk:"keep"` S_Common string - S_Different string + S_Different string `android:"arch_variant"` A_Common []string - A_Different []string + A_Different []string `android:"arch_variant"` F_Common *bool - F_Different *bool + F_Different *bool `android:"arch_variant"` EmbeddedPropertiesStruct } +func (p *testPropertiesStruct) optimizableProperties() interface{} { + return p +} + +func (p *testPropertiesStruct) String() string { + return p.name +} + +var _ propertiesContainer = (*testPropertiesStruct)(nil) + func TestCommonValueOptimization(t *testing.T) { - common := &testPropertiesStruct{} - structs := []*testPropertiesStruct{ + common := &testPropertiesStruct{name: "common"} + structs := []propertiesContainer{ &testPropertiesStruct{ + name: "struct-0", private: "common", Public_Kept: "common", S_Common: "common", @@ -260,6 +272,7 @@ func TestCommonValueOptimization(t *testing.T) { }, }, &testPropertiesStruct{ + name: "struct-1", private: "common", Public_Kept: "common", S_Common: "common", @@ -276,11 +289,15 @@ func TestCommonValueOptimization(t *testing.T) { } extractor := newCommonValueExtractor(common) - extractor.extractCommonProperties(common, structs) h := TestHelper{t} - h.AssertDeepEquals("common properties not correct", common, + + err := extractor.extractCommonProperties(common, structs) + h.AssertDeepEquals("unexpected error", nil, err) + + h.AssertDeepEquals("common properties not correct", &testPropertiesStruct{ + name: "common", private: "", Public_Kept: "", S_Common: "common", @@ -293,10 +310,12 @@ func TestCommonValueOptimization(t *testing.T) { S_Embedded_Common: "embedded_common", S_Embedded_Different: "", }, - }) + }, + common) - h.AssertDeepEquals("updated properties[0] not correct", structs[0], + h.AssertDeepEquals("updated properties[0] not correct", &testPropertiesStruct{ + name: "struct-0", private: "common", Public_Kept: "common", S_Common: "", @@ -309,10 +328,12 @@ func TestCommonValueOptimization(t *testing.T) { S_Embedded_Common: "", S_Embedded_Different: "embedded_upper", }, - }) + }, + structs[0]) - h.AssertDeepEquals("updated properties[1] not correct", structs[1], + h.AssertDeepEquals("updated properties[1] not correct", &testPropertiesStruct{ + name: "struct-1", private: "common", Public_Kept: "common", S_Common: "", @@ -325,5 +346,29 @@ func TestCommonValueOptimization(t *testing.T) { S_Embedded_Common: "", S_Embedded_Different: "embedded_lower", }, - }) + }, + structs[1]) +} + +func TestCommonValueOptimization_InvalidArchSpecificVariants(t *testing.T) { + common := &testPropertiesStruct{name: "common"} + structs := []propertiesContainer{ + &testPropertiesStruct{ + name: "struct-0", + S_Common: "should-be-but-is-not-common0", + }, + &testPropertiesStruct{ + name: "struct-1", + S_Common: "should-be-but-is-not-common1", + }, + } + + extractor := newCommonValueExtractor(common) + + h := TestHelper{t} + + err := extractor.extractCommonProperties(common, structs) + h.AssertErrorMessageEquals("unexpected error", `field "S_Common" is not tagged as "arch_variant" but has arch specific properties: + "struct-0" has value "should-be-but-is-not-common0" + "struct-1" has value "should-be-but-is-not-common1"`, err) } diff --git a/sdk/testing.go b/sdk/testing.go index 9e272019e..14a397c68 100644 --- a/sdk/testing.go +++ b/sdk/testing.go @@ -173,6 +173,15 @@ func (h *TestHelper) AssertStringEquals(message string, expected string, actual } } +func (h *TestHelper) AssertErrorMessageEquals(message string, expected string, actual error) { + h.t.Helper() + if actual == nil { + h.t.Errorf("Expected error but was nil") + } else if actual.Error() != expected { + h.t.Errorf("%s: expected %s, actual %s", message, expected, actual.Error()) + } +} + func (h *TestHelper) AssertTrimmedStringEquals(message string, expected string, actual string) { h.t.Helper() h.AssertStringEquals(message, strings.TrimSpace(expected), strings.TrimSpace(actual)) diff --git a/sdk/update.go b/sdk/update.go index e14347f32..d43a42d6e 100644 --- a/sdk/update.go +++ b/sdk/update.go @@ -301,18 +301,18 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) andro addHostDeviceSupportedProperties(s.ModuleBase.DeviceSupported(), s.ModuleBase.HostSupported(), snapshotModule) - var dynamicMemberPropertiesList []interface{} + var dynamicMemberPropertiesContainers []propertiesContainer osTypeToMemberProperties := make(map[android.OsType]*sdk) for _, sdkVariant := range sdkVariants { properties := sdkVariant.dynamicMemberTypeListProperties osTypeToMemberProperties[sdkVariant.Target().Os] = sdkVariant - dynamicMemberPropertiesList = append(dynamicMemberPropertiesList, properties) + dynamicMemberPropertiesContainers = append(dynamicMemberPropertiesContainers, &dynamicMemberPropertiesContainer{sdkVariant, properties}) } // Extract the common lists of members into a separate struct. commonDynamicMemberProperties := s.dynamicSdkMemberTypes.createMemberListProperties() extractor := newCommonValueExtractor(commonDynamicMemberProperties) - extractor.extractCommonProperties(commonDynamicMemberProperties, dynamicMemberPropertiesList) + extractCommonProperties(ctx, extractor, commonDynamicMemberProperties, dynamicMemberPropertiesContainers) // Add properties common to all os types. s.addMemberPropertiesToPropertySet(builder, snapshotModule, commonDynamicMemberProperties) @@ -389,6 +389,13 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) andro return outputZipFile } +func extractCommonProperties(ctx android.ModuleContext, extractor *commonValueExtractor, commonProperties interface{}, inputPropertiesSlice interface{}) { + err := extractor.extractCommonProperties(commonProperties, inputPropertiesSlice) + if err != nil { + ctx.ModuleErrorf("error extracting common properties: %s", err) + } +} + func (s *sdk) addMemberPropertiesToPropertySet(builder *snapshotBuilder, propertySet android.BpPropertySet, dynamicMemberTypeListProperties interface{}) { for _, memberListProperty := range s.memberListProperties() { names := memberListProperty.getter(dynamicMemberTypeListProperties) @@ -814,6 +821,10 @@ type baseInfo struct { Properties android.SdkMemberProperties } +func (b *baseInfo) optimizableProperties() interface{} { + return b.Properties +} + type osTypeSpecificInfo struct { baseInfo @@ -825,6 +836,8 @@ type osTypeSpecificInfo struct { archInfos []*archTypeSpecificInfo } +var _ propertiesContainer = (*osTypeSpecificInfo)(nil) + type variantPropertiesFactoryFunc func() android.SdkMemberProperties // Create a new osTypeSpecificInfo for the specified os type and its properties @@ -882,24 +895,21 @@ func newOsTypeSpecificInfo(ctx android.SdkMemberContext, osType android.OsType, // Optimize the properties by extracting common properties from arch type specific // properties into os type specific properties. -func (osInfo *osTypeSpecificInfo) optimizeProperties(commonValueExtractor *commonValueExtractor) { +func (osInfo *osTypeSpecificInfo) optimizeProperties(ctx *memberContext, commonValueExtractor *commonValueExtractor) { // Nothing to do if there is only a single common architecture. if len(osInfo.archInfos) == 0 { return } multilib := multilibNone - var archPropertiesList []android.SdkMemberProperties for _, archInfo := range osInfo.archInfos { multilib = multilib.addArchType(archInfo.archType) // Optimize the arch properties first. - archInfo.optimizeProperties(commonValueExtractor) - - archPropertiesList = append(archPropertiesList, archInfo.Properties) + archInfo.optimizeProperties(ctx, commonValueExtractor) } - commonValueExtractor.extractCommonProperties(osInfo.Properties, archPropertiesList) + extractCommonProperties(ctx.sdkMemberContext, commonValueExtractor, osInfo.Properties, osInfo.archInfos) // Choose setting for compile_multilib that is appropriate for the arch variants supplied. osInfo.Properties.Base().Compile_multilib = multilib.String() @@ -972,6 +982,17 @@ func (osInfo *osTypeSpecificInfo) addToPropertySet(ctx *memberContext, bpModule } } +func (osInfo *osTypeSpecificInfo) isHostVariant() bool { + osClass := osInfo.osType.Class + return osClass == android.Host || osClass == android.HostCross +} + +var _ isHostVariant = (*osTypeSpecificInfo)(nil) + +func (osInfo *osTypeSpecificInfo) String() string { + return fmt.Sprintf("OsType{%s}", osInfo.osType) +} + type archTypeSpecificInfo struct { baseInfo @@ -980,6 +1001,8 @@ type archTypeSpecificInfo struct { linkInfos []*linkTypeSpecificInfo } +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, variantPropertiesFactory variantPropertiesFactoryFunc, archVariants []android.Module) *archTypeSpecificInfo { @@ -1011,6 +1034,10 @@ func newArchSpecificInfo(ctx android.SdkMemberContext, archType android.ArchType return archInfo } +func (archInfo *archTypeSpecificInfo) optimizableProperties() interface{} { + return archInfo.Properties +} + // Get the link type of the variant // // If the variant is not differentiated by link type then it returns "", @@ -1033,17 +1060,12 @@ func getLinkType(variant android.Module) string { // Optimize the properties by extracting common properties from link type specific // properties into arch type specific properties. -func (archInfo *archTypeSpecificInfo) optimizeProperties(commonValueExtractor *commonValueExtractor) { +func (archInfo *archTypeSpecificInfo) optimizeProperties(ctx *memberContext, commonValueExtractor *commonValueExtractor) { if len(archInfo.linkInfos) == 0 { return } - var propertiesList []android.SdkMemberProperties - for _, linkInfo := range archInfo.linkInfos { - propertiesList = append(propertiesList, linkInfo.Properties) - } - - commonValueExtractor.extractCommonProperties(archInfo.Properties, propertiesList) + extractCommonProperties(ctx.sdkMemberContext, commonValueExtractor, archInfo.Properties, archInfo.linkInfos) } // Add the properties for an arch type to a property set. @@ -1058,12 +1080,18 @@ func (archInfo *archTypeSpecificInfo) addToPropertySet(ctx *memberContext, archP } } +func (archInfo *archTypeSpecificInfo) String() string { + return fmt.Sprintf("ArchType{%s}", archInfo.archType) +} + type linkTypeSpecificInfo struct { baseInfo linkType string } +var _ propertiesContainer = (*linkTypeSpecificInfo)(nil) + // Create a new linkTypeSpecificInfo for the specified link type and its properties // structures populated with information from the variant. func newLinkSpecificInfo(ctx android.SdkMemberContext, linkType string, variantPropertiesFactory variantPropertiesFactoryFunc, linkVariant android.Module) *linkTypeSpecificInfo { @@ -1079,6 +1107,10 @@ func newLinkSpecificInfo(ctx android.SdkMemberContext, linkType string, variantP return linkInfo } +func (l *linkTypeSpecificInfo) String() string { + return fmt.Sprintf("LinkType{%s}", l.linkType) +} + type memberContext struct { sdkMemberContext android.ModuleContext builder *snapshotBuilder @@ -1133,21 +1165,21 @@ func (s *sdk) createMemberSnapshot(ctx *memberContext, member *sdkMember, bpModu // The list of property structures which are os type specific but common across // architectures within that os type. - var osSpecificPropertiesList []android.SdkMemberProperties + var osSpecificPropertiesContainers []*osTypeSpecificInfo for osType, osTypeVariants := range variantsByOsType { osInfo := newOsTypeSpecificInfo(ctx, osType, variantPropertiesFactory, osTypeVariants) osTypeToInfo[osType] = osInfo // Add the os specific properties to a list of os type specific yet architecture // independent properties structs. - osSpecificPropertiesList = append(osSpecificPropertiesList, osInfo.Properties) + osSpecificPropertiesContainers = append(osSpecificPropertiesContainers, osInfo) // Optimize the properties across all the variants for a specific os type. - osInfo.optimizeProperties(commonValueExtractor) + osInfo.optimizeProperties(ctx, commonValueExtractor) } // Extract properties which are common across all architectures and os types. - commonValueExtractor.extractCommonProperties(commonProperties, osSpecificPropertiesList) + extractCommonProperties(ctx.sdkMemberContext, commonValueExtractor, commonProperties, osSpecificPropertiesContainers) // Add the common properties to the module. commonProperties.AddToPropertySet(ctx, bpModule) @@ -1186,15 +1218,49 @@ func (s *sdk) getPossibleOsTypes() []android.OsType { return osTypes } -// Given a struct value, access a field within that struct (or one of its embedded -// structs). +// Given a set of properties (struct value), return the value of the field within that +// struct (or one of its embedded structs). type fieldAccessorFunc func(structValue reflect.Value) reflect.Value +// Checks the metadata to determine whether the property should be ignored for the +// purposes of common value extraction or not. +type extractorMetadataPredicate func(metadata propertiesContainer) bool + +// Indicates whether optimizable properties are provided by a host variant or +// not. +type isHostVariant interface { + isHostVariant() bool +} + +// A property that can be optimized by the commonValueExtractor. +type extractorProperty struct { + // The name of the field for this property. + name string + + // Filter that can use metadata associated with the properties being optimized + // to determine whether the field should be ignored during common value + // optimization. + filter extractorMetadataPredicate + + // Retrieves the value on which common value optimization will be performed. + getter fieldAccessorFunc + + // The empty value for the field. + emptyValue reflect.Value + + // True if the property can support arch variants false otherwise. + archVariant bool +} + +func (p extractorProperty) String() string { + return p.name +} + // Supports extracting common values from a number of instances of a properties // structure into a separate common set of properties. type commonValueExtractor struct { - // The getters for every field from which common values can be extracted. - fieldGetters []fieldAccessorFunc + // The properties that the extractor can optimize. + properties []extractorProperty } // Create a new common value extractor for the structure type for the supplied @@ -1229,8 +1295,25 @@ func (e *commonValueExtractor) gatherFields(structType reflect.Type, containingS continue } + var filter extractorMetadataPredicate + + // Add a filter + if proptools.HasTag(field, "sdk", "ignored-on-host") { + filter = func(metadata propertiesContainer) bool { + if m, ok := metadata.(isHostVariant); ok { + if m.isHostVariant() { + return false + } + } + return true + } + } + // Save a copy of the field index for use in the function. fieldIndex := f + + name := field.Name + fieldGetter := func(value reflect.Value) reflect.Value { if containingStructAccessor != nil { // This is an embedded structure so first access the field for the embedded @@ -1241,6 +1324,12 @@ func (e *commonValueExtractor) gatherFields(structType reflect.Type, containingS // Skip through interface and pointer values to find the structure. value = getStructValue(value) + defer func() { + if r := recover(); r != nil { + panic(fmt.Errorf("%s for fieldIndex %d of field %s of value %#v", r, fieldIndex, name, value.Interface())) + } + }() + // Return the field. return value.Field(fieldIndex) } @@ -1249,7 +1338,14 @@ func (e *commonValueExtractor) gatherFields(structType reflect.Type, containingS // Gather fields from the embedded structure. e.gatherFields(field.Type, fieldGetter) } else { - e.fieldGetters = append(e.fieldGetters, fieldGetter) + property := extractorProperty{ + name, + filter, + fieldGetter, + reflect.Zero(field.Type), + proptools.HasTag(field, "android", "arch_variant"), + } + e.properties = append(e.properties, property) } } } @@ -1270,31 +1366,81 @@ foundStruct: return value } +// A container of properties to be optimized. +// +// Allows additional information to be associated with the properties, e.g. for +// filtering. +type propertiesContainer interface { + fmt.Stringer + + // Get the properties that need optimizing. + optimizableProperties() interface{} +} + +// A wrapper for dynamic member properties to allow them to be optimized. +type dynamicMemberPropertiesContainer struct { + sdkVariant *sdk + dynamicMemberProperties interface{} +} + +func (c dynamicMemberPropertiesContainer) optimizableProperties() interface{} { + return c.dynamicMemberProperties +} + +func (c dynamicMemberPropertiesContainer) String() string { + return c.sdkVariant.String() +} + // Extract common properties from a slice of property structures of the same type. // // All the property structures must be of the same type. // commonProperties - must be a pointer to the structure into which common properties will be added. -// inputPropertiesSlice - must be a slice of input properties structures. +// inputPropertiesSlice - must be a slice of propertiesContainer interfaces. // // Iterates over each exported field (capitalized name) and checks to see whether they // have the same value (using DeepEquals) across all the input properties. If it does not then no // change is made. Otherwise, the common value is stored in the field in the commonProperties // and the field in each of the input properties structure is set to its default value. -func (e *commonValueExtractor) extractCommonProperties(commonProperties interface{}, inputPropertiesSlice interface{}) { +func (e *commonValueExtractor) extractCommonProperties(commonProperties interface{}, inputPropertiesSlice interface{}) error { commonPropertiesValue := reflect.ValueOf(commonProperties) commonStructValue := commonPropertiesValue.Elem() - for _, fieldGetter := range e.fieldGetters { + sliceValue := reflect.ValueOf(inputPropertiesSlice) + + for _, property := range e.properties { + fieldGetter := property.getter + filter := property.filter + if filter == nil { + filter = func(metadata propertiesContainer) bool { + return true + } + } + // Check to see if all the structures have the same value for the field. The commonValue - // is nil on entry to the loop and if it is nil on exit then there is no common value, - // otherwise it points to the common value. + // is nil on entry to the loop and if it is nil on exit then there is no common value or + // all the values have been filtered out, otherwise it points to the common value. var commonValue *reflect.Value - sliceValue := reflect.ValueOf(inputPropertiesSlice) + + // Assume that all the values will be the same. + // + // While similar to this is not quite the same as commonValue == nil. If all the values + // have been filtered out then this will be false but commonValue == nil will be true. + valuesDiffer := false for i := 0; i < sliceValue.Len(); i++ { - itemValue := sliceValue.Index(i) + container := sliceValue.Index(i).Interface().(propertiesContainer) + itemValue := reflect.ValueOf(container.optimizableProperties()) fieldValue := fieldGetter(itemValue) + if !filter(container) { + expectedValue := property.emptyValue.Interface() + actualValue := fieldValue.Interface() + if !reflect.DeepEqual(expectedValue, actualValue) { + return fmt.Errorf("field %q is supposed to be ignored for %q but is set to %#v instead of %#v", property, container, actualValue, expectedValue) + } + continue + } + if commonValue == nil { // Use the first value as the commonProperties value. commonValue = &fieldValue @@ -1303,21 +1449,40 @@ func (e *commonValueExtractor) extractCommonProperties(commonProperties interfac // no value in common so break out. if !reflect.DeepEqual(fieldValue.Interface(), commonValue.Interface()) { commonValue = nil + valuesDiffer = true break } } } - // If the fields all have a common value then store it in the common struct field + // If the fields all have common value then store it in the common struct field // and set the input struct's field to the empty value. if commonValue != nil { - emptyValue := reflect.Zero(commonValue.Type()) + emptyValue := property.emptyValue fieldGetter(commonStructValue).Set(*commonValue) for i := 0; i < sliceValue.Len(); i++ { - itemValue := sliceValue.Index(i) + container := sliceValue.Index(i).Interface().(propertiesContainer) + itemValue := reflect.ValueOf(container.optimizableProperties()) fieldValue := fieldGetter(itemValue) fieldValue.Set(emptyValue) } } + + if valuesDiffer && !property.archVariant { + // The values differ but the property does not support arch variants so it + // is an error. + var details strings.Builder + for i := 0; i < sliceValue.Len(); i++ { + container := sliceValue.Index(i).Interface().(propertiesContainer) + itemValue := reflect.ValueOf(container.optimizableProperties()) + fieldValue := fieldGetter(itemValue) + + _, _ = fmt.Fprintf(&details, "\n %q has value %q", container.String(), fieldValue.Interface()) + } + + return fmt.Errorf("field %q is not tagged as \"arch_variant\" but has arch specific properties:%s", property.String(), details.String()) + } } + + return nil } |