diff options
78 files changed, 2819 insertions, 584 deletions
diff --git a/android/bazel.go b/android/bazel.go index 992d8aa27..8d137621d 100644 --- a/android/bazel.go +++ b/android/bazel.go @@ -180,7 +180,6 @@ var ( // also depends on //system/logging/liblog:liblog (http://b/186822772) "libc_ndk", // http://b/187013218, cc_library_static, depends on //bionic/libm:libm (http://b/183064661) "libc_malloc_hooks", // http://b/187016307, cc_library, ld.lld: error: undefined symbol: __malloc_hook - "libm", // http://b/183064661, cc_library, math.h:25:16: error: unexpected token in argument list // http://b/186823769: Needs C++ STL support, includes from unconverted standard libraries in //external/libcxx // c++_static @@ -189,7 +188,7 @@ var ( "libBionicBenchmarksUtils", // cc_library_static, fatal error: 'map' file not found, from libcxx "fmtlib", // cc_library_static, fatal error: 'cassert' file not found, from libcxx "fmtlib_ndk", // cc_library_static, fatal error: 'cassert' file not found - "libbase", // http://b/186826479, cc_library, fatal error: 'memory' file not found, from libcxx + "libbase", // Requires liblog. http://b/186826479, cc_library, fatal error: 'memory' file not found, from libcxx. // http://b/186024507: Includes errors because of the system_shared_libs default value. // Missing -isystem bionic/libc/include through the libc/libm/libdl diff --git a/android/config.go b/android/config.go index ed90c3181..396b1a66b 100644 --- a/android/config.go +++ b/android/config.go @@ -821,6 +821,12 @@ func (c *config) UnbundledBuildApps() bool { return Bool(c.productVariables.Unbundled_build_apps) } +// Returns true if building image that aren't bundled with the platform. +// UnbundledBuild() is always true when this is true. +func (c *config) UnbundledBuildImage() bool { + return Bool(c.productVariables.Unbundled_build_image) +} + // Returns true if building modules against prebuilt SDKs. func (c *config) AlwaysUsePrebuiltSdks() bool { return Bool(c.productVariables.Always_use_prebuilt_sdks) diff --git a/android/module.go b/android/module.go index 07d82f1a6..126d629ab 100644 --- a/android/module.go +++ b/android/module.go @@ -344,8 +344,18 @@ type ModuleContext interface { // Deprecated: use ModuleContext.Build instead. ModuleBuild(pctx PackageContext, params ModuleBuildParams) + // Returns a list of paths expanded from globs and modules referenced using ":module" syntax. The property must + // be tagged with `android:"path" to support automatic source module dependency resolution. + // + // Deprecated: use PathsForModuleSrc or PathsForModuleSrcExcludes instead. ExpandSources(srcFiles, excludes []string) Paths + + // Returns a single path expanded from globs and modules referenced using ":module" syntax. The property must + // be tagged with `android:"path" to support automatic source module dependency resolution. + // + // Deprecated: use PathForModuleSrc instead. ExpandSource(srcFile, prop string) Path + ExpandOptionalSource(srcFile *string, prop string) OptionalPath // InstallExecutable creates a rule to copy srcPath to name in the installPath directory, @@ -473,6 +483,7 @@ type Module interface { InitRc() Paths VintfFragments() Paths NoticeFiles() Paths + EffectiveLicenseFiles() Paths AddProperties(props ...interface{}) GetProperties() []interface{} @@ -1505,6 +1516,10 @@ func (m *ModuleBase) ExportedToMake() bool { return m.commonProperties.NamespaceExportedToMake } +func (m *ModuleBase) EffectiveLicenseFiles() Paths { + return m.commonProperties.Effective_license_text +} + // computeInstallDeps finds the installed paths of all dependencies that have a dependency // tag that is annotated as needing installation via the IsInstallDepNeeded method. func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]*installPathsDepSet, []*packagingSpecsDepSet) { @@ -2797,30 +2812,57 @@ func (m *moduleContext) blueprintModuleContext() blueprint.ModuleContext { return m.bp } -// SrcIsModule decodes module references in the format ":name" into the module name, or empty string if the input -// was not a module reference. +// SrcIsModule decodes module references in the format ":unqualified-name" or "//namespace:name" +// into the module name, or empty string if the input was not a module reference. func SrcIsModule(s string) (module string) { - if len(s) > 1 && s[0] == ':' { - return s[1:] + if len(s) > 1 { + if s[0] == ':' { + module = s[1:] + if !isUnqualifiedModuleName(module) { + // The module name should be unqualified but is not so do not treat it as a module. + module = "" + } + } else if s[0] == '/' && s[1] == '/' { + module = s + } } - return "" + return module } -// SrcIsModule decodes module references in the format ":name{.tag}" into the module name and tag, ":name" into the -// module name and an empty string for the tag, or empty strings if the input was not a module reference. +// SrcIsModule decodes module references in the format ":unqualified-name{.tag}" or +// "//namespace:name{.tag}" into the module name and an empty string for the tag, or empty strings +// if the input was not a module reference. func SrcIsModuleWithTag(s string) (module, tag string) { - if len(s) > 1 && s[0] == ':' { - module = s[1:] - if tagStart := strings.IndexByte(module, '{'); tagStart > 0 { - if module[len(module)-1] == '}' { - tag = module[tagStart+1 : len(module)-1] - module = module[:tagStart] - return module, tag + if len(s) > 1 { + if s[0] == ':' { + module = s[1:] + } else if s[0] == '/' && s[1] == '/' { + module = s + } + + if module != "" { + if tagStart := strings.IndexByte(module, '{'); tagStart > 0 { + if module[len(module)-1] == '}' { + tag = module[tagStart+1 : len(module)-1] + module = module[:tagStart] + } + } + + if s[0] == ':' && !isUnqualifiedModuleName(module) { + // The module name should be unqualified but is not so do not treat it as a module. + module = "" + tag = "" } } - return module, "" } - return "", "" + + return module, tag +} + +// isUnqualifiedModuleName makes sure that the supplied module is an unqualified module name, i.e. +// does not contain any /. +func isUnqualifiedModuleName(module string) bool { + return strings.IndexByte(module, '/') == -1 } type sourceOrOutputDependencyTag struct { @@ -2832,8 +2874,26 @@ func sourceOrOutputDepTag(tag string) blueprint.DependencyTag { return sourceOrOutputDependencyTag{tag: tag} } +// Deprecated, use IsSourceDepTagWithOutputTag(tag, "") instead. var SourceDepTag = sourceOrOutputDepTag("") +// IsSourceDepTag returns true if the supplied blueprint.DependencyTag is one that was used to add +// dependencies by either ExtractSourceDeps, ExtractSourcesDeps or automatically for properties +// tagged with `android:"path"`. +func IsSourceDepTag(depTag blueprint.DependencyTag) bool { + _, ok := depTag.(sourceOrOutputDependencyTag) + return ok +} + +// IsSourceDepTagWithOutputTag returns true if the supplied blueprint.DependencyTag is one that was +// used to add dependencies by either ExtractSourceDeps, ExtractSourcesDeps or automatically for +// properties tagged with `android:"path"` AND it was added using a module reference of +// :moduleName{outputTag}. +func IsSourceDepTagWithOutputTag(depTag blueprint.DependencyTag, outputTag string) bool { + t, ok := depTag.(sourceOrOutputDependencyTag) + return ok && t.tag == outputTag +} + // Adds necessary dependencies to satisfy filegroup or generated sources modules listed in srcFiles // using ":module" syntax, if any. // diff --git a/android/module_test.go b/android/module_test.go index 9ac929179..9e2b0ca29 100644 --- a/android/module_test.go +++ b/android/module_test.go @@ -55,6 +55,27 @@ func TestSrcIsModule(t *testing.T) { }, wantModule: "foo:bar", }, + { + name: "fully qualified", + args: args{ + s: "//foo:bar", + }, + wantModule: "//foo:bar", + }, + { + name: "fully qualified with tag", + args: args{ + s: "//foo:bar{.tag}", + }, + wantModule: "//foo:bar{.tag}", + }, + { + name: "invalid unqualified name", + args: args{ + s: ":foo/bar", + }, + wantModule: "", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -128,6 +149,35 @@ func TestSrcIsModuleWithTag(t *testing.T) { }, wantModule: "foo.bar}", }, + { + name: "fully qualified", + args: args{ + s: "//foo:bar", + }, + wantModule: "//foo:bar", + }, + { + name: "fully qualified with tag", + args: args{ + s: "//foo:bar{.tag}", + }, + wantModule: "//foo:bar", + wantTag: ".tag", + }, + { + name: "invalid unqualified name", + args: args{ + s: ":foo/bar", + }, + wantModule: "", + }, + { + name: "invalid unqualified name with tag", + args: args{ + s: ":foo/bar{.tag}", + }, + wantModule: "", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/android/mutator.go b/android/mutator.go index 365bf290b..819dd0f2e 100644 --- a/android/mutator.go +++ b/android/mutator.go @@ -19,6 +19,7 @@ import ( "fmt" "reflect" "strings" + "sync" "github.com/google/blueprint" "github.com/google/blueprint/proptools" @@ -229,6 +230,9 @@ var bp2buildPreArchMutators = []RegisterMutatorFunc{} var bp2buildDepsMutators = []RegisterMutatorFunc{} var bp2buildMutators = map[string]RegisterMutatorFunc{} +// See http://b/192523357 +var bp2buildLock sync.Mutex + // RegisterBp2BuildMutator registers specially crafted mutators for // converting Blueprint/Android modules into special modules that can // be code-generated into Bazel BUILD targets. @@ -238,6 +242,9 @@ func RegisterBp2BuildMutator(moduleType string, m func(TopDownMutatorContext)) { f := func(ctx RegisterMutatorsContext) { ctx.TopDown(moduleType, m) } + // Use a lock to avoid a concurrent map write if RegisterBp2BuildMutator is called in parallel + bp2buildLock.Lock() + defer bp2buildLock.Unlock() bp2buildMutators[moduleType] = f } diff --git a/android/namespace.go b/android/namespace.go index d137636e2..4f727e14e 100644 --- a/android/namespace.go +++ b/android/namespace.go @@ -27,7 +27,11 @@ import ( ) func init() { - RegisterModuleType("soong_namespace", NamespaceFactory) + registerNamespaceBuildComponents(InitRegistrationContext) +} + +func registerNamespaceBuildComponents(ctx RegistrationContext) { + ctx.RegisterModuleType("soong_namespace", NamespaceFactory) } // threadsafe sorted list diff --git a/android/namespace_test.go b/android/namespace_test.go index 08e221a65..ea399da06 100644 --- a/android/namespace_test.go +++ b/android/namespace_test.go @@ -636,13 +636,12 @@ func setupTestFromFiles(t *testing.T, bps MockFS) (ctx *TestContext, errs []erro result := GroupFixturePreparers( FixtureModifyContext(func(ctx *TestContext) { ctx.RegisterModuleType("test_module", newTestModule) - ctx.RegisterModuleType("soong_namespace", NamespaceFactory) ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule) - ctx.PreArchMutators(RegisterNamespaceMutator) ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { ctx.BottomUp("rename", renameMutator) }) }), + PrepareForTestWithNamespace, bps.AddToFixture(), ). // Ignore errors for now so tests can check them later. diff --git a/android/path_properties_test.go b/android/path_properties_test.go index 568f8682b..07b48696c 100644 --- a/android/path_properties_test.go +++ b/android/path_properties_test.go @@ -63,7 +63,8 @@ func (p *pathDepsMutatorTestModule) GenerateAndroidBuildActions(ctx ModuleContex if p.props.Foo != "" { // Make sure there is only one dependency on a module listed in a property present in multiple property structs - if ctx.GetDirectDepWithTag(SrcIsModule(p.props.Foo), sourceOrOutputDepTag("")) == nil { + m := SrcIsModule(p.props.Foo) + if GetModuleFromPathDep(ctx, m, "") == nil { ctx.ModuleErrorf("GetDirectDepWithTag failed") } } diff --git a/android/paths.go b/android/paths.go index 128ec12d0..99d5ba7bc 100644 --- a/android/paths.go +++ b/android/paths.go @@ -446,7 +446,7 @@ func (p OutputPaths) Strings() []string { // If the dependency is not found, a missingErrorDependency is returned. // If the module dependency is not a SourceFileProducer or OutputFileProducer, appropriate errors will be returned. func getPathsFromModuleDep(ctx ModuleWithDepsPathContext, path, moduleName, tag string) (Paths, error) { - module := ctx.GetDirectDepWithTag(moduleName, sourceOrOutputDepTag(tag)) + module := GetModuleFromPathDep(ctx, moduleName, tag) if module == nil { return nil, missingDependencyError{[]string{moduleName}} } @@ -474,6 +474,22 @@ func getPathsFromModuleDep(ctx ModuleWithDepsPathContext, path, moduleName, tag } } +// GetModuleFromPathDep will return the module that was added as a dependency automatically for +// properties tagged with `android:"path"` or manually using ExtractSourceDeps or +// ExtractSourcesDeps. +// +// The moduleName and tag supplied to this should be the values returned from SrcIsModuleWithTag. +// Or, if no tag is expected then the moduleName should be the value returned by SrcIsModule and +// the tag must be "". +// +// If tag is "" then the returned module will be the dependency that was added for ":moduleName". +// Otherwise, it is the dependency that was added for ":moduleName{tag}". +// +// TODO(b/193228441) Make this handle fully qualified names, e.g. //namespace:moduleName. +func GetModuleFromPathDep(ctx ModuleWithDepsPathContext, moduleName, tag string) blueprint.Module { + return ctx.GetDirectDepWithTag(moduleName, sourceOrOutputDepTag(tag)) +} + // PathsAndMissingDepsForModuleSrcExcludes returns a Paths{} containing the resolved references in // paths, minus those listed in excludes. Elements of paths and excludes are resolved as: // * filepath, relative to local module directory, resolves as a filepath relative to the local @@ -1252,10 +1268,11 @@ var _ resPathProvider = SourcePath{} // PathForModuleSrc returns a Path representing the paths... under the // module's local source directory. func PathForModuleSrc(ctx ModuleMissingDepsPathContext, pathComponents ...string) Path { - p, err := validatePath(pathComponents...) - if err != nil { - reportPathError(ctx, err) - } + // Just join the components textually just to make sure that it does not corrupt a fully qualified + // module reference, e.g. if the pathComponents is "://other:foo" then using filepath.Join() or + // validatePath() will corrupt it, e.g. replace "//" with "/". If the path is not a module + // reference then it will be validated by expandOneSrcPath anyway when it calls expandOneSrcPath. + p := strings.Join(pathComponents, string(filepath.Separator)) paths, err := expandOneSrcPath(ctx, p, nil) if err != nil { if depErr, ok := err.(missingDependencyError); ok { diff --git a/android/paths_test.go b/android/paths_test.go index 6f5d79e7e..7675905fc 100644 --- a/android/paths_test.go +++ b/android/paths_test.go @@ -1125,6 +1125,12 @@ type pathForModuleSrcTestCase struct { rels []string src string rel string + + // Make test specific preparations to the test fixture. + preparer FixturePreparer + + // A test specific error handler. + errorHandler FixtureErrorHandler } func testPathForModuleSrc(t *testing.T, tests []pathForModuleSrcTestCase) { @@ -1157,14 +1163,23 @@ func testPathForModuleSrc(t *testing.T, tests []pathForModuleSrcTestCase) { "foo/src_special/$": nil, } + errorHandler := test.errorHandler + if errorHandler == nil { + errorHandler = FixtureExpectsNoErrors + } + result := GroupFixturePreparers( FixtureRegisterWithContext(func(ctx RegistrationContext) { ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory) ctx.RegisterModuleType("output_file_provider", pathForModuleSrcOutputFileProviderModuleFactory) - ctx.RegisterModuleType("filegroup", FileGroupFactory) }), + PrepareForTestWithFilegroup, + PrepareForTestWithNamespace, mockFS.AddToFixture(), - ).RunTest(t) + OptionalFixturePreparer(test.preparer), + ). + ExtendWithErrorHandler(errorHandler). + RunTest(t) m := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule) @@ -1333,6 +1348,74 @@ func TestPathForModuleSrc(t *testing.T) { src: "foo/src_special/$", rel: "src_special/$", }, + { + // This test makes sure that an unqualified module name cannot contain characters that make + // it appear as a qualified module name. + name: "output file provider, invalid fully qualified name", + bp: ` + test { + name: "foo", + src: "://other:b", + srcs: ["://other:c"], + }`, + preparer: FixtureAddTextFile("other/Android.bp", ` + soong_namespace {} + + output_file_provider { + name: "b", + outs: ["gen/b"], + } + + output_file_provider { + name: "c", + outs: ["gen/c"], + } + `), + src: "foo/:/other:b", + rel: ":/other:b", + srcs: []string{"foo/:/other:c"}, + rels: []string{":/other:c"}, + }, + { + name: "output file provider, missing fully qualified name", + bp: ` + test { + name: "foo", + src: "//other:b", + srcs: ["//other:c"], + }`, + errorHandler: FixtureExpectsAllErrorsToMatchAPattern([]string{ + `"foo" depends on undefined module "//other:b"`, + `"foo" depends on undefined module "//other:c"`, + }), + }, + { + // TODO(b/193228441): Fix broken test. + name: "output file provider, fully qualified name", + bp: ` + test { + name: "foo", + src: "//other:b", + srcs: ["//other:c"], + }`, + preparer: FixtureAddTextFile("other/Android.bp", ` + soong_namespace {} + + output_file_provider { + name: "b", + outs: ["gen/b"], + } + + output_file_provider { + name: "c", + outs: ["gen/c"], + } + `), + errorHandler: FixtureExpectsAllErrorsToMatchAPattern([]string{ + `"foo": missing dependencies: //other:b, is the property annotated with android:"path"`, + `"foo": missing dependency on "//other:c", is the property annotated with android:"path"`, + }), + }, } testPathForModuleSrc(t, tests) diff --git a/android/prebuilt.go b/android/prebuilt.go index f3493bd5a..e61150243 100644 --- a/android/prebuilt.go +++ b/android/prebuilt.go @@ -61,6 +61,16 @@ type PrebuiltProperties struct { // a matching name. Prefer *bool `android:"arch_variant"` + // When specified this names a Soong config variable that controls the prefer property. + // + // If the value of the named Soong config variable is true then prefer is set to false and vice + // versa. If the Soong config variable is not set then it defaults to false, so prefer defaults + // to true. + // + // If specified then the prefer property is ignored in favor of the value of the Soong config + // variable. + Use_source_config_var *ConfigVarProperties + SourceExists bool `blueprint:"mutated"` UsePrebuilt bool `blueprint:"mutated"` @@ -68,6 +78,22 @@ type PrebuiltProperties struct { PrebuiltRenamedToSource bool `blueprint:"mutated"` } +// Properties that can be used to select a Soong config variable. +type ConfigVarProperties struct { + // Allow instances of this struct to be used as a property value in a BpPropertySet. + BpPrintableBase + + // The name of the configuration namespace. + // + // As passed to add_soong_config_namespace in Make. + Config_namespace *string + + // The name of the configuration variable. + // + // As passed to add_soong_config_var_value in Make. + Var_name *string +} + type Prebuilt struct { properties PrebuiltProperties @@ -364,12 +390,18 @@ func (p *Prebuilt) usePrebuilt(ctx TopDownMutatorContext, source Module, prebuil return false } - // TODO: use p.Properties.Name and ctx.ModuleDir to override preference - if Bool(p.properties.Prefer) { + // If source is not available or is disabled then always use the prebuilt. + if source == nil || !source.Enabled() { return true } - return source == nil || !source.Enabled() + // If the use_source_config_var property is set then it overrides the prefer property setting. + if configVar := p.properties.Use_source_config_var; configVar != nil { + return !ctx.Config().VendorConfig(proptools.String(configVar.Config_namespace)).Bool(proptools.String(configVar.Var_name)) + } + + // TODO: use p.Properties.Name and ctx.ModuleDir to override preference + return Bool(p.properties.Prefer) } func (p *Prebuilt) SourceExists() bool { diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go index 23524a533..dcd77ea46 100644 --- a/android/prebuilt_test.go +++ b/android/prebuilt_test.go @@ -26,6 +26,7 @@ var prebuiltsTests = []struct { replaceBp bool // modules is added to default bp boilerplate if false. modules string prebuilt []OsType + preparer FixturePreparer }{ { name: "no prebuilt", @@ -291,6 +292,86 @@ var prebuiltsTests = []struct { }`, prebuilt: []OsType{Android, BuildOs}, }, + { + name: "prebuilt use_source_config_var={acme, use_source} - no var specified", + modules: ` + source { + name: "bar", + } + + prebuilt { + name: "bar", + use_source_config_var: {config_namespace: "acme", var_name: "use_source"}, + srcs: ["prebuilt_file"], + }`, + // When use_source_env is specified then it will use the prebuilt by default if the environment + // variable is not set. + prebuilt: []OsType{Android, BuildOs}, + }, + { + name: "prebuilt use_source_config_var={acme, use_source} - acme_use_source=false", + modules: ` + source { + name: "bar", + } + + prebuilt { + name: "bar", + use_source_config_var: {config_namespace: "acme", var_name: "use_source"}, + srcs: ["prebuilt_file"], + }`, + preparer: FixtureModifyProductVariables(func(variables FixtureProductVariables) { + variables.VendorVars = map[string]map[string]string{ + "acme": { + "use_source": "false", + }, + } + }), + // Setting the environment variable named in use_source_env to false will cause the prebuilt to + // be used. + prebuilt: []OsType{Android, BuildOs}, + }, + { + name: "prebuilt use_source_config_var={acme, use_source} - acme_use_source=true", + modules: ` + source { + name: "bar", + } + + prebuilt { + name: "bar", + use_source_config_var: {config_namespace: "acme", var_name: "use_source"}, + srcs: ["prebuilt_file"], + }`, + preparer: FixtureModifyProductVariables(func(variables FixtureProductVariables) { + variables.VendorVars = map[string]map[string]string{ + "acme": { + "use_source": "true", + }, + } + }), + // Setting the environment variable named in use_source_env to true will cause the source to be + // used. + prebuilt: nil, + }, + { + name: "prebuilt use_source_config_var={acme, use_source} - acme_use_source=true, no source", + modules: ` + prebuilt { + name: "bar", + use_source_config_var: {config_namespace: "acme", var_name: "use_source"}, + srcs: ["prebuilt_file"], + }`, + preparer: FixtureModifyProductVariables(func(variables FixtureProductVariables) { + variables.VendorVars = map[string]map[string]string{ + "acme": { + "use_source": "true", + }, + } + }), + // Although the environment variable says to use source there is no source available. + prebuilt: []OsType{Android, BuildOs}, + }, } func TestPrebuilts(t *testing.T) { @@ -329,6 +410,7 @@ func TestPrebuilts(t *testing.T) { }), fs.AddToFixture(), FixtureRegisterWithContext(registerTestPrebuiltModules), + OptionalFixturePreparer(test.preparer), ).RunTestWithBp(t, bp) for _, variant := range result.ModuleVariantsForTests("foo") { diff --git a/android/testing.go b/android/testing.go index b36f62cce..17a812ea1 100644 --- a/android/testing.go +++ b/android/testing.go @@ -110,6 +110,11 @@ var PrepareForTestWithLicenseDefaultModules = GroupFixturePreparers( FixtureAddFile("build/soong/licenses/LICENSE", nil), ) +var PrepareForTestWithNamespace = FixtureRegisterWithContext(func(ctx RegistrationContext) { + registerNamespaceBuildComponents(ctx) + ctx.PreArchMutators(RegisterNamespaceMutator) +}) + // Test fixture preparer that will register most java build components. // // Singletons and mutators should only be added here if they are needed for a majority of java diff --git a/android/variable.go b/android/variable.go index b6e168cbc..bbb98688f 100644 --- a/android/variable.go +++ b/android/variable.go @@ -225,6 +225,7 @@ type productVariables struct { Allow_missing_dependencies *bool `json:",omitempty"` Unbundled_build *bool `json:",omitempty"` Unbundled_build_apps *bool `json:",omitempty"` + Unbundled_build_image *bool `json:",omitempty"` Always_use_prebuilt_sdks *bool `json:",omitempty"` Skip_boot_jars_check *bool `json:",omitempty"` Malloc_not_svelte *bool `json:",omitempty"` diff --git a/apex/Android.bp b/apex/Android.bp index 6269757db..b9b54286b 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -9,6 +9,7 @@ bootstrap_go_package { "blueprint", "soong", "soong-android", + "soong-bazel", "soong-bpf", "soong-cc", "soong-filesystem", diff --git a/apex/apex.go b/apex/apex.go index baaf87475..11df288a3 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -27,6 +27,7 @@ import ( "github.com/google/blueprint/proptools" "android/soong/android" + "android/soong/bazel" "android/soong/bpf" "android/soong/cc" prebuilt_etc "android/soong/etc" @@ -53,6 +54,8 @@ func registerApexBuildComponents(ctx android.RegistrationContext) { ctx.PreArchMutators(registerPreArchMutators) ctx.PreDepsMutators(RegisterPreDepsMutators) ctx.PostDepsMutators(RegisterPostDepsMutators) + + android.RegisterBp2BuildMutator("apex", ApexBundleBp2Build) } func registerPreArchMutators(ctx android.RegisterMutatorsContext) { @@ -327,6 +330,7 @@ type apexBundle struct { android.DefaultableModuleBase android.OverridableModuleBase android.SdkBase + android.BazelModuleBase // Properties properties apexBundleProperties @@ -3178,3 +3182,63 @@ func rModulesPackages() map[string][]string { }, } } + +// For Bazel / bp2build + +type bazelApexBundleAttributes struct { + Manifest bazel.LabelAttribute +} + +type bazelApexBundle struct { + android.BazelTargetModuleBase + bazelApexBundleAttributes +} + +func BazelApexBundleFactory() android.Module { + module := &bazelApexBundle{} + module.AddProperties(&module.bazelApexBundleAttributes) + android.InitBazelTargetModule(module) + return module +} + +func ApexBundleBp2Build(ctx android.TopDownMutatorContext) { + module, ok := ctx.Module().(*apexBundle) + if !ok { + // Not an APEX bundle + return + } + if !module.ConvertWithBp2build(ctx) { + return + } + if ctx.ModuleType() != "apex" { + return + } + + apexBundleBp2BuildInternal(ctx, module) +} + +func apexBundleBp2BuildInternal(ctx android.TopDownMutatorContext, module *apexBundle) { + var manifestLabelAttribute bazel.LabelAttribute + + manifestStringPtr := module.properties.Manifest + if module.properties.Manifest != nil { + manifestLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *manifestStringPtr)) + } + + attrs := &bazelApexBundleAttributes{ + Manifest: manifestLabelAttribute, + } + + props := bazel.BazelTargetModuleProperties{ + Rule_class: "apex", + Bzl_load_location: "//build/bazel/rules:apex.bzl", + } + + ctx.CreateBazelTargetModule(BazelApexBundleFactory, module.Name(), props, attrs) +} + +func (m *bazelApexBundle) Name() string { + return m.BaseModuleName() +} + +func (m *bazelApexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {} diff --git a/apex/apex_test.go b/apex/apex_test.go index b5b1d4401..422e46c4d 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -4687,16 +4687,26 @@ func TestPrebuiltExportDexImplementationJars(t *testing.T) { p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency) dexJarBuildPath := p.DexJarBuildPath() stem := android.RemoveOptionalPrebuiltPrefix(name) - if expected, actual := ".intermediates/myapex.deapexer/android_common/deapexer/javalib/"+stem+".jar", android.NormalizePathForTesting(dexJarBuildPath); actual != expected { - t.Errorf("Incorrect DexJarBuildPath value '%s', expected '%s'", actual, expected) - } + android.AssertStringEquals(t, "DexJarBuildPath should be apex-related path.", + ".intermediates/myapex.deapexer/android_common/deapexer/javalib/"+stem+".jar", + android.NormalizePathForTesting(dexJarBuildPath)) + } + + checkDexJarInstallPath := func(t *testing.T, ctx *android.TestContext, name string) { + // Make sure the import has been given the correct path to the dex jar. + p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency) + dexJarBuildPath := p.DexJarInstallPath() + stem := android.RemoveOptionalPrebuiltPrefix(name) + android.AssertStringEquals(t, "DexJarInstallPath should be apex-related path.", + "target/product/test_device/apex/myapex/javalib/"+stem+".jar", + android.NormalizePathForTesting(dexJarBuildPath)) } ensureNoSourceVariant := func(t *testing.T, ctx *android.TestContext, name string) { // Make sure that an apex variant is not created for the source module. - if expected, actual := []string{"android_common"}, ctx.ModuleVariantsForTests(name); !reflect.DeepEqual(expected, actual) { - t.Errorf("invalid set of variants for %q: expected %q, found %q", "libfoo", expected, actual) - } + android.AssertArrayString(t, "Check if there is no source variant", + []string{"android_common"}, + ctx.ModuleVariantsForTests(name)) } t.Run("prebuilt only", func(t *testing.T) { @@ -4745,8 +4755,10 @@ func TestPrebuiltExportDexImplementationJars(t *testing.T) { } checkDexJarBuildPath(t, ctx, "libfoo") + checkDexJarInstallPath(t, ctx, "libfoo") checkDexJarBuildPath(t, ctx, "libbar") + checkDexJarInstallPath(t, ctx, "libbar") }) t.Run("prebuilt with source preferred", func(t *testing.T) { @@ -4792,9 +4804,11 @@ func TestPrebuiltExportDexImplementationJars(t *testing.T) { ctx := testDexpreoptWithApexes(t, bp, "", transform) checkDexJarBuildPath(t, ctx, "prebuilt_libfoo") + checkDexJarInstallPath(t, ctx, "prebuilt_libfoo") ensureNoSourceVariant(t, ctx, "libfoo") checkDexJarBuildPath(t, ctx, "prebuilt_libbar") + checkDexJarInstallPath(t, ctx, "prebuilt_libbar") ensureNoSourceVariant(t, ctx, "libbar") }) @@ -4842,9 +4856,11 @@ func TestPrebuiltExportDexImplementationJars(t *testing.T) { ctx := testDexpreoptWithApexes(t, bp, "", transform) checkDexJarBuildPath(t, ctx, "prebuilt_libfoo") + checkDexJarInstallPath(t, ctx, "prebuilt_libfoo") ensureNoSourceVariant(t, ctx, "libfoo") checkDexJarBuildPath(t, ctx, "prebuilt_libbar") + checkDexJarInstallPath(t, ctx, "prebuilt_libbar") ensureNoSourceVariant(t, ctx, "libbar") }) } diff --git a/bazel/properties.go b/bazel/properties.go index 0dd47da73..7ecc92bdb 100644 --- a/bazel/properties.go +++ b/bazel/properties.go @@ -558,6 +558,19 @@ func (lla LabelListAttribute) HasConfigurableValues() bool { return len(lla.ConfigurableValues) > 0 } +// IsEmpty returns true if the attribute has no values under any configuration. +func (lla LabelListAttribute) IsEmpty() bool { + if len(lla.Value.Includes) > 0 { + return false + } + for axis, _ := range lla.ConfigurableValues { + if lla.ConfigurableValues[axis].HasConfigurableValues() { + return false + } + } + return true +} + // ResolveExcludes handles excludes across the various axes, ensuring that items are removed from // the base value and included in default values as appropriate. func (lla *LabelListAttribute) ResolveExcludes() { diff --git a/bp2build/Android.bp b/bp2build/Android.bp index 0e6030e5b..dded14ba1 100644 --- a/bp2build/Android.bp +++ b/bp2build/Android.bp @@ -19,6 +19,7 @@ bootstrap_go_package { ], deps: [ "soong-android", + "soong-apex", "soong-bazel", "soong-cc", "soong-cc-config", @@ -27,6 +28,7 @@ bootstrap_go_package { "soong-sh", ], testSrcs: [ + "apex_conversion_test.go", "build_conversion_test.go", "bzl_conversion_test.go", "cc_library_conversion_test.go", diff --git a/bp2build/apex_conversion_test.go b/bp2build/apex_conversion_test.go new file mode 100644 index 000000000..fbf6fa289 --- /dev/null +++ b/bp2build/apex_conversion_test.go @@ -0,0 +1,48 @@ +// Copyright 2021 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bp2build + +import ( + "android/soong/android" + "android/soong/apex" + "testing" +) + +func runApexTestCase(t *testing.T, tc bp2buildTestCase) { + t.Helper() + runBp2BuildTestCase(t, registerApexModuleTypes, tc) +} + +func registerApexModuleTypes(ctx android.RegistrationContext) { +} + +func TestApexBundleSimple(t *testing.T) { + runApexTestCase(t, bp2buildTestCase{ + description: "apex - simple example", + moduleTypeUnderTest: "apex", + moduleTypeUnderTestFactory: apex.BundleFactory, + moduleTypeUnderTestBp2BuildMutator: apex.ApexBundleBp2Build, + filesystem: map[string]string{}, + blueprint: ` +apex { + name: "apogee", + manifest: "manifest.json", +} +`, + expectedBazelTargets: []string{`apex( + name = "apogee", + manifest = "manifest.json", +)`}}) +} diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go index 285677a1c..4f720f56c 100644 --- a/bp2build/cc_library_conversion_test.go +++ b/bp2build/cc_library_conversion_test.go @@ -674,6 +674,10 @@ filegroup { blueprint: soongCcLibraryPreamble, expectedBazelTargets: []string{`cc_library( name = "a", + asflags = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], copts = [ "-Ifoo/bar", "-I$(BINDIR)/foo/bar", diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go index 40edec80b..c33889fc8 100644 --- a/bp2build/cc_library_static_conversion_test.go +++ b/bp2build/cc_library_static_conversion_test.go @@ -1413,7 +1413,7 @@ cc_library_static { func TestCcLibraryStaticProductVariableStringReplacement(t *testing.T) { runCcLibraryStaticTestCase(t, bp2buildTestCase{ - description: "cc_library_static product variable selects", + description: "cc_library_static product variable string replacement", moduleTypeUnderTest: "cc_library_static", moduleTypeUnderTestFactory: cc.LibraryStaticFactory, moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, @@ -1422,7 +1422,7 @@ func TestCcLibraryStaticProductVariableStringReplacement(t *testing.T) { blueprint: soongCcLibraryStaticPreamble + ` cc_library_static { name: "foo_static", - srcs: ["common.c"], + srcs: ["common.S"], product_variables: { platform_sdk_version: { asflags: ["-DPLATFORM_SDK_VERSION=%d"], @@ -1431,7 +1431,10 @@ cc_library_static { } `, expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - asflags = select({ + asflags = [ + "-I.", + "-I$(BINDIR)/.", + ] + select({ "//build/bazel/product_variables:platform_sdk_version": ["-DPLATFORM_SDK_VERSION=$(Platform_sdk_version)"], "//conditions:default": [], }), @@ -1440,7 +1443,7 @@ cc_library_static { "-I$(BINDIR)/.", ], linkstatic = True, - srcs_c = ["common.c"], + srcs_as = ["common.S"], )`}, }) } diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go index 57f75eabb..df4924bfe 100644 --- a/bp2build/cc_object_conversion_test.go +++ b/bp2build/cc_object_conversion_test.go @@ -46,6 +46,7 @@ func TestCcObjectSimple(t *testing.T) { blueprint: `cc_object { name: "foo", local_include_dirs: ["include"], + default_shared_libs: [], cflags: [ "-Wno-gcc-compat", "-Wall", @@ -83,6 +84,7 @@ func TestCcObjectDefaults(t *testing.T) { moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, blueprint: `cc_object { name: "foo", + default_shared_libs: [], local_include_dirs: ["include"], srcs: [ "a/b/*.h", @@ -135,12 +137,14 @@ func TestCcObjectCcObjetDepsInObjs(t *testing.T) { }, blueprint: `cc_object { name: "foo", + default_shared_libs: [], srcs: ["a/b/c.c"], objs: ["bar"], } cc_object { name: "bar", + default_shared_libs: [], srcs: ["x/y/z.c"], } `, @@ -178,6 +182,7 @@ func TestCcObjectIncludeBuildDirFalse(t *testing.T) { }, blueprint: `cc_object { name: "foo", + default_shared_libs: [], srcs: ["a/b/c.c"], include_build_directory: false, } @@ -199,12 +204,14 @@ func TestCcObjectProductVariable(t *testing.T) { moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, blueprint: `cc_object { name: "foo", + default_shared_libs: [], include_build_directory: false, product_variables: { platform_sdk_version: { asflags: ["-DPLATFORM_SDK_VERSION=%d"], }, }, + srcs: ["src.S"], } `, expectedBazelTargets: []string{`cc_object( @@ -214,6 +221,7 @@ func TestCcObjectProductVariable(t *testing.T) { "//conditions:default": [], }), copts = ["-fno-addrsig"], + srcs_as = ["src.S"], )`, }, }) @@ -227,13 +235,14 @@ func TestCcObjectCflagsOneArch(t *testing.T) { moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, blueprint: `cc_object { name: "foo", + default_shared_libs: [], srcs: ["a.cpp"], arch: { x86: { cflags: ["-fPIC"], // string list }, arm: { - srcs: ["arch/arm/file.S"], // label list + srcs: ["arch/arm/file.cpp"], // label list }, }, } @@ -250,7 +259,7 @@ func TestCcObjectCflagsOneArch(t *testing.T) { "//conditions:default": [], }), srcs = ["a.cpp"] + select({ - "//build/bazel/platforms/arch:arm": ["arch/arm/file.S"], + "//build/bazel/platforms/arch:arm": ["arch/arm/file.cpp"], "//conditions:default": [], }), )`, @@ -266,6 +275,7 @@ func TestCcObjectCflagsFourArch(t *testing.T) { moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, blueprint: `cc_object { name: "foo", + default_shared_libs: [], srcs: ["base.cpp"], arch: { x86: { @@ -321,6 +331,7 @@ func TestCcObjectCflagsMultiOs(t *testing.T) { moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, blueprint: `cc_object { name: "foo", + default_shared_libs: [], srcs: ["base.cpp"], target: { android: { diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go index 96400241e..a60863036 100644 --- a/bpfix/bpfix/bpfix.go +++ b/bpfix/bpfix/bpfix.go @@ -130,7 +130,15 @@ var fixSteps = []FixStep{ }, { Name: "removePdkProperty", - Fix: runPatchListMod(removePdkProperty), + Fix: runPatchListMod(removeObsoleteProperty("product_variables.pdk")), + }, + { + Name: "removeScudoProperty", + Fix: runPatchListMod(removeObsoleteProperty("sanitize.scudo")), + }, + { + Name: "formatFlagProperties", + Fix: runPatchListMod(formatFlagProperties), }, } @@ -392,7 +400,7 @@ func rewriteTestModuleTypes(f *Fixer) error { continue } - if !strings.HasPrefix(mod.Type, "java_") && !strings.HasPrefix(mod.Type, "android_") { + if !strings.HasPrefix(mod.Type, "java_") && !strings.HasPrefix(mod.Type, "android_") && mod.Type != "cc_binary" { continue } @@ -420,6 +428,14 @@ func rewriteTestModuleTypes(f *Fixer) error { mod.Type = "java_test_host" } } + + // when a cc_binary module has a nonempty test_suites field, modify the type to cc_test + if mod.Type == "cc_binary" { + hasTestSuites := hasNonEmptyLiteralListProperty(mod, "test_suites") + if hasTestSuites { + mod.Type = "cc_test" + } + } } return nil @@ -855,7 +871,9 @@ func convertToSingleSource(mod *parser.Module, srcPropertyName string) { } } -func runPatchListMod(modFunc func(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error) func(*Fixer) error { +type patchListModFunction func(*parser.Module, []byte, *parser.PatchList) error + +func runPatchListMod(modFunc patchListModFunction) func(*Fixer) error { return func(f *Fixer) error { // Make sure all the offsets are accurate buf, err := f.reparse() @@ -1025,23 +1043,63 @@ func removeTags(mod *parser.Module, buf []byte, patchlist *parser.PatchList) err return patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, replaceStr) } -func removePdkProperty(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error { - prop, ok := mod.GetProperty("product_variables") - if !ok { - return nil - } - propMap, ok := prop.Value.(*parser.Map) - if !ok { - return nil - } - pdkProp, ok := propMap.GetProperty("pdk") - if !ok { - return nil +type propertyProvider interface { + GetProperty(string) (*parser.Property, bool) + RemoveProperty(string) bool +} + +func removeNestedProperty(mod *parser.Module, patchList *parser.PatchList, propName string) error { + propNames := strings.Split(propName, ".") + + var propProvider, toRemoveFrom propertyProvider + propProvider = mod + + var propToRemove *parser.Property + for i, name := range propNames { + p, ok := propProvider.GetProperty(name) + if !ok { + return nil + } + // if this is the inner most element, it's time to delete + if i == len(propNames)-1 { + if propToRemove == nil { + // if we cannot remove the properties that the current property is nested in, + // remove only the current property + propToRemove = p + toRemoveFrom = propProvider + } + + // remove the property from the list, in case we remove other properties in this list + toRemoveFrom.RemoveProperty(propToRemove.Name) + // only removing the property would leave blank line(s), remove with a patch + if err := patchList.Add(propToRemove.Pos().Offset, propToRemove.End().Offset+2, ""); err != nil { + return err + } + } else { + propMap, ok := p.Value.(*parser.Map) + if !ok { + return nil + } + if len(propMap.Properties) > 1 { + // if there are other properties in this struct, we need to keep this struct + toRemoveFrom = nil + propToRemove = nil + } else if propToRemove == nil { + // otherwise, we can remove the empty struct entirely + toRemoveFrom = propProvider + propToRemove = p + } + propProvider = propMap + } } - if len(propMap.Properties) > 1 { - return patchlist.Add(pdkProp.Pos().Offset, pdkProp.End().Offset+2, "") + + return nil +} + +func removeObsoleteProperty(propName string) patchListModFunction { + return func(mod *parser.Module, buf []byte, patchList *parser.PatchList) error { + return removeNestedProperty(mod, patchList, propName) } - return patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, "") } func mergeMatchingModuleProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error { @@ -1289,3 +1347,69 @@ func inList(s string, list []string) bool { } return false } + +func formatFlagProperty(mod *parser.Module, field string, buf []byte, patchlist *parser.PatchList) error { + // the comment or empty lines in the value of the field are skipped + listValue, ok := getLiteralListProperty(mod, field) + if !ok { + // if do not find + return nil + } + for i := 0; i < len(listValue.Values); i++ { + curValue, ok := listValue.Values[i].(*parser.String) + if !ok { + return fmt.Errorf("Expecting string for %s.%s fields", mod.Type, field) + } + if !strings.HasPrefix(curValue.Value, "-") { + return fmt.Errorf("Expecting the string `%s` starting with '-'", curValue.Value) + } + if i+1 < len(listValue.Values) { + nextValue, ok := listValue.Values[i+1].(*parser.String) + if !ok { + return fmt.Errorf("Expecting string for %s.%s fields", mod.Type, field) + } + if !strings.HasPrefix(nextValue.Value, "-") { + // delete the line + err := patchlist.Add(curValue.Pos().Offset, curValue.End().Offset+2, "") + if err != nil { + return err + } + // replace the line + value := "\"" + curValue.Value + " " + nextValue.Value + "\"," + err = patchlist.Add(nextValue.Pos().Offset, nextValue.End().Offset+1, value) + if err != nil { + return err + } + // combined two lines to one + i++ + } + } + } + return nil +} + +func formatFlagProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error { + relevantFields := []string{ + // cc flags + "asflags", + "cflags", + "clang_asflags", + "clang_cflags", + "conlyflags", + "cppflags", + "ldflags", + "tidy_flags", + // java flags + "aaptflags", + "dxflags", + "javacflags", + "kotlincflags", + } + for _, field := range relevantFields { + err := formatFlagProperty(mod, field, buf, patchlist) + if err != nil { + return err + } + } + return nil +} diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go index ebfeb22c7..d8772c10d 100644 --- a/bpfix/bpfix/bpfix_test.go +++ b/bpfix/bpfix/bpfix_test.go @@ -999,7 +999,171 @@ func TestRemoveSoongConfigBoolVariable(t *testing.T) { } } -func TestRemovePdkProperty(t *testing.T) { +func TestRemoveNestedProperty(t *testing.T) { + tests := []struct { + name string + in string + out string + propertyName string + }{ + { + name: "remove no nesting", + in: ` +cc_library { + name: "foo", + foo: true, +}`, + out: ` +cc_library { + name: "foo", +} +`, + propertyName: "foo", + }, + { + name: "remove one nest", + in: ` +cc_library { + name: "foo", + foo: { + bar: true, + }, +}`, + out: ` +cc_library { + name: "foo", +} +`, + propertyName: "foo.bar", + }, + { + name: "remove one nest, multiple props", + in: ` +cc_library { + name: "foo", + foo: { + bar: true, + baz: false, + }, +}`, + out: ` +cc_library { + name: "foo", + foo: { + baz: false, + }, +} +`, + propertyName: "foo.bar", + }, + { + name: "remove multiple nest", + in: ` +cc_library { + name: "foo", + foo: { + bar: { + baz: { + a: true, + } + }, + }, +}`, + out: ` +cc_library { + name: "foo", +} +`, + propertyName: "foo.bar.baz.a", + }, + { + name: "remove multiple nest, outer non-empty", + in: ` +cc_library { + name: "foo", + foo: { + bar: { + baz: { + a: true, + } + }, + other: true, + }, +}`, + out: ` +cc_library { + name: "foo", + foo: { + other: true, + }, +} +`, + propertyName: "foo.bar.baz.a", + }, + { + name: "remove multiple nest, inner non-empty", + in: ` +cc_library { + name: "foo", + foo: { + bar: { + baz: { + a: true, + }, + other: true, + }, + }, +}`, + out: ` +cc_library { + name: "foo", + foo: { + bar: { + other: true, + }, + }, +} +`, + propertyName: "foo.bar.baz.a", + }, + { + name: "remove multiple nest, inner-most non-empty", + in: ` +cc_library { + name: "foo", + foo: { + bar: { + baz: { + a: true, + other: true, + }, + }, + }, +}`, + out: ` +cc_library { + name: "foo", + foo: { + bar: { + baz: { + other: true, + }, + }, + }, +} +`, + propertyName: "foo.bar.baz.a", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + runPass(t, test.in, test.out, runPatchListMod(removeObsoleteProperty(test.propertyName))) + }) + } +} + +func TestRemoveObsoleteProperties(t *testing.T) { tests := []struct { name string in string @@ -1052,7 +1216,7 @@ func TestRemovePdkProperty(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - runPass(t, test.in, test.out, runPatchListMod(removePdkProperty)) + runPass(t, test.in, test.out, runPatchListMod(removeObsoleteProperty("product_variables.pdk"))) }) } } @@ -1124,3 +1288,323 @@ func TestRewriteRuntimeResourceOverlay(t *testing.T) { }) } } + +func TestRewriteTestModuleTypes(t *testing.T) { + tests := []struct { + name string + in string + out string + }{ + { + name: "cc_binary with test_suites", + in: ` + cc_binary { + name: "foo", + srcs: ["srcs"], + test_suites: ["test_suite1"], + } + `, + out: ` + cc_test { + name: "foo", + srcs: ["srcs"], + test_suites: ["test_suite1"], + } + `, + }, + { + name: "cc_binary without test_suites", + in: ` + cc_binary { + name: "foo", + srcs: ["srcs"], + } + `, + out: ` + cc_binary { + name: "foo", + srcs: ["srcs"], + } + `, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + runPass(t, test.in, test.out, func(fixer *Fixer) error { + return rewriteTestModuleTypes(fixer) + }) + }) + } +} + +func TestFormatFlagProperty(t *testing.T) { + tests := []struct { + name string + in string + out string + }{ + { + name: "group options and values for apptflags, dxflags, javacflags, and kotlincflags", + in: ` + android_test { + name: "foo", + aaptflags: [ + // comment1_1 + "--flag1", + // comment1_2 + "1", + // comment2_1 + // comment2_2 + "--flag2", + // comment3_1 + // comment3_2 + // comment3_3 + "--flag3", + // comment3_4 + // comment3_5 + // comment3_6 + "3", + // other comment1_1 + // other comment1_2 + ], + dxflags: [ + "--flag1", + // comment1_1 + "1", + // comment2_1 + "--flag2", + // comment3_1 + "--flag3", + // comment3_2 + "3", + ], + javacflags: [ + "--flag1", + + "1", + "--flag2", + "--flag3", + "3", + ], + kotlincflags: [ + + "--flag1", + "1", + + "--flag2", + "--flag3", + "3", + + ], + } + `, + out: ` + android_test { + name: "foo", + aaptflags: [ + // comment1_1 + // comment1_2 + "--flag1 1", + // comment2_1 + // comment2_2 + "--flag2", + // comment3_1 + // comment3_2 + // comment3_3 + // comment3_4 + // comment3_5 + // comment3_6 + "--flag3 3", + // other comment1_1 + // other comment1_2 + ], + dxflags: [ + // comment1_1 + "--flag1 1", + // comment2_1 + "--flag2", + // comment3_1 + // comment3_2 + "--flag3 3", + ], + javacflags: [ + + "--flag1 1", + "--flag2", + "--flag3 3", + ], + kotlincflags: [ + + "--flag1 1", + + "--flag2", + "--flag3 3", + + ], + } + `, + }, + { + name: "group options and values for asflags, cflags, clang_asflags, clang_cflags, conlyflags, cppflags, ldflags, and tidy_flags", + in: ` + cc_test { + name: "foo", + asflags: [ + // comment1_1 + "--flag1", + "1", + // comment2_1 + // comment2_2 + "--flag2", + // comment2_3 + "2", + // comment3_1 + // comment3_2 + "--flag3", + // comment3_3 + // comment3_4 + // comment3_4 + "3", + // comment4_1 + // comment4_2 + // comment4_3 + "--flag4", + ], + cflags: [ + "--flag1", + "1", + "--flag2", + "2", + "--flag3", + "3", + "--flag4", + ], + clang_asflags: [ + "--flag1", + "1", + "--flag2", + "2", + "--flag3", + "3", + "--flag4", + ], + clang_cflags: [ + "--flag1", + "1", + "--flag2", + "2", + "--flag3", + "3", + "--flag4", + ], + conlyflags: [ + "--flag1", + "1", + "--flag2", + "2", + "--flag3", + "3", + "--flag4", + ], + cppflags: [ + "--flag1", + "1", + "--flag2", + "2", + "--flag3", + "3", + "--flag4", + ], + ldflags: [ + "--flag1", + "1", + "--flag2", + "2", + "--flag3", + "3", + "--flag4", + ], + tidy_flags: [ + "--flag1", + "1", + "--flag2", + "2", + "--flag3", + "3", + "--flag4", + ], + } + `, + out: ` + cc_test { + name: "foo", + asflags: [ + // comment1_1 + "--flag1 1", + // comment2_1 + // comment2_2 + // comment2_3 + "--flag2 2", + // comment3_1 + // comment3_2 + // comment3_3 + // comment3_4 + // comment3_4 + "--flag3 3", + // comment4_1 + // comment4_2 + // comment4_3 + "--flag4", + ], + cflags: [ + "--flag1 1", + "--flag2 2", + "--flag3 3", + "--flag4", + ], + clang_asflags: [ + "--flag1 1", + "--flag2 2", + "--flag3 3", + "--flag4", + ], + clang_cflags: [ + "--flag1 1", + "--flag2 2", + "--flag3 3", + "--flag4", + ], + conlyflags: [ + "--flag1 1", + "--flag2 2", + "--flag3 3", + "--flag4", + ], + cppflags: [ + "--flag1 1", + "--flag2 2", + "--flag3 3", + "--flag4", + ], + ldflags: [ + "--flag1 1", + "--flag2 2", + "--flag3 3", + "--flag4", + ], + tidy_flags: [ + "--flag1 1", + "--flag2 2", + "--flag3 3", + "--flag4", + ], + } + `, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + runPass(t, test.in, test.out, runPatchListMod(formatFlagProperties)) + }) + } +} diff --git a/cc/binary.go b/cc/binary.go index 48f70d9b8..c177a0899 100644 --- a/cc/binary.go +++ b/cc/binary.go @@ -183,11 +183,6 @@ func (binary *binaryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps { return deps } -func (binary *binaryDecorator) isDependencyRoot() bool { - // Binaries are always the dependency root. - return true -} - // NewBinary builds and returns a new Module corresponding to a C++ binary. // Individual module implementations which comprise a C++ binary should call this function, // set some fields on the result, and then call the Init function. diff --git a/cc/bp2build.go b/cc/bp2build.go index 76c5f3be9..536f1125d 100644 --- a/cc/bp2build.go +++ b/cc/bp2build.go @@ -90,8 +90,11 @@ func depsBp2BuildMutator(ctx android.BottomUpMutatorContext) { // TODO(b/186024507, b/186489250): Temporarily exclude adding // system_shared_libs deps until libc and libm builds. - // allDeps = append(allDeps, lib.SharedProperties.Shared.System_shared_libs...) - // allDeps = append(allDeps, lib.StaticProperties.Static.System_shared_libs...) + if lib.static() { + allDeps = append(allDeps, lib.StaticProperties.Static.System_shared_libs...) + } else if lib.shared() { + allDeps = append(allDeps, lib.SharedProperties.Shared.System_shared_libs...) + } // Deps in the target/arch nested static: { .. } and shared: { .. } props of a cc_library. // target: { <target>: shared: { ... } } @@ -253,7 +256,7 @@ func bp2buildParseStaticOrSharedProps(ctx android.TopDownMutatorContext, module Copts: bazel.StringListAttribute{Value: props.Cflags}, Srcs: bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, props.Srcs)), Static_deps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Static_libs)), - Dynamic_deps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Shared_libs)), + Dynamic_deps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, append(props.Shared_libs, props.System_shared_libs...))), Whole_archive_deps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleWholeDeps(ctx, props.Whole_static_libs)), } @@ -385,16 +388,6 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul return result } - // Parse the list of copts. - parseCopts := func(baseCompilerProps *BaseCompilerProperties) []string { - var copts []string - copts = append(copts, parseCommandLineFlags(baseCompilerProps.Cflags)...) - for _, dir := range parseLocalIncludeDirs(baseCompilerProps) { - copts = append(copts, includeFlags(dir)...) - } - return copts - } - // Parse srcs from an arch or OS's props value. parseSrcs := func(baseCompilerProps *BaseCompilerProperties) bazel.LabelList { // Add srcs-like dependencies such as generated files. @@ -410,11 +403,15 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul for _, props := range module.compiler.compilerProps() { if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok { srcs.SetValue(parseSrcs(baseCompilerProps)) - copts.Value = parseCopts(baseCompilerProps) + copts.Value = parseCommandLineFlags(baseCompilerProps.Cflags) asFlags.Value = parseCommandLineFlags(baseCompilerProps.Asflags) conlyFlags.Value = parseCommandLineFlags(baseCompilerProps.Conlyflags) cppFlags.Value = parseCommandLineFlags(baseCompilerProps.Cppflags) + for _, dir := range parseLocalIncludeDirs(baseCompilerProps) { + copts.Value = append(copts.Value, includeFlags(dir)...) + asFlags.Value = append(asFlags.Value, includeFlags(dir)...) + } break } } @@ -424,8 +421,10 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul // "-I<module-dir>" in its copts. if c, ok := module.compiler.(*baseCompiler); ok && c.includeBuildDirectory() { copts.Value = append(copts.Value, includeFlags(".")...) + asFlags.Value = append(asFlags.Value, includeFlags(".")...) } else if c, ok := module.compiler.(*libraryDecorator); ok && c.includeBuildDirectory() { copts.Value = append(copts.Value, includeFlags(".")...) + asFlags.Value = append(asFlags.Value, includeFlags(".")...) } archVariantCompilerProps := module.GetArchVariantProperties(ctx, &BaseCompilerProperties{}) @@ -440,8 +439,15 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul srcs.SetSelectValue(axis, config, srcsList) } - copts.SetSelectValue(axis, config, parseCopts(baseCompilerProps)) - asFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Asflags)) + archVariantCopts := parseCommandLineFlags(baseCompilerProps.Cflags) + archVariantAsflags := parseCommandLineFlags(baseCompilerProps.Asflags) + for _, dir := range parseLocalIncludeDirs(baseCompilerProps) { + archVariantCopts = append(archVariantCopts, includeFlags(dir)...) + archVariantAsflags = append(archVariantAsflags, includeFlags(dir)...) + } + + copts.SetSelectValue(axis, config, archVariantCopts) + asFlags.SetSelectValue(axis, config, archVariantAsflags) conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Conlyflags)) cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Cppflags)) } @@ -554,7 +560,9 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) staticDeps.Value = android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs) wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs) wholeArchiveDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs)) - sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs) + // TODO(b/186024507): Handle system_shared_libs as its own attribute, so that the appropriate default + // may be supported. + sharedLibs := android.FirstUniqueStrings(append(baseLinkerProps.Shared_libs, baseLinkerProps.System_shared_libs...)) dynamicDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs)) headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs) @@ -581,7 +589,7 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) staticDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs)) wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs) wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs)) - sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs) + sharedLibs := android.FirstUniqueStrings(append(baseLinkerProps.Shared_libs, baseLinkerProps.System_shared_libs...)) dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs)) headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs) @@ -53,25 +53,9 @@ func RegisterCCBuildComponents(ctx android.RegistrationContext) { }) ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { - ctx.TopDown("asan_deps", sanitizerDepsMutator(Asan)) - ctx.BottomUp("asan", sanitizerMutator(Asan)).Parallel() - - ctx.TopDown("hwasan_deps", sanitizerDepsMutator(Hwasan)) - ctx.BottomUp("hwasan", sanitizerMutator(Hwasan)).Parallel() - - ctx.TopDown("fuzzer_deps", sanitizerDepsMutator(Fuzzer)) - ctx.BottomUp("fuzzer", sanitizerMutator(Fuzzer)).Parallel() - - // cfi mutator shouldn't run before sanitizers that return true for - // incompatibleWithCfi() - ctx.TopDown("cfi_deps", sanitizerDepsMutator(cfi)) - ctx.BottomUp("cfi", sanitizerMutator(cfi)).Parallel() - - ctx.TopDown("scs_deps", sanitizerDepsMutator(scs)) - ctx.BottomUp("scs", sanitizerMutator(scs)).Parallel() - - ctx.TopDown("tsan_deps", sanitizerDepsMutator(tsan)) - ctx.BottomUp("tsan", sanitizerMutator(tsan)).Parallel() + for _, san := range Sanitizers { + san.registerMutators(ctx) + } ctx.TopDown("sanitize_runtime_deps", sanitizerRuntimeDepsMutator).Parallel() ctx.BottomUp("sanitize_runtime", sanitizerRuntimeMutator).Parallel() @@ -337,6 +321,7 @@ type BaseProperties struct { // Used by vendor snapshot to record dependencies from snapshot modules. SnapshotSharedLibs []string `blueprint:"mutated"` + SnapshotStaticLibs []string `blueprint:"mutated"` SnapshotRuntimeLibs []string `blueprint:"mutated"` Installable *bool @@ -525,8 +510,6 @@ type DepsContext interface { // feature represents additional (optional) steps to building cc-related modules, such as invocation // of clang-tidy. type feature interface { - begin(ctx BaseModuleContext) - deps(ctx DepsContext, deps Deps) Deps flags(ctx ModuleContext, flags Flags) Flags props() []interface{} } @@ -1120,17 +1103,6 @@ func (c *Module) Init() android.Module { return c } -// Returns true for dependency roots (binaries) -// TODO(ccross): also handle dlopenable libraries -func (c *Module) IsDependencyRoot() bool { - if root, ok := c.linker.(interface { - isDependencyRoot() bool - }); ok { - return root.isDependencyRoot() - } - return false -} - func (c *Module) UseVndk() bool { return c.Properties.VndkVersion != "" } @@ -1898,21 +1870,12 @@ func (c *Module) begin(ctx BaseModuleContext) { if c.coverage != nil { c.coverage.begin(ctx) } - if c.sabi != nil { - c.sabi.begin(ctx) - } - if c.vndkdep != nil { - c.vndkdep.begin(ctx) - } if c.lto != nil { c.lto.begin(ctx) } if c.pgo != nil { c.pgo.begin(ctx) } - for _, feature := range c.features { - feature.begin(ctx) - } if ctx.useSdk() && c.IsSdkVariant() { version, err := nativeApiLevelFromUser(ctx, ctx.sdkVersion()) if err != nil { @@ -1936,24 +1899,9 @@ func (c *Module) deps(ctx DepsContext) Deps { if c.stl != nil { deps = c.stl.deps(ctx, deps) } - if c.sanitize != nil { - deps = c.sanitize.deps(ctx, deps) - } if c.coverage != nil { deps = c.coverage.deps(ctx, deps) } - if c.sabi != nil { - deps = c.sabi.deps(ctx, deps) - } - if c.vndkdep != nil { - deps = c.vndkdep.deps(ctx, deps) - } - if c.lto != nil { - deps = c.lto.deps(ctx, deps) - } - for _, feature := range c.features { - deps = feature.deps(ctx, deps) - } deps.WholeStaticLibs = android.LastUniqueStrings(deps.WholeStaticLibs) deps.StaticLibs = android.LastUniqueStrings(deps.StaticLibs) @@ -2066,9 +2014,9 @@ func AddSharedLibDependenciesWithVersions(ctx android.BottomUpMutatorContext, mo } func GetSnapshot(c LinkableInterface, snapshotInfo **SnapshotInfo, actx android.BottomUpMutatorContext) SnapshotInfo { - // Only modules with BOARD_VNDK_VERSION uses snapshot. Others use the zero value of + // Only device modules with BOARD_VNDK_VERSION uses snapshot. Others use the zero value of // SnapshotInfo, which provides no mappings. - if *snapshotInfo == nil { + if *snapshotInfo == nil && c.Device() { // Only retrieve the snapshot on demand in order to avoid circular dependencies // between the modules in the snapshot and the snapshot itself. var snapshotModule []blueprint.Module @@ -2077,16 +2025,16 @@ func GetSnapshot(c LinkableInterface, snapshotInfo **SnapshotInfo, actx android. } else if recoverySnapshotVersion := actx.DeviceConfig().RecoverySnapshotVersion(); recoverySnapshotVersion != "current" && recoverySnapshotVersion != "" && c.InRecovery() { snapshotModule = actx.AddVariationDependencies(nil, nil, "recovery_snapshot") } - if len(snapshotModule) > 0 { + if len(snapshotModule) > 0 && snapshotModule[0] != nil { snapshot := actx.OtherModuleProvider(snapshotModule[0], SnapshotInfoProvider).(SnapshotInfo) *snapshotInfo = &snapshot // republish the snapshot for use in later mutators on this module actx.SetProvider(SnapshotInfoProvider, snapshot) - } else { - *snapshotInfo = &SnapshotInfo{} } } - + if *snapshotInfo == nil { + *snapshotInfo = &SnapshotInfo{} + } return **snapshotInfo } @@ -2856,6 +2804,8 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { c.Properties.AndroidMkStaticLibs = append( c.Properties.AndroidMkStaticLibs, makeLibName) } + // Record BaseLibName for snapshots. + c.Properties.SnapshotStaticLibs = append(c.Properties.SnapshotStaticLibs, BaseLibName(depName)) } } else if !c.IsStubs() { // Stubs lib doesn't link to the runtime lib, object, crt, etc. dependencies. @@ -3179,6 +3129,13 @@ func (c *Module) Binary() bool { return false } +func (c *Module) StaticExecutable() bool { + if b, ok := c.linker.(*binaryDecorator); ok { + return b.static() + } + return false +} + func (c *Module) Object() bool { if o, ok := c.linker.(interface { object() bool diff --git a/cc/library.go b/cc/library.go index 4fd7c7475..56c460c24 100644 --- a/cc/library.go +++ b/cc/library.go @@ -300,6 +300,12 @@ func CcLibraryBp2Build(ctx android.TopDownMutatorContext) { srcs := compilerAttrs.srcs + asFlags := compilerAttrs.asFlags + if compilerAttrs.asSrcs.IsEmpty() && sharedAttrs.Srcs_as.IsEmpty() && staticAttrs.Srcs_as.IsEmpty() { + // Skip asflags for BUILD file simplicity if there are no assembly sources. + asFlags = bazel.MakeStringListAttribute(nil) + } + attrs := &bazelCcLibraryAttributes{ Srcs: srcs, Srcs_c: compilerAttrs.cSrcs, @@ -308,7 +314,7 @@ func CcLibraryBp2Build(ctx android.TopDownMutatorContext) { Copts: compilerAttrs.copts, Cppflags: compilerAttrs.cppFlags, Conlyflags: compilerAttrs.conlyFlags, - Asflags: compilerAttrs.asFlags, + Asflags: asFlags, Implementation_deps: linkerAttrs.deps, Deps: linkerAttrs.exportedDeps, @@ -2370,6 +2376,12 @@ func ccLibraryStaticBp2BuildInternal(ctx android.TopDownMutatorContext, module * linkerAttrs := bp2BuildParseLinkerProps(ctx, module) exportedIncludes := bp2BuildParseExportedIncludes(ctx, module) + asFlags := compilerAttrs.asFlags + if compilerAttrs.asSrcs.IsEmpty() { + // Skip asflags for BUILD file simplicity if there are no assembly sources. + asFlags = bazel.MakeStringListAttribute(nil) + } + attrs := &bazelCcLibraryStaticAttributes{ Copts: compilerAttrs.copts, Srcs: compilerAttrs.srcs, @@ -2386,7 +2398,7 @@ func ccLibraryStaticBp2BuildInternal(ctx android.TopDownMutatorContext, module * Srcs_c: compilerAttrs.cSrcs, Conlyflags: compilerAttrs.conlyFlags, Srcs_as: compilerAttrs.asSrcs, - Asflags: compilerAttrs.asFlags, + Asflags: asFlags, } props := bazel.BazelTargetModuleProperties{ diff --git a/cc/linkable.go b/cc/linkable.go index 231626ecc..6232efb95 100644 --- a/cc/linkable.go +++ b/cc/linkable.go @@ -14,11 +14,6 @@ type PlatformSanitizeable interface { // SanitizePropDefined returns whether the Sanitizer properties struct for this module is defined. SanitizePropDefined() bool - // IsDependencyRoot returns whether a module is of a type which cannot be a linkage dependency - // of another module. For example, cc_binary and rust_binary represent dependency roots as other - // modules cannot have linkage dependencies against these types. - IsDependencyRoot() bool - // IsSanitizerEnabled returns whether a sanitizer is enabled. IsSanitizerEnabled(t SanitizerType) bool @@ -88,12 +83,18 @@ type Snapshottable interface { // SnapshotLibrary returns true if this module is a snapshot library. IsSnapshotLibrary() bool + // EffectiveLicenseFiles returns the list of License files for this module. + EffectiveLicenseFiles() android.Paths + // SnapshotRuntimeLibs returns a list of libraries needed by this module at runtime but which aren't build dependencies. SnapshotRuntimeLibs() []string // SnapshotSharedLibs returns the list of shared library dependencies for this module. SnapshotSharedLibs() []string + // SnapshotStaticLibs returns the list of static library dependencies for this module. + SnapshotStaticLibs() []string + // IsSnapshotPrebuilt returns true if this module is a snapshot prebuilt. IsSnapshotPrebuilt() bool } @@ -124,6 +125,7 @@ type LinkableInterface interface { IsPrebuilt() bool Toc() android.OptionalPath + Device() bool Host() bool InRamdisk() bool @@ -226,6 +228,9 @@ type LinkableInterface interface { // Header returns true if this is a library headers module. Header() bool + // StaticExecutable returns true if this is a binary module with "static_executable: true". + StaticExecutable() bool + // EverInstallable returns true if the module is ever installable EverInstallable() bool @@ -15,6 +15,8 @@ package cc import ( + "github.com/google/blueprint/proptools" + "android/soong/android" ) @@ -67,23 +69,19 @@ func (lto *lto) props() []interface{} { func (lto *lto) begin(ctx BaseModuleContext) { if ctx.Config().IsEnvTrue("DISABLE_LTO") { - lto.Properties.Lto.Never = boolPtr(true) + lto.Properties.Lto.Never = proptools.BoolPtr(true) } else if ctx.Config().IsEnvTrue("GLOBAL_THINLTO") { staticLib := ctx.static() && !ctx.staticBinary() hostBin := ctx.Host() vndk := ctx.isVndk() // b/169217596 if !staticLib && !hostBin && !vndk { if !lto.Never() && !lto.FullLTO() { - lto.Properties.Lto.Thin = boolPtr(true) + lto.Properties.Lto.Thin = proptools.BoolPtr(true) } } } } -func (lto *lto) deps(ctx BaseModuleContext, deps Deps) Deps { - return deps -} - func (lto *lto) useClangLld(ctx BaseModuleContext) bool { if lto.Properties.Use_clang_lld != nil { return Bool(lto.Properties.Use_clang_lld) @@ -233,12 +231,12 @@ func ltoMutator(mctx android.BottomUpMutatorContext) { // LTO properties for dependencies if name == "lto-full" { - variation.lto.Properties.Lto.Full = boolPtr(true) - variation.lto.Properties.Lto.Thin = boolPtr(false) + variation.lto.Properties.Lto.Full = proptools.BoolPtr(true) + variation.lto.Properties.Lto.Thin = proptools.BoolPtr(false) } if name == "lto-thin" { - variation.lto.Properties.Lto.Full = boolPtr(false) - variation.lto.Properties.Lto.Thin = boolPtr(true) + variation.lto.Properties.Lto.Full = proptools.BoolPtr(false) + variation.lto.Properties.Lto.Thin = proptools.BoolPtr(true) } variation.Properties.PreventInstall = true variation.Properties.HideFromMake = true diff --git a/cc/object.go b/cc/object.go index 39fc43dca..5ded0e94c 100644 --- a/cc/object.go +++ b/cc/object.go @@ -67,9 +67,19 @@ func (handler *objectBazelHandler) generateBazelBuildActions(ctx android.ModuleC } type ObjectLinkerProperties struct { + // list of static library modules that should only provide headers for this module. + Static_libs []string `android:"arch_variant,variant_prepend"` + + // list of shared library modules should only provide headers for this module. + Shared_libs []string `android:"arch_variant"` + // list of modules that should only provide headers for this module. Header_libs []string `android:"arch_variant,variant_prepend"` + // list of default libraries that will provide headers for this module. If unset, generally + // defaults to libc, libm, and libdl. Set to [] to prevent using headers from the defaults. + Default_shared_libs []string `android:"arch_variant"` + // names of other cc_object modules to link into this module using partial linking Objs []string `android:"arch_variant"` @@ -84,8 +94,8 @@ type ObjectLinkerProperties struct { Crt *bool } -func newObject() *Module { - module := newBaseModule(android.HostAndDeviceSupported, android.MultilibBoth) +func newObject(hod android.HostOrDeviceSupported) *Module { + module := newBaseModule(hod, android.MultilibBoth) module.sanitize = &sanitize{} module.stl = &stl{} return module @@ -95,7 +105,7 @@ func newObject() *Module { // necessary, but sometimes used to generate .s files from .c files to use as // input to a cc_genrule module. func ObjectFactory() android.Module { - module := newObject() + module := newObject(android.HostAndDeviceSupported) module.linker = &objectLinker{ baseLinker: NewBaseLinker(module.sanitize), } @@ -113,6 +123,7 @@ func ObjectFactory() android.Module { // For bp2build conversion. type bazelObjectAttributes struct { Srcs bazel.LabelListAttribute + Srcs_as bazel.LabelListAttribute Hdrs bazel.LabelListAttribute Deps bazel.LabelListAttribute Copts bazel.StringListAttribute @@ -169,13 +180,19 @@ func ObjectBp2Build(ctx android.TopDownMutatorContext) { // and this isn't typically done for cc_object. srcs := compilerAttrs.srcs srcs.Append(compilerAttrs.cSrcs) - srcs.Append(compilerAttrs.asSrcs) + + asFlags := compilerAttrs.asFlags + if compilerAttrs.asSrcs.IsEmpty() { + // Skip asflags for BUILD file simplicity if there are no assembly sources. + asFlags = bazel.MakeStringListAttribute(nil) + } attrs := &bazelObjectAttributes{ Srcs: srcs, + Srcs_as: compilerAttrs.asSrcs, Deps: deps, Copts: compilerAttrs.copts, - Asflags: compilerAttrs.asFlags, + Asflags: asFlags, } props := bazel.BazelTargetModuleProperties{ @@ -198,7 +215,18 @@ func (*objectLinker) linkerInit(ctx BaseModuleContext) {} func (object *objectLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.HeaderLibs = append(deps.HeaderLibs, object.Properties.Header_libs...) + deps.SharedLibs = append(deps.SharedLibs, object.Properties.Shared_libs...) + deps.StaticLibs = append(deps.StaticLibs, object.Properties.Static_libs...) deps.ObjFiles = append(deps.ObjFiles, object.Properties.Objs...) + + deps.SystemSharedLibs = object.Properties.Default_shared_libs + if deps.SystemSharedLibs == nil { + // Provide a default set of shared libraries if default_shared_libs is unspecified. + // Note: If an empty list [] is specified, it implies that the module declines the + // default shared libraries. + deps.SystemSharedLibs = append(deps.SystemSharedLibs, ctx.toolchain().DefaultSharedLibraries()...) + } + deps.LateSharedLibs = append(deps.LateSharedLibs, deps.SystemSharedLibs...) return deps } @@ -247,6 +275,20 @@ func (object *objectLinker) link(ctx ModuleContext, return outputFile } +func (object *objectLinker) linkerSpecifiedDeps(specifiedDeps specifiedDeps) specifiedDeps { + specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, object.Properties.Shared_libs...) + + // Must distinguish nil and [] in default_shared_libs - ensure that [] in + // either input list doesn't come out as nil. + if specifiedDeps.defaultSharedLibs == nil { + specifiedDeps.defaultSharedLibs = object.Properties.Default_shared_libs + } else { + specifiedDeps.defaultSharedLibs = append(specifiedDeps.defaultSharedLibs, object.Properties.Default_shared_libs...) + } + + return specifiedDeps +} + func (object *objectLinker) unstrippedOutputFilePath() android.Path { return nil } diff --git a/cc/prebuilt.go b/cc/prebuilt.go index fd310a2fe..f7154ecd1 100644 --- a/cc/prebuilt.go +++ b/cc/prebuilt.go @@ -388,8 +388,8 @@ func (p *prebuiltObjectLinker) object() bool { return true } -func newPrebuiltObject() *Module { - module := newObject() +func NewPrebuiltObject(hod android.HostOrDeviceSupported) *Module { + module := newObject(hod) prebuilt := &prebuiltObjectLinker{ objectLinker: objectLinker{ baseLinker: NewBaseLinker(nil), @@ -403,7 +403,7 @@ func newPrebuiltObject() *Module { } func prebuiltObjectFactory() android.Module { - module := newPrebuiltObject() + module := NewPrebuiltObject(android.HostAndDeviceSupported) return module.Init() } diff --git a/cc/sabi.go b/cc/sabi.go index 1f331cb3d..5fd6f5da3 100644 --- a/cc/sabi.go +++ b/cc/sabi.go @@ -45,12 +45,6 @@ func (sabi *sabi) props() []interface{} { return []interface{}{&sabi.Properties} } -func (sabi *sabi) begin(ctx BaseModuleContext) {} - -func (sabi *sabi) deps(ctx BaseModuleContext, deps Deps) Deps { - return deps -} - func (sabi *sabi) flags(ctx ModuleContext, flags Flags) Flags { // Filter out flags which libTooling don't understand. // This is here for legacy reasons and future-proof, in case the version of libTooling and clang diff --git a/cc/sanitize.go b/cc/sanitize.go index b0eb0c6ab..defe8fde1 100644 --- a/cc/sanitize.go +++ b/cc/sanitize.go @@ -21,6 +21,7 @@ import ( "sync" "github.com/google/blueprint" + "github.com/google/blueprint/proptools" "android/soong/android" "android/soong/cc/config" @@ -73,25 +74,28 @@ var ( type SanitizerType int -func boolPtr(v bool) *bool { - if v { - return &v - } else { - return nil - } -} - const ( Asan SanitizerType = iota + 1 Hwasan tsan intOverflow - cfi scs Fuzzer memtag_heap + cfi // cfi is last to prevent it running before incompatible mutators ) +var Sanitizers = []SanitizerType{ + Asan, + Hwasan, + tsan, + intOverflow, + scs, + Fuzzer, + memtag_heap, + cfi, // cfi is last to prevent it running before incompatible mutators +} + // Name of the sanitizer variation for this sanitizer type func (t SanitizerType) variationName() string { switch t { @@ -140,6 +144,18 @@ func (t SanitizerType) name() string { } } +func (t SanitizerType) registerMutators(ctx android.RegisterMutatorsContext) { + switch t { + case Asan, Hwasan, Fuzzer, scs, tsan, cfi: + ctx.TopDown(t.variationName()+"_deps", sanitizerDepsMutator(t)) + ctx.BottomUp(t.variationName(), sanitizerMutator(t)) + case memtag_heap, intOverflow: + // do nothing + default: + panic(fmt.Errorf("unknown SanitizerType %d", t)) + } +} + func (*Module) SanitizerSupported(t SanitizerType) bool { switch t { case Asan: @@ -167,24 +183,46 @@ func (t SanitizerType) incompatibleWithCfi() bool { } type SanitizeUserProps struct { + // Prevent use of any sanitizers on this module Never *bool `android:"arch_variant"` - // main sanitizers - Address *bool `android:"arch_variant"` - Thread *bool `android:"arch_variant"` + // ASan (Address sanitizer), incompatible with static binaries. + // Always runs in a diagnostic mode. + // Use of address sanitizer disables cfi sanitizer. + // Hwaddress sanitizer takes precedence over this sanitizer. + Address *bool `android:"arch_variant"` + // TSan (Thread sanitizer), incompatible with static binaries and 32 bit architectures. + // Always runs in a diagnostic mode. + // Use of thread sanitizer disables cfi and scudo sanitizers. + // Hwaddress sanitizer takes precedence over this sanitizer. + Thread *bool `android:"arch_variant"` + // HWASan (Hardware Address sanitizer). + // Use of hwasan sanitizer disables cfi, address, thread, and scudo sanitizers. Hwaddress *bool `android:"arch_variant"` - // local sanitizers - Undefined *bool `android:"arch_variant"` - All_undefined *bool `android:"arch_variant"` - Misc_undefined []string `android:"arch_variant"` - Fuzzer *bool `android:"arch_variant"` - Safestack *bool `android:"arch_variant"` - Cfi *bool `android:"arch_variant"` - Integer_overflow *bool `android:"arch_variant"` - Scudo *bool `android:"arch_variant"` - Scs *bool `android:"arch_variant"` - Memtag_heap *bool `android:"arch_variant"` + // Undefined behavior sanitizer + All_undefined *bool `android:"arch_variant"` + // Subset of undefined behavior sanitizer + Undefined *bool `android:"arch_variant"` + // List of specific undefined behavior sanitizers to enable + Misc_undefined []string `android:"arch_variant"` + // Fuzzer, incompatible with static binaries. + Fuzzer *bool `android:"arch_variant"` + // safe-stack sanitizer, incompatible with 32-bit architectures. + Safestack *bool `android:"arch_variant"` + // cfi sanitizer, incompatible with asan, hwasan, fuzzer, or Darwin + Cfi *bool `android:"arch_variant"` + // signed/unsigned integer overflow sanitizer, incompatible with Darwin. + Integer_overflow *bool `android:"arch_variant"` + // scudo sanitizer, incompatible with asan, hwasan, tsan + // This should not be used in Android 11+ : https://source.android.com/devices/tech/debug/scudo + // deprecated + Scudo *bool `android:"arch_variant"` + // shadow-call-stack sanitizer, only available on arm64 + Scs *bool `android:"arch_variant"` + // Memory-tagging, only available on arm64 + // if diag.memtag unset or false, enables async memory tagging + Memtag_heap *bool `android:"arch_variant"` // A modifier for ASAN and HWASAN for write only instrumentation Writeonly *bool `android:"arch_variant"` @@ -193,12 +231,22 @@ type SanitizeUserProps struct { // Replaces abort() on error with a human-readable error message. // Address and Thread sanitizers always run in diagnostic mode. Diag struct { - Undefined *bool `android:"arch_variant"` - Cfi *bool `android:"arch_variant"` - Integer_overflow *bool `android:"arch_variant"` - Memtag_heap *bool `android:"arch_variant"` - Misc_undefined []string `android:"arch_variant"` - No_recover []string `android:"arch_variant"` + // Undefined behavior sanitizer, diagnostic mode + Undefined *bool `android:"arch_variant"` + // cfi sanitizer, diagnostic mode, incompatible with asan, hwasan, fuzzer, or Darwin + Cfi *bool `android:"arch_variant"` + // signed/unsigned integer overflow sanitizer, diagnostic mode, incompatible with Darwin. + Integer_overflow *bool `android:"arch_variant"` + // Memory-tagging, only available on arm64 + // requires sanitizer.memtag: true + // if set, enables sync memory tagging + Memtag_heap *bool `android:"arch_variant"` + // List of specific undefined behavior sanitizers to enable in diagnostic mode + Misc_undefined []string `android:"arch_variant"` + // List of sanitizers to pass to -fno-sanitize-recover + // results in only the first detected error for these sanitizers being reported and program then + // exits with a non-zero exit code. + No_recover []string `android:"arch_variant"` } `android:"arch_variant"` // Sanitizers to run with flag configuration specified @@ -207,7 +255,9 @@ type SanitizeUserProps struct { Cfi_assembly_support *bool `android:"arch_variant"` } `android:"arch_variant"` - // value to pass to -fsanitize-recover= + // List of sanitizers to pass to -fsanitize-recover + // allows execution to continue for these sanitizers to detect multiple errors rather than only + // the first one Recover []string // value to pass to -fsanitize-blacklist @@ -215,9 +265,7 @@ type SanitizeUserProps struct { } type SanitizeProperties struct { - // Enable AddressSanitizer, ThreadSanitizer, UndefinedBehaviorSanitizer, and - // others. Please see SanitizerUserProps in build/soong/cc/sanitize.go for - // details. + // Sanitizers are not supported for Fuchsia. Sanitize SanitizeUserProps `android:"arch_variant"` SanitizerEnabled bool `blueprint:"mutated"` SanitizeDep bool `blueprint:"mutated"` @@ -268,9 +316,13 @@ func (sanitize *sanitize) begin(ctx BaseModuleContext) { } // cc_test targets default to SYNC MemTag unless explicitly set to ASYNC (via diag: {memtag_heap}). - if ctx.testBinary() && s.Memtag_heap == nil { - s.Memtag_heap = boolPtr(true) - s.Diag.Memtag_heap = boolPtr(true) + if ctx.testBinary() { + if s.Memtag_heap == nil { + s.Memtag_heap = proptools.BoolPtr(true) + } + if s.Diag.Memtag_heap == nil { + s.Diag.Memtag_heap = proptools.BoolPtr(true) + } } var globalSanitizers []string @@ -291,48 +343,48 @@ func (sanitize *sanitize) begin(ctx BaseModuleContext) { if len(globalSanitizers) > 0 { var found bool if found, globalSanitizers = removeFromList("undefined", globalSanitizers); found && s.All_undefined == nil { - s.All_undefined = boolPtr(true) + s.All_undefined = proptools.BoolPtr(true) } if found, globalSanitizers = removeFromList("default-ub", globalSanitizers); found && s.Undefined == nil { - s.Undefined = boolPtr(true) + s.Undefined = proptools.BoolPtr(true) } if found, globalSanitizers = removeFromList("address", globalSanitizers); found && s.Address == nil { - s.Address = boolPtr(true) + s.Address = proptools.BoolPtr(true) } if found, globalSanitizers = removeFromList("thread", globalSanitizers); found && s.Thread == nil { - s.Thread = boolPtr(true) + s.Thread = proptools.BoolPtr(true) } if found, globalSanitizers = removeFromList("fuzzer", globalSanitizers); found && s.Fuzzer == nil { - s.Fuzzer = boolPtr(true) + s.Fuzzer = proptools.BoolPtr(true) } if found, globalSanitizers = removeFromList("safe-stack", globalSanitizers); found && s.Safestack == nil { - s.Safestack = boolPtr(true) + s.Safestack = proptools.BoolPtr(true) } if found, globalSanitizers = removeFromList("cfi", globalSanitizers); found && s.Cfi == nil { if !ctx.Config().CFIDisabledForPath(ctx.ModuleDir()) { - s.Cfi = boolPtr(true) + s.Cfi = proptools.BoolPtr(true) } } // Global integer_overflow builds do not support static libraries. if found, globalSanitizers = removeFromList("integer_overflow", globalSanitizers); found && s.Integer_overflow == nil { if !ctx.Config().IntegerOverflowDisabledForPath(ctx.ModuleDir()) && !ctx.static() { - s.Integer_overflow = boolPtr(true) + s.Integer_overflow = proptools.BoolPtr(true) } } if found, globalSanitizers = removeFromList("scudo", globalSanitizers); found && s.Scudo == nil { - s.Scudo = boolPtr(true) + s.Scudo = proptools.BoolPtr(true) } if found, globalSanitizers = removeFromList("hwaddress", globalSanitizers); found && s.Hwaddress == nil { - s.Hwaddress = boolPtr(true) + s.Hwaddress = proptools.BoolPtr(true) } if found, globalSanitizers = removeFromList("writeonly", globalSanitizers); found && s.Writeonly == nil { @@ -341,11 +393,11 @@ func (sanitize *sanitize) begin(ctx BaseModuleContext) { if s.Address == nil && s.Hwaddress == nil { ctx.ModuleErrorf("writeonly modifier cannot be used without 'address' or 'hwaddress'") } - s.Writeonly = boolPtr(true) + s.Writeonly = proptools.BoolPtr(true) } if found, globalSanitizers = removeFromList("memtag_heap", globalSanitizers); found && s.Memtag_heap == nil { if !ctx.Config().MemtagHeapDisabledForPath(ctx.ModuleDir()) { - s.Memtag_heap = boolPtr(true) + s.Memtag_heap = proptools.BoolPtr(true) } } @@ -356,17 +408,17 @@ func (sanitize *sanitize) begin(ctx BaseModuleContext) { // Global integer_overflow builds do not support static library diagnostics. if found, globalSanitizersDiag = removeFromList("integer_overflow", globalSanitizersDiag); found && s.Diag.Integer_overflow == nil && Bool(s.Integer_overflow) && !ctx.static() { - s.Diag.Integer_overflow = boolPtr(true) + s.Diag.Integer_overflow = proptools.BoolPtr(true) } if found, globalSanitizersDiag = removeFromList("cfi", globalSanitizersDiag); found && s.Diag.Cfi == nil && Bool(s.Cfi) { - s.Diag.Cfi = boolPtr(true) + s.Diag.Cfi = proptools.BoolPtr(true) } if found, globalSanitizersDiag = removeFromList("memtag_heap", globalSanitizersDiag); found && s.Diag.Memtag_heap == nil && Bool(s.Memtag_heap) { - s.Diag.Memtag_heap = boolPtr(true) + s.Diag.Memtag_heap = proptools.BoolPtr(true) } if len(globalSanitizersDiag) > 0 { @@ -378,30 +430,30 @@ func (sanitize *sanitize) begin(ctx BaseModuleContext) { if ctx.Arch().ArchType == android.Arm64 { if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) { if s.Memtag_heap == nil { - s.Memtag_heap = boolPtr(true) + s.Memtag_heap = proptools.BoolPtr(true) } if s.Diag.Memtag_heap == nil { - s.Diag.Memtag_heap = boolPtr(true) + s.Diag.Memtag_heap = proptools.BoolPtr(true) } } else if ctx.Config().MemtagHeapAsyncEnabledForPath(ctx.ModuleDir()) { if s.Memtag_heap == nil { - s.Memtag_heap = boolPtr(true) + s.Memtag_heap = proptools.BoolPtr(true) } } } // Enable CFI for all components in the include paths (for Aarch64 only) if s.Cfi == nil && ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) && ctx.Arch().ArchType == android.Arm64 { - s.Cfi = boolPtr(true) + s.Cfi = proptools.BoolPtr(true) if inList("cfi", ctx.Config().SanitizeDeviceDiag()) { - s.Diag.Cfi = boolPtr(true) + s.Diag.Cfi = proptools.BoolPtr(true) } } // Is CFI actually enabled? if !ctx.Config().EnableCFI() { - s.Cfi = boolPtr(false) - s.Diag.Cfi = boolPtr(false) + s.Cfi = nil + s.Diag.Cfi = nil } // HWASan requires AArch64 hardware feature (top-byte-ignore). @@ -421,14 +473,14 @@ func (sanitize *sanitize) begin(ctx BaseModuleContext) { // Also disable CFI if ASAN is enabled. if Bool(s.Address) || Bool(s.Hwaddress) { - s.Cfi = boolPtr(false) - s.Diag.Cfi = boolPtr(false) + s.Cfi = nil + s.Diag.Cfi = nil } // Disable sanitizers that depend on the UBSan runtime for windows/darwin builds. if !ctx.Os().Linux() { - s.Cfi = boolPtr(false) - s.Diag.Cfi = boolPtr(false) + s.Cfi = nil + s.Diag.Cfi = nil s.Misc_undefined = nil s.Undefined = nil s.All_undefined = nil @@ -443,8 +495,8 @@ func (sanitize *sanitize) begin(ctx BaseModuleContext) { s.Cfi = nil s.Diag.Cfi = nil } else { - s.Cfi = boolPtr(false) - s.Diag.Cfi = boolPtr(false) + s.Cfi = nil + s.Diag.Cfi = nil } } @@ -495,18 +547,10 @@ func (sanitize *sanitize) begin(ctx BaseModuleContext) { // TODO(b/131771163): CFI transiently depends on LTO, and thus Fuzzer is // mutually incompatible. if Bool(s.Fuzzer) { - s.Cfi = boolPtr(false) + s.Cfi = nil } } -func (sanitize *sanitize) deps(ctx BaseModuleContext, deps Deps) Deps { - if !sanitize.Properties.SanitizerEnabled { // || c.static() { - return deps - } - - return deps -} - func toDisableImplicitIntegerChange(flags []string) bool { // Returns true if any flag is fsanitize*integer, and there is // no explicit flag about sanitize=implicit-integer-sign-change. @@ -789,23 +833,27 @@ func (sanitize *sanitize) isVariantOnProductionDevice() bool { } func (sanitize *sanitize) SetSanitizer(t SanitizerType, b bool) { + bPtr := proptools.BoolPtr(b) + if !b { + bPtr = nil + } switch t { case Asan: - sanitize.Properties.Sanitize.Address = boolPtr(b) + sanitize.Properties.Sanitize.Address = bPtr case Hwasan: - sanitize.Properties.Sanitize.Hwaddress = boolPtr(b) + sanitize.Properties.Sanitize.Hwaddress = bPtr case tsan: - sanitize.Properties.Sanitize.Thread = boolPtr(b) + sanitize.Properties.Sanitize.Thread = bPtr case intOverflow: - sanitize.Properties.Sanitize.Integer_overflow = boolPtr(b) + sanitize.Properties.Sanitize.Integer_overflow = bPtr case cfi: - sanitize.Properties.Sanitize.Cfi = boolPtr(b) + sanitize.Properties.Sanitize.Cfi = bPtr case scs: - sanitize.Properties.Sanitize.Scs = boolPtr(b) + sanitize.Properties.Sanitize.Scs = bPtr case memtag_heap: - sanitize.Properties.Sanitize.Memtag_heap = boolPtr(b) + sanitize.Properties.Sanitize.Memtag_heap = bPtr case Fuzzer: - sanitize.Properties.Sanitize.Fuzzer = boolPtr(b) + sanitize.Properties.Sanitize.Fuzzer = bPtr default: panic(fmt.Errorf("unknown SanitizerType %d", t)) } @@ -1261,7 +1309,7 @@ var _ PlatformSanitizeable = (*Module)(nil) func sanitizerMutator(t SanitizerType) func(android.BottomUpMutatorContext) { return func(mctx android.BottomUpMutatorContext) { if c, ok := mctx.Module().(PlatformSanitizeable); ok && c.SanitizePropDefined() { - if c.IsDependencyRoot() && c.IsSanitizerEnabled(t) { + if c.Binary() && c.IsSanitizerEnabled(t) { modules := mctx.CreateVariations(t.variationName()) modules[0].(PlatformSanitizeable).SetSanitizer(t, true) } else if c.IsSanitizerEnabled(t) || c.SanitizeDep() { diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go index 4430fc38f..0070e4026 100644 --- a/cc/sanitize_test.go +++ b/cc/sanitize_test.go @@ -222,11 +222,12 @@ func (t MemtagNoteType) str() string { case Async: return "async" default: - panic("invalid note type") + panic("type_note_invalid") } } func checkHasMemtagNote(t *testing.T, m android.TestingModule, expected MemtagNoteType) { + t.Helper() note_async := "note_memtag_heap_async" note_sync := "note_memtag_heap_sync" @@ -251,75 +252,86 @@ var prepareForTestWithMemtagHeap = android.GroupFixturePreparers( android.FixtureModifyMockFS(func(fs android.MockFS) { templateBp := ` cc_test { - name: "%[1]s_test", + name: "unset_test_%[1]s", gtest: false, } cc_test { - name: "%[1]s_test_false", + name: "no_memtag_test_%[1]s", gtest: false, sanitize: { memtag_heap: false }, } cc_test { - name: "%[1]s_test_true", + name: "set_memtag_test_%[1]s", gtest: false, sanitize: { memtag_heap: true }, } cc_test { - name: "%[1]s_test_true_nodiag", + name: "set_memtag_set_async_test_%[1]s", gtest: false, sanitize: { memtag_heap: true, diag: { memtag_heap: false } }, } cc_test { - name: "%[1]s_test_true_diag", + name: "set_memtag_set_sync_test_%[1]s", gtest: false, sanitize: { memtag_heap: true, diag: { memtag_heap: true } }, } + cc_test { + name: "unset_memtag_set_sync_test_%[1]s", + gtest: false, + sanitize: { diag: { memtag_heap: true } }, + } + cc_binary { - name: "%[1]s_binary", + name: "unset_binary_%[1]s", } cc_binary { - name: "%[1]s_binary_false", + name: "no_memtag_binary_%[1]s", sanitize: { memtag_heap: false }, } cc_binary { - name: "%[1]s_binary_true", + name: "set_memtag_binary_%[1]s", sanitize: { memtag_heap: true }, } cc_binary { - name: "%[1]s_binary_true_nodiag", + name: "set_memtag_set_async_binary_%[1]s", sanitize: { memtag_heap: true, diag: { memtag_heap: false } }, } cc_binary { - name: "%[1]s_binary_true_diag", + name: "set_memtag_set_sync_binary_%[1]s", sanitize: { memtag_heap: true, diag: { memtag_heap: true } }, } + + cc_binary { + name: "unset_memtag_set_sync_binary_%[1]s", + sanitize: { diag: { memtag_heap: true } }, + } ` - subdirDefaultBp := fmt.Sprintf(templateBp, "default") - subdirExcludeBp := fmt.Sprintf(templateBp, "exclude") - subdirSyncBp := fmt.Sprintf(templateBp, "sync") - subdirAsyncBp := fmt.Sprintf(templateBp, "async") + subdirNoOverrideBp := fmt.Sprintf(templateBp, "no_override") + subdirOverrideDefaultDisableBp := fmt.Sprintf(templateBp, "override_default_disable") + subdirSyncBp := fmt.Sprintf(templateBp, "override_default_sync") + subdirAsyncBp := fmt.Sprintf(templateBp, "override_default_async") fs.Merge(android.MockFS{ - "subdir_default/Android.bp": []byte(subdirDefaultBp), - "subdir_exclude/Android.bp": []byte(subdirExcludeBp), - "subdir_sync/Android.bp": []byte(subdirSyncBp), - "subdir_async/Android.bp": []byte(subdirAsyncBp), + "subdir_no_override/Android.bp": []byte(subdirNoOverrideBp), + "subdir_override_default_disable/Android.bp": []byte(subdirOverrideDefaultDisableBp), + "subdir_sync/Android.bp": []byte(subdirSyncBp), + "subdir_async/Android.bp": []byte(subdirAsyncBp), }) }), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.MemtagHeapExcludePaths = []string{"subdir_exclude"} - // "subdir_exclude" is covered by both include and exclude paths. Exclude wins. - variables.MemtagHeapSyncIncludePaths = []string{"subdir_sync", "subdir_exclude"} - variables.MemtagHeapAsyncIncludePaths = []string{"subdir_async", "subdir_exclude"} + variables.MemtagHeapExcludePaths = []string{"subdir_override_default_disable"} + // "subdir_override_default_disable" is covered by both include and override_default_disable paths. override_default_disable wins. + variables.MemtagHeapSyncIncludePaths = []string{"subdir_sync", "subdir_override_default_disable"} + variables.MemtagHeapAsyncIncludePaths = []string{"subdir_async", "subdir_override_default_disable"} }), ) @@ -332,53 +344,67 @@ func TestSanitizeMemtagHeap(t *testing.T) { ).RunTest(t) ctx := result.TestContext - checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_no_override", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_async", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_disable", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_sync", variant), None) + + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_no_override", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_async", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_disable", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_sync", variant), None) + + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_no_override", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_async", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_disable", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_async", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_disable", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_no_override", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_async", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_disable", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_sync", variant), Async) + + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_no_override", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_async", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_disable", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_sync", variant), Async) + + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_async", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_disable", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_async", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_disable", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_sync", variant), Sync) + + // should sanitize: { diag: { memtag: true } } result in Sync instead of None here? + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_no_override", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_async", variant), Sync) + // should sanitize: { diag: { memtag: true } } result in Sync instead of None here? + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_disable", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_async", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_disable", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_no_override", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_async", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_disable", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_async", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync) } func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) { @@ -393,53 +419,66 @@ func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) { ).RunTest(t) ctx := result.TestContext - checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_no_override", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_async", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_disable", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_sync", variant), None) + + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_no_override", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_async", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_disable", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_sync", variant), None) + + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_no_override", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_async", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_disable", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_async", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_disable", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_no_override", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_async", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_disable", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_sync", variant), Async) + + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_no_override", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_async", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_disable", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_sync", variant), Async) + + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_async", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_disable", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_async", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_disable", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_async", variant), Sync) + // should sanitize: { diag: { memtag: true } } result in Sync instead of None here? + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_disable", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_async", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_disable", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_no_override", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_async", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_disable", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_async", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync) } func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) { @@ -455,51 +494,64 @@ func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) { ).RunTest(t) ctx := result.TestContext - checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_no_override", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_async", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_disable", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_sync", variant), None) + + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_no_override", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_async", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_disable", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_sync", variant), None) + + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_async", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_disable", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_async", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_disable", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_no_override", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_async", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_disable", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_sync", variant), Async) + + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_no_override", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_async", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_disable", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_sync", variant), Async) + + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_async", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_disable", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_async", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_disable", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_async", variant), Sync) + // should sanitize: { diag: { memtag: true } } result in Sync instead of None here? + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_disable", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_async", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_disable", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_async", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_disable", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_sync", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_no_override", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_async", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync) } diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go index 3a382a18f..4f031ff96 100644 --- a/cc/snapshot_prebuilt.go +++ b/cc/snapshot_prebuilt.go @@ -264,6 +264,7 @@ var recoverySnapshotImageSingleton recoverySnapshotImage func init() { VendorSnapshotImageSingleton.Init(android.InitRegistrationContext) recoverySnapshotImageSingleton.init(android.InitRegistrationContext) + android.RegisterMakeVarsProvider(pctx, snapshotMakeVarsProvider) } const ( @@ -383,6 +384,24 @@ var SnapshotInfoProvider = blueprint.NewMutatorProvider(SnapshotInfo{}, "deps") var _ android.ImageInterface = (*snapshot)(nil) +func snapshotMakeVarsProvider(ctx android.MakeVarsContext) { + snapshotSet := map[string]struct{}{} + ctx.VisitAllModules(func(m android.Module) { + if s, ok := m.(*snapshot); ok { + if _, ok := snapshotSet[s.Name()]; ok { + // arch variant generates duplicated modules + // skip this as we only need to know the path of the module. + return + } + snapshotSet[s.Name()] = struct{}{} + imageNameVersion := strings.Split(s.image.imageVariantName(ctx.DeviceConfig()), ".") + ctx.Strict( + strings.Join([]string{strings.ToUpper(imageNameVersion[0]), s.baseSnapshot.Version(), "SNAPSHOT_DIR"}, "_"), + ctx.ModuleDir(s)) + } + }) +} + func vendorSnapshotFactory() android.Module { return snapshotFactory(VendorSnapshotImageSingleton) } @@ -931,7 +950,7 @@ func (p *snapshotObjectLinker) nativeCoverage() bool { // development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_object // overrides the vendor variant of the cc object with the same name, if BOARD_VNDK_VERSION is set. func VendorSnapshotObjectFactory() android.Module { - module := newObject() + module := newObject(android.DeviceSupported) prebuilt := &snapshotObjectLinker{ objectLinker: objectLinker{ @@ -949,7 +968,7 @@ func VendorSnapshotObjectFactory() android.Module { // development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_object // overrides the recovery variant of the cc object with the same name, if BOARD_VNDK_VERSION is set. func RecoverySnapshotObjectFactory() android.Module { - module := newObject() + module := newObject(android.DeviceSupported) prebuilt := &snapshotObjectLinker{ objectLinker: objectLinker{ diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go index a6c8ed522..b0538bea0 100644 --- a/cc/snapshot_utils.go +++ b/cc/snapshot_utils.go @@ -53,6 +53,10 @@ func (m *Module) SnapshotSharedLibs() []string { return m.Properties.SnapshotSharedLibs } +func (m *Module) SnapshotStaticLibs() []string { + return m.Properties.SnapshotStaticLibs +} + // snapshotLibraryInterface is an interface for libraries captured to VNDK / vendor snapshots. type snapshotLibraryInterface interface { libraryInterface diff --git a/cc/testing.go b/cc/testing.go index 80cc0ef67..b9d84f6cb 100644 --- a/cc/testing.go +++ b/cc/testing.go @@ -367,6 +367,7 @@ func commonDefaultModules() string { stl: "none", min_sdk_version: "16", crt: true, + default_shared_libs: [], apex_available: [ "//apex_available:platform", "//apex_available:anyapex", diff --git a/cc/tidy.go b/cc/tidy.go index 616cf8ab4..b2382e88a 100644 --- a/cc/tidy.go +++ b/cc/tidy.go @@ -61,13 +61,6 @@ func (tidy *tidyFeature) props() []interface{} { return []interface{}{&tidy.Properties} } -func (tidy *tidyFeature) begin(ctx BaseModuleContext) { -} - -func (tidy *tidyFeature) deps(ctx DepsContext, deps Deps) Deps { - return deps -} - func (tidy *tidyFeature) flags(ctx ModuleContext, flags Flags) Flags { CheckBadTidyFlags(ctx, "tidy_flags", tidy.Properties.Tidy_flags) CheckBadTidyChecks(ctx, "tidy_checks", tidy.Properties.Tidy_checks) diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go index 5a303c806..003b7c98b 100644 --- a/cc/vendor_snapshot.go +++ b/cc/vendor_snapshot.go @@ -242,10 +242,12 @@ type snapshotJsonFlags struct { SanitizeUbsanDep bool `json:",omitempty"` // binary flags - Symlinks []string `json:",omitempty"` + Symlinks []string `json:",omitempty"` + StaticExecutable bool `json:",omitempty"` // dependencies SharedLibs []string `json:",omitempty"` + StaticLibs []string `json:",omitempty"` RuntimeLibs []string `json:",omitempty"` Required []string `json:",omitempty"` @@ -381,6 +383,8 @@ func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) { if m.Shared() { prop.SharedLibs = m.SnapshotSharedLibs() } + // static libs dependencies are required to collect the NOTICE files. + prop.StaticLibs = m.SnapshotStaticLibs() if sanitizable, ok := m.(PlatformSanitizeable); ok { if sanitizable.Static() && sanitizable.SanitizePropDefined() { prop.SanitizeMinimalDep = sanitizable.MinimalRuntimeDep() || sanitizable.MinimalRuntimeNeeded() @@ -426,8 +430,10 @@ func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) { } else if m.Binary() { // binary flags prop.Symlinks = m.Symlinks() + prop.StaticExecutable = m.StaticExecutable() prop.SharedLibs = m.SnapshotSharedLibs() - + // static libs dependencies are required to collect the NOTICE files. + prop.StaticLibs = m.SnapshotStaticLibs() // install bin binPath := m.OutputFile().Path() snapshotBinOut := filepath.Join(snapshotArchDir, targetArch, "binary", binPath.Base()) @@ -495,13 +501,13 @@ func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) { headers = append(headers, m.SnapshotHeaders()...) } - if len(m.NoticeFiles()) > 0 { + if len(m.EffectiveLicenseFiles()) > 0 { noticeName := ctx.ModuleName(m) + ".txt" noticeOut := filepath.Join(noticeDir, noticeName) // skip already copied notice file if !installedNotices[noticeOut] { installedNotices[noticeOut] = true - snapshotOutputs = append(snapshotOutputs, combineNoticesRule(ctx, m.NoticeFiles(), noticeOut)) + snapshotOutputs = append(snapshotOutputs, combineNoticesRule(ctx, m.EffectiveLicenseFiles(), noticeOut)) } } }) diff --git a/cc/vndk.go b/cc/vndk.go index 0b4007605..499d4282c 100644 --- a/cc/vndk.go +++ b/cc/vndk.go @@ -100,12 +100,6 @@ func (vndk *vndkdep) props() []interface{} { return []interface{}{&vndk.Properties} } -func (vndk *vndkdep) begin(ctx BaseModuleContext) {} - -func (vndk *vndkdep) deps(ctx BaseModuleContext, deps Deps) Deps { - return deps -} - func (vndk *vndkdep) isVndk() bool { return Bool(vndk.Properties.Vndk.Enabled) } diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go index 57b4c4f3f..94b9adf76 100644 --- a/dexpreopt/class_loader_context.go +++ b/dexpreopt/class_loader_context.go @@ -15,6 +15,7 @@ package dexpreopt import ( + "encoding/json" "fmt" "sort" "strconv" @@ -363,6 +364,15 @@ func (clcMap ClassLoaderContextMap) UsesLibs() (ulibs []string) { return ulibs } +func (clcMap ClassLoaderContextMap) Dump() string { + jsonCLC := toJsonClassLoaderContext(clcMap) + bytes, err := json.MarshalIndent(jsonCLC, "", " ") + if err != nil { + panic(err) + } + return string(bytes) +} + // Now that the full unconditional context is known, reconstruct conditional context. // Apply filters for individual libraries, mirroring what the PackageManager does when it // constructs class loader context on device. diff --git a/java/app.go b/java/app.go index fc1ace07b..fc6e183e6 100755 --- a/java/app.go +++ b/java/app.go @@ -1213,7 +1213,7 @@ func (u *usesLibrary) addLib(lib string, optional bool) { } func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, hasFrameworkLibs bool) { - if !ctx.Config().UnbundledBuild() { + if !ctx.Config().UnbundledBuild() || ctx.Config().UnbundledBuildImage() { ctx.AddVariationDependencies(nil, usesLibTag, u.usesLibraryProperties.Uses_libs...) ctx.AddVariationDependencies(nil, usesLibTag, u.presentOptionalUsesLibs(ctx)...) // Only add these extra dependencies if the module depends on framework libs. This avoids @@ -1249,15 +1249,16 @@ func replaceInList(list []string, oldstr, newstr string) { // to their dex jars on host and on device. func (u *usesLibrary) classLoaderContextForUsesLibDeps(ctx android.ModuleContext) dexpreopt.ClassLoaderContextMap { clcMap := make(dexpreopt.ClassLoaderContextMap) - - if !ctx.Config().UnbundledBuild() { + // Skip when UnbundledBuild() is true, but UnbundledBuildImage() is false. + // Added UnbundledBuildImage() condition to generate dexpreopt.config even though unbundled image is built. + if !ctx.Config().UnbundledBuild() || ctx.Config().UnbundledBuildImage() { ctx.VisitDirectDeps(func(m android.Module) { if tag, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok { dep := ctx.OtherModuleName(m) if lib, ok := m.(UsesLibraryDependency); ok { - libName := dep + libName := android.RemoveOptionalPrebuiltPrefix(dep) if ulib, ok := m.(ProvidesUsesLib); ok && ulib.ProvidesUsesLib() != nil { - libName = *ulib.ProvidesUsesLib() + libName = android.RemoveOptionalPrebuiltPrefix(*ulib.ProvidesUsesLib()) // Replace module name with library name in `uses_libs`/`optional_uses_libs` // in order to pass verify_uses_libraries check (which compares these // properties against library names written in the manifest). diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index 515dd89d3..bdf0daeaf 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -162,11 +162,12 @@ type commonBootclasspathFragment interface { // versioned sdk. produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput - // produceBootImageFiles produces the boot image (i.e. .art, .oat and .vdex) files for each of the - // required android.ArchType values in the returned map. + // produceBootImageFiles will attempt to produce rules to create the boot image files at the paths + // predefined in the bootImageConfig. // - // It must return nil if the boot image files cannot be produced for whatever reason. - produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch + // If it could not create the files then it will return nil. Otherwise, it will return a map from + // android.ArchType to the predefined paths of the boot image files. + produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch } var _ commonBootclasspathFragment = (*BootclasspathFragmentModule)(nil) @@ -454,9 +455,13 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo if imageConfig != nil { // Delegate the production of the boot image files to a module type specific method. common := ctx.Module().(commonBootclasspathFragment) - bootImageFilesByArch = common.produceBootImageFiles(ctx, imageConfig, contents) + bootImageFilesByArch = common.produceBootImageFiles(ctx, imageConfig) if shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) { + // Zip the boot image files up, if available. This will generate the zip file in a + // predefined location. + buildBootImageZipInPredefinedLocation(ctx, imageConfig, bootImageFilesByArch) + // Copy the dex jars of this fragment's content modules to their predefined locations. copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule) } @@ -532,27 +537,18 @@ func (b *BootclasspathFragmentModule) configuredJars(ctx android.ModuleContext) global := dexpreopt.GetGlobalConfig(ctx) - // Convert content names to their appropriate stems, in case a test library is overriding an actual boot jar - var stems []string - for _, name := range b.properties.Contents { - dep := ctx.GetDirectDepWithTag(name, bootclasspathFragmentContentDepTag) - if m, ok := dep.(ModuleWithStem); ok { - stems = append(stems, m.Stem()) - } else { - ctx.PropertyErrorf("contents", "%v is not a ModuleWithStem", name) - } - } + possibleUpdatableModules := gatherPossibleUpdatableModuleNamesAndStems(ctx, b.properties.Contents, bootclasspathFragmentContentDepTag) // Only create configs for updatable boot jars. Non-updatable boot jars must be part of the // platform_bootclasspath's classpath proto config to guarantee that they come before any // updatable jars at runtime. - jars := global.UpdatableBootJars.Filter(stems) + jars := global.UpdatableBootJars.Filter(possibleUpdatableModules) // TODO(satayev): for apex_test we want to include all contents unconditionally to classpaths // config. However, any test specific jars would not be present in UpdatableBootJars. Instead, // we should check if we are creating a config for apex_test via ApexInfo and amend the values. // This is an exception to support end-to-end test for SdkExtensions, until such support exists. - if android.InList("test_framework-sdkextensions", stems) { + if android.InList("test_framework-sdkextensions", possibleUpdatableModules) { jars = jars.Append("com.android.sdkext", "test_framework-sdkextensions") } return jars @@ -673,55 +669,49 @@ func (b *BootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleC } // produceBootImageFiles builds the boot image files from the source if it is required. -func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch { +func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch { if SkipDexpreoptBootJars(ctx) { return nil } - // Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars - // GenerateSingletonBuildActions method as it cannot create it for itself. - dexpreopt.GetGlobalSoongConfig(ctx) - // Only generate the boot image if the configuration does not skip it. - if !b.generateBootImageBuildActions(ctx, contents, imageConfig) { - return nil - } - - // Only make the files available to an apex if they were actually generated. - files := bootImageFilesByArch{} - for _, variant := range imageConfig.apexVariants() { - files[variant.target.Arch.ArchType] = variant.imagesDeps.Paths() - } - - return files + return b.generateBootImageBuildActions(ctx, imageConfig) } // generateBootImageBuildActions generates ninja rules to create the boot image if required for this // module. // -// Returns true if the boot image is created, false otherwise. -func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android.ModuleContext, contents []android.Module, imageConfig *bootImageConfig) bool { +// If it could not create the files then it will return nil. Otherwise, it will return a map from +// android.ArchType to the predefined paths of the boot image files. +func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch { global := dexpreopt.GetGlobalConfig(ctx) if !shouldBuildBootImages(ctx.Config(), global) { - return false + return nil } // Bootclasspath fragment modules that are for the platform do not produce a boot image. apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) if apexInfo.IsForPlatform() { - return false + return nil } // Bootclasspath fragment modules that are versioned do not produce a boot image. if android.IsModuleInVersionedSdk(ctx.Module()) { - return false + return nil } // Build a profile for the image config and then use that to build the boot image. profile := bootImageProfileRule(ctx, imageConfig) - buildBootImage(ctx, imageConfig, profile) - return true + // Build boot image files for the host variants. + buildBootImageVariantsForBuildOs(ctx, imageConfig, profile) + + // Build boot image files for the android variants. + androidBootImageFilesByArch := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile) + + // Return the boot image files for the android variants for inclusion in an APEX and to be zipped + // up for the dist. + return androidBootImageFilesByArch } // Collect information for opening IDE project files in java/jdeps.go. @@ -949,7 +939,7 @@ func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx an } // produceBootImageFiles extracts the boot image files from the APEX if available. -func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch { +func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch { if !shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) { return nil } @@ -971,6 +961,7 @@ func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx and } di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo) + files := bootImageFilesByArch{} for _, variant := range imageConfig.apexVariants() { arch := variant.target.Arch.ArchType for _, toPath := range variant.imagesDeps { @@ -978,6 +969,10 @@ func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx and // Get the path to the file that the deapexer extracted from the prebuilt apex file. fromPath := di.PrebuiltExportPath(apexRelativePath) + // Return the toPath as the calling code expects the paths in the returned map to be the + // paths predefined in the bootImageConfig. + files[arch] = append(files[arch], toPath) + // Copy the file to the predefined location. ctx.Build(pctx, android.BuildParams{ Rule: android.Cp, @@ -987,10 +982,13 @@ func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx and } } - // The returned files will be made available to APEXes that include a bootclasspath_fragment. - // However, as a prebuilt_bootclasspath_fragment can never contribute to an APEX there is no point - // in returning any files. - return nil + // Build the boot image files for the host variants. These are built from the dex files provided + // by the contents of this module as prebuilt versions of the host boot image files are not + // available, i.e. there is no host specific prebuilt apex containing them. This has to be built + // without a profile as the prebuilt modules do not provide a profile. + buildBootImageVariantsForBuildOs(ctx, imageConfig, nil) + + return files } var _ commonBootclasspathFragment = (*prebuiltBootclasspathFragmentModule)(nil) diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go index f7a200ad3..12bb71174 100644 --- a/java/classpath_fragment.go +++ b/java/classpath_fragment.go @@ -91,6 +91,29 @@ type classpathJar struct { maxSdkVersion int32 } +// gatherPossibleUpdatableModuleNamesAndStems returns a set of module and stem names from the +// supplied contents that may be in the updatable boot jars. +// +// The module names are included because sometimes the stem is set to just change the name of +// the installed file and it expects the configuration to still use the actual module name. +// +// The stem names are included because sometimes the stem is set to change the effective name of the +// module that is used in the configuration as well,e .g. when a test library is overriding an +// actual boot jar +func gatherPossibleUpdatableModuleNamesAndStems(ctx android.ModuleContext, contents []string, tag blueprint.DependencyTag) []string { + set := map[string]struct{}{} + for _, name := range contents { + dep := ctx.GetDirectDepWithTag(name, tag) + set[name] = struct{}{} + if m, ok := dep.(ModuleWithStem); ok { + set[m.Stem()] = struct{}{} + } else { + ctx.PropertyErrorf("contents", "%v is not a ModuleWithStem", name) + } + } + return android.SortedStringKeys(set) +} + // Converts android.ConfiguredJarList into a list of classpathJars for each given classpathType. func configuredJarListToClasspathJars(ctx android.ModuleContext, configuredJars android.ConfiguredJarList, classpaths ...classpathType) []classpathJar { paths := configuredJars.DevicePaths(ctx.Config(), android.Android) diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp new file mode 100644 index 000000000..51d998a2e --- /dev/null +++ b/java/core-libraries/Android.bp @@ -0,0 +1,194 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +// Definitions for building the Android core libraries, i.e. ART, I18n and +// Conscrypt. +// +// These are here as the definitions are used by the build itself and include +// parts from all three of those modules. +// + +// A stubs target containing the parts of the public SDK API provided by the +// core libraries. +// +// Don't use this directly, use "sdk_version: core_current". +java_library { + name: "core.current.stubs", + visibility: ["//visibility:public"], + static_libs: [ + "art.module.public.api.stubs", + "conscrypt.module.public.api.stubs", + "i18n.module.public.api.stubs", + ], + sdk_version: "none", + system_modules: "none", + + dist: { + targets: [ + "sdk", + "win_sdk", + ], + }, +} + +// Distributed with the SDK for turning into system modules to compile apps +// against. +java_library { + name: "core-current-stubs-for-system-modules", + visibility: ["//development/sdk"], + static_libs: [ + "core.current.stubs", + // This one is not on device but it's needed when javac compiles code + // containing lambdas. + "core-lambda-stubs-for-system-modules", + // This one is not on device but it's needed when javac compiles code + // containing @Generated annotations produced by some code generation + // tools. + // See http://b/123891440. + "core-generated-annotation-stubs", + ], + sdk_version: "none", + system_modules: "none", + dist: { + dest: "core-for-system-modules.jar", + targets: [ + "sdk", + "win_sdk", + ], + }, +} + +// Used when compiling higher-level code against core.current.stubs. +java_system_modules { + name: "core-current-stubs-system-modules", + visibility: ["//visibility:public"], + libs: [ + "core-current-stubs-for-system-modules", + ], +} + +// A stubs target containing the parts of the public SDK & @SystemApi(MODULE_LIBRARIES) API +// provided by the core libraries. +// +// Don't use this directly, use "sdk_version: module_current". +java_library { + name: "core.module_lib.stubs", + static_libs: [ + "art.module.public.api.stubs.module_lib", + + // Replace the following with the module-lib correspondence when Conscrypt or i18N module + // provides @SystemApi(MODULE_LIBRARIES). Currently, assume that only ART module provides + // @SystemApi(MODULE_LIBRARIES). + "conscrypt.module.public.api.stubs", + "i18n.module.public.api.stubs", + ], + sdk_version: "none", + system_modules: "none", + visibility: ["//visibility:private"], +} + +// Used when compiling higher-level code with sdk_version "module_current" +java_system_modules { + name: "core-module-lib-stubs-system-modules", + libs: [ + "core.module_lib.stubs", + // This one is not on device but it's needed when javac compiles code + // containing lambdas. + "core-lambda-stubs-for-system-modules", + // This one is not on device but it's needed when javac compiles code + // containing @Generated annotations produced by some code generation + // tools. + // See http://b/123891440. + "core-generated-annotation-stubs", + ], + visibility: ["//visibility:public"], +} + +// Ideally this should be a restricted allowlist but there are hundreds of modules that depend on +// this. +// TODO(http://b/134561230) - limit the number of dependents on this. +core_platform_visibility = ["//visibility:public"] + +// Libraries containing the core platform API stubs for the core libraries. +// +// Although this stubs library is primarily used by the Java compiler / build to indicate +// the core platform API surface area, compile_dex: true is used so that the Core Platform +// API annotations are available to the dex tools that enable enforcement of runtime +// accessibility. b/119068555 +java_library { + name: "legacy.core.platform.api.stubs", + visibility: core_platform_visibility, + hostdex: true, + compile_dex: true, + + sdk_version: "none", + system_modules: "none", + static_libs: [ + "art.module.public.api.stubs.module_lib", + "conscrypt.module.platform.api.stubs", + "legacy.i18n.module.platform.api.stubs", + ], + patch_module: "java.base", +} + +java_library { + name: "stable.core.platform.api.stubs", + visibility: core_platform_visibility, + hostdex: true, + compile_dex: true, + + sdk_version: "none", + system_modules: "none", + static_libs: [ + "art.module.public.api.stubs.module_lib", + // conscrypt only has a stable version, so it is okay to depend on it here: + "conscrypt.module.platform.api.stubs", + "stable.i18n.module.platform.api.stubs", + ], + patch_module: "java.base", +} + +// Used when compiling higher-level code against *.core.platform.api.stubs. +java_system_modules { + name: "legacy-core-platform-api-stubs-system-modules", + visibility: core_platform_visibility, + libs: [ + "legacy.core.platform.api.stubs", + // This one is not on device but it's needed when javac compiles code + // containing lambdas. + "core-lambda-stubs-for-system-modules", + // This one is not on device but it's needed when javac compiles code + // containing @Generated annotations produced by some code generation + // tools. + // See http://b/123891440. + "core-generated-annotation-stubs", + ], +} + +java_system_modules { + name: "stable-core-platform-api-stubs-system-modules", + visibility: core_platform_visibility, + libs: [ + "stable.core.platform.api.stubs", + // This one is not on device but it's needed when javac compiles code + // containing lambdas. + "core-lambda-stubs-for-system-modules", + // This one is not on device but it's needed when javac compiles code + // containing @Generated annotations produced by some code generation + // tools. + // See http://b/123891440. + "core-generated-annotation-stubs", + ], +} diff --git a/java/core-libraries/OWNERS b/java/core-libraries/OWNERS new file mode 100644 index 000000000..bb3546abd --- /dev/null +++ b/java/core-libraries/OWNERS @@ -0,0 +1,3 @@ +include platform/external/icu:/OWNERS +include platform/external/conscrypt:/OWNERS +include platform/libcore:/OWNERS diff --git a/java/dexpreopt.go b/java/dexpreopt.go index 2e46d74fa..0faae36ba 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -141,10 +141,9 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr } } - // If it is neither app nor test, make config files regardless of its dexpreopt setting. + // If it is test, make config files regardless of its dexpreopt setting. // The config files are required for apps defined in make which depend on the lib. - // TODO(b/158843648): The config for apps should be generated as well regardless of setting. - if (d.isApp || d.isTest) && d.dexpreoptDisabled(ctx) { + if d.isTest && d.dexpreoptDisabled(ctx) { return } diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index 03769fab7..19c65cac7 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -502,32 +502,72 @@ func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, srcBootDexJars } } -// buildBootImage takes a bootImageConfig, and creates rules to build it. -func buildBootImage(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) { - var zipFiles android.Paths +// buildBootImageVariantsForAndroidOs generates rules to build the boot image variants for the +// android.Android OsType and returns a map from the architectures to the paths of the generated +// boot image files. +// +// The paths are returned because they are needed elsewhere in Soong, e.g. for populating an APEX. +func buildBootImageVariantsForAndroidOs(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) bootImageFilesByArch { + return buildBootImageForOsType(ctx, image, profile, android.Android) +} + +// buildBootImageVariantsForBuildOs generates rules to build the boot image variants for the +// android.BuildOs OsType, i.e. the type of OS on which the build is being running. +// +// The files need to be generated into their predefined location because they are used from there +// both within Soong and outside, e.g. for ART based host side testing and also for use by some +// cloud based tools. However, they are not needed by callers of this function and so the paths do +// not need to be returned from this func, unlike the buildBootImageVariantsForAndroidOs func. +func buildBootImageVariantsForBuildOs(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) { + buildBootImageForOsType(ctx, image, profile, android.BuildOs) +} + +// buildBootImageForOsType takes a bootImageConfig, a profile file and an android.OsType +// boot image files are required for and it creates rules to build the boot image +// files for all the required architectures for them. +// +// It returns a map from android.ArchType to the predefined paths of the boot image files. +func buildBootImageForOsType(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath, requiredOsType android.OsType) bootImageFilesByArch { + filesByArch := bootImageFilesByArch{} for _, variant := range image.variants { - files := buildBootImageVariant(ctx, variant, profile) - if variant.target.Os == android.Android { - zipFiles = append(zipFiles, files.Paths()...) + if variant.target.Os == requiredOsType { + buildBootImageVariant(ctx, variant, profile) + filesByArch[variant.target.Arch.ArchType] = variant.imagesDeps.Paths() } } - if image.zip != nil { - rule := android.NewRuleBuilder(pctx, ctx) - rule.Command(). - BuiltTool("soong_zip"). - FlagWithOutput("-o ", image.zip). - FlagWithArg("-C ", image.dir.Join(ctx, android.Android.String()).String()). - FlagWithInputList("-f ", zipFiles, " -f ") + return filesByArch +} - rule.Build("zip_"+image.name, "zip "+image.name+" image") +// buildBootImageZipInPredefinedLocation generates a zip file containing all the boot image files. +// +// The supplied filesByArch is nil when the boot image files have not been generated. Otherwise, it +// is a map from android.ArchType to the predefined locations. +func buildBootImageZipInPredefinedLocation(ctx android.ModuleContext, image *bootImageConfig, filesByArch bootImageFilesByArch) { + if filesByArch == nil { + return + } + + // Compute the list of files from all the architectures. + zipFiles := android.Paths{} + for _, archType := range android.ArchTypeList() { + zipFiles = append(zipFiles, filesByArch[archType]...) } + + rule := android.NewRuleBuilder(pctx, ctx) + rule.Command(). + BuiltTool("soong_zip"). + FlagWithOutput("-o ", image.zip). + FlagWithArg("-C ", image.dir.Join(ctx, android.Android.String()).String()). + FlagWithInputList("-f ", zipFiles, " -f ") + + rule.Build("zip_"+image.name, "zip "+image.name+" image") } // Generate boot image build rules for a specific target. -func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) android.WritablePaths { +func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) { - globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx) + globalSoong := dexpreopt.GetGlobalSoongConfig(ctx) global := dexpreopt.GetGlobalConfig(ctx) arch := image.target.Arch.ArchType @@ -641,11 +681,8 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p var vdexInstalls android.RuleBuilderInstalls var unstrippedInstalls android.RuleBuilderInstalls - var zipFiles android.WritablePaths - for _, artOrOat := range image.moduleFiles(ctx, outputDir, ".art", ".oat") { cmd.ImplicitOutput(artOrOat) - zipFiles = append(zipFiles, artOrOat) // Install the .oat and .art files rule.Install(artOrOat, filepath.Join(installDir, artOrOat.Base())) @@ -653,7 +690,6 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p for _, vdex := range image.moduleFiles(ctx, outputDir, ".vdex") { cmd.ImplicitOutput(vdex) - zipFiles = append(zipFiles, vdex) // Note that the vdex files are identical between architectures. // Make rules will create symlinks to share them between architectures. @@ -675,8 +711,6 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p image.installs = rule.Installs() image.vdexInstalls = vdexInstalls image.unstrippedInstalls = unstrippedInstalls - - return zipFiles } const failureMessage = `ERROR: Dex2oat failed to compile a boot image. @@ -684,7 +718,7 @@ It is likely that the boot classpath is inconsistent. Rebuild with ART_BOOT_IMAGE_EXTRA_ARGS="--runtime-arg -verbose:verifier" to see verification errors.` func bootImageProfileRule(ctx android.ModuleContext, image *bootImageConfig) android.WritablePath { - globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx) + globalSoong := dexpreopt.GetGlobalSoongConfig(ctx) global := dexpreopt.GetGlobalConfig(ctx) if global.DisableGenerateProfile { diff --git a/java/droidstubs.go b/java/droidstubs.go index d348b5556..ec1b04a06 100644 --- a/java/droidstubs.go +++ b/java/droidstubs.go @@ -128,12 +128,15 @@ type DroidstubsProperties struct { // whicih can be used for scheduling purposes High_mem *bool - // is set to true, Metalava will allow framework SDK to contain API levels annotations. + // if set to true, Metalava will allow framework SDK to contain API levels annotations. Api_levels_annotations_enabled *bool // the dirs which Metalava extracts API levels annotations from. Api_levels_annotations_dirs []string + // the sdk kind which Metalava extracts API levels annotations from. Supports 'public' and 'system' for now; defaults to public. + Api_levels_sdk_type *string + // the filename which Metalava extracts API levels annotations from. Defaults to android.jar. Api_levels_jar_filename *string @@ -367,6 +370,7 @@ func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *a filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar") + var dirs []string ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) { if t, ok := m.(*ExportedDroiddocDir); ok { for _, dep := range t.deps { @@ -383,12 +387,32 @@ func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *a cmd.Implicit(dep) } } - cmd.FlagWithArg("--android-jar-pattern ", t.dir.String()+"/%/public/"+filename) + + dirs = append(dirs, t.dir.String()) } else { ctx.PropertyErrorf("api_levels_annotations_dirs", "module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m)) } }) + + // Add all relevant --android-jar-pattern patterns for Metalava. + // When parsing a stub jar for a specific version, Metalava picks the first pattern that defines + // an actual file present on disk (in the order the patterns were passed). For system APIs for + // privileged apps that are only defined since API level 21 (Lollipop), fallback to public stubs + // for older releases. + if sdkType := proptools.StringDefault(d.properties.Api_levels_sdk_type, "public"); sdkType != "public" { + if sdkType != "system" { + ctx.PropertyErrorf("api_levels_sdk_type", "only 'public' and 'system' are supported") + } + // If building non public stubs, add all sdkType patterns first... + for _, dir := range dirs { + cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkType, filename)) + } + } + for _, dir := range dirs { + // ... and fallback to public ones, for Metalava to use if needed. + cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, "public", filename)) + } } func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths, diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go index db664c15e..60d0bea5d 100644 --- a/java/droidstubs_test.go +++ b/java/droidstubs_test.go @@ -16,6 +16,7 @@ package java import ( "reflect" + "regexp" "strings" "testing" @@ -81,6 +82,46 @@ func TestDroidstubs(t *testing.T) { } } +func TestSystemDroidstubs(t *testing.T) { + ctx, _ := testJavaWithFS(t, ` + droiddoc_exported_dir { + name: "some-exported-dir", + path: "somedir", + } + + droiddoc_exported_dir { + name: "some-other-exported-dir", + path: "someotherdir", + } + + droidstubs { + name: "foo-stubs", + srcs: ["foo-doc/a.java"], + api_levels_annotations_dirs: [ + "some-exported-dir", + "some-other-exported-dir", + ], + api_levels_annotations_enabled: true, + api_levels_sdk_type: "system", + } + `, + map[string][]byte{ + "foo-doc/a.java": nil, + }) + + m := ctx.ModuleForTests("foo-stubs", "android_common") + manifest := m.Output("metalava.sbox.textproto") + cmd := String(android.RuleBuilderSboxProtoForTests(t, manifest).Commands[0].Command) + r := regexp.MustCompile(`--android-jar-pattern [^ ]+/android.jar`) + matches := r.FindAllString(cmd, -1) + android.AssertArrayString(t, "order of patterns", []string{ + "--android-jar-pattern somedir/%/system/android.jar", + "--android-jar-pattern someotherdir/%/system/android.jar", + "--android-jar-pattern somedir/%/public/android.jar", + "--android-jar-pattern someotherdir/%/public/android.jar", + }, matches) +} + func TestDroidstubsSandbox(t *testing.T) { ctx, _ := testJavaWithFS(t, ` genrule { diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go index af124842c..c4832d2f8 100644 --- a/java/hiddenapi_modular.go +++ b/java/hiddenapi_modular.go @@ -1161,6 +1161,14 @@ func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.M return true } + // A bootclasspath module that is part of a versioned sdk never provides a boot dex jar as there + // is no equivalently versioned prebuilt APEX file from which it can be obtained. However, + // versioned bootclasspath modules are processed by Soong so in order to avoid them causing build + // failures missing boot dex jars need to be deferred. + if android.IsModuleInVersionedSdk(ctx.Module()) { + return true + } + // This is called for both platform_bootclasspath and bootclasspath_fragment modules. // // A bootclasspath_fragment module should only use the APEX variant of source or prebuilt modules. @@ -1197,6 +1205,14 @@ func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.M // // TODO(b/187910671): Remove this once platform variants are no longer created unnecessarily. if android.IsModulePrebuilt(module) { + // An inactive source module can still contribute to the APEX but an inactive prebuilt module + // should not contribute to anything. So, rather than have a missing dex jar cause a Soong + // failure defer the error reporting to Ninja. Unless the prebuilt build target is explicitly + // built Ninja should never use the dex jar file. + if !isActiveModule(module) { + return true + } + if am, ok := module.(android.ApexModule); ok && am.InAnyApex() { apexInfo := ctx.OtherModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo) if apexInfo.IsForPlatform() { @@ -1205,14 +1221,6 @@ func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.M } } - // A bootclasspath module that is part of a versioned sdk never provides a boot dex jar as there - // is no equivalently versioned prebuilt APEX file from which it can be obtained. However, - // versioned bootclasspath modules are processed by Soong so in order to avoid them causing build - // failures missing boot dex jars need to be deferred. - if android.IsModuleInVersionedSdk(ctx.Module()) { - return true - } - return false } diff --git a/java/java.go b/java/java.go index 0770f7ffb..e74185ec5 100644 --- a/java/java.go +++ b/java/java.go @@ -1165,7 +1165,8 @@ type Import struct { properties ImportProperties // output file containing classes.dex and resources - dexJarFile android.Path + dexJarFile android.Path + dexJarInstallFile android.Path combinedClasspathFile android.Path classLoaderContexts dexpreopt.ClassLoaderContextMap @@ -1310,6 +1311,7 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo) if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(j.BaseModuleName())); dexOutputPath != nil { j.dexJarFile = dexOutputPath + j.dexJarInstallFile = android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(j.BaseModuleName())) // Initialize the hiddenapi structure. j.initHiddenAPI(ctx, dexOutputPath, outputFile, nil) @@ -1350,6 +1352,7 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { dexOutputFile = j.hiddenAPIEncodeDex(ctx, dexOutputFile) j.dexJarFile = dexOutputFile + j.dexJarInstallFile = android.PathForModuleInstall(ctx, "framework", jarName) } } @@ -1391,7 +1394,7 @@ func (j *Import) DexJarBuildPath() android.Path { } func (j *Import) DexJarInstallPath() android.Path { - return nil + return j.dexJarInstallFile } func (j *Import) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap { diff --git a/java/lint.go b/java/lint.go index dd5e4fb13..fe3218e90 100644 --- a/java/lint.go +++ b/java/lint.go @@ -391,8 +391,9 @@ func (l *linter) lint(ctx android.ModuleContext) { rule.Command().Text("rm -f").Output(html).Output(text).Output(xml) var apiVersionsName, apiVersionsPrebuilt string - if l.compileSdkKind == android.SdkModule { - // When compiling an SDK module we use the filtered database because otherwise lint's + if l.compileSdkKind == android.SdkModule || l.compileSdkKind == android.SdkSystemServer { + // When compiling an SDK module (or system server) we use the filtered + // database because otherwise lint's // NewApi check produces too many false positives; This database excludes information // about classes created in mainline modules hence removing those false positives. apiVersionsName = "api_versions_public_filtered.xml" diff --git a/java/lint_test.go b/java/lint_test.go index 9cf1c33fe..456e6ba73 100644 --- a/java/lint_test.go +++ b/java/lint_test.go @@ -261,6 +261,9 @@ func TestJavaLintDatabaseSelectionFull(t *testing.T) { } func TestJavaLintDatabaseSelectionPublicFiltered(t *testing.T) { + testCases := []string{ + "module_current", "system_server_current", + } bp := ` java_library { name: "foo", @@ -274,17 +277,20 @@ func TestJavaLintDatabaseSelectionPublicFiltered(t *testing.T) { }, } ` - result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules). - RunTestWithBp(t, bp) + for _, testCase := range testCases { + thisBp := strings.Replace(bp, "XXX", testCase, 1) + result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules). + RunTestWithBp(t, thisBp) - foo := result.ModuleForTests("foo", "android_common") - sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto")) - if !strings.Contains(*sboxProto.Commands[0].Command, - "/api_versions_public_filtered.xml") { - t.Error("did not use public-filtered lint api database", *sboxProto.Commands[0].Command) - } - if strings.Contains(*sboxProto.Commands[0].Command, - "/api_versions.xml") { - t.Error("used full api database") + foo := result.ModuleForTests("foo", "android_common") + sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto")) + if !strings.Contains(*sboxProto.Commands[0].Command, + "/api_versions_public_filtered.xml") { + t.Error("did not use public-filtered lint api database for case", testCase) + } + if strings.Contains(*sboxProto.Commands[0].Command, + "/api_versions.xml") { + t.Error("used full api database for case", testCase) + } } } diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index d72cee0c3..c1e14b21e 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -437,7 +437,15 @@ func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android. // Build a profile for the image config and then use that to build the boot image. profile := bootImageProfileRule(ctx, imageConfig) - buildBootImage(ctx, imageConfig, profile) + + // Build boot image files for the android variants. + androidBootImageFilesByArch := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile) + + // Zip the android variant boot image files up. + buildBootImageZipInPredefinedLocation(ctx, imageConfig, androidBootImageFilesByArch) + + // Build boot image files for the host variants. There are use directly by ART host side tests. + buildBootImageVariantsForBuildOs(ctx, imageConfig, profile) dumpOatRules(ctx, imageConfig) } diff --git a/java/sdk_library.go b/java/sdk_library.go index ed9aeffd4..2b7185759 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -1923,8 +1923,12 @@ type SdkLibraryImport struct { // Is nil if the source module does not exist. xmlPermissionsFileModule *sdkLibraryXml - // Path to the dex implementation jar obtained from the prebuilt_apex, if any. + // Build path to the dex implementation jar obtained from the prebuilt_apex, if any. dexJarFile android.Path + + // Expected install file path of the source module(sdk_library) + // or dex implementation jar obtained from the prebuilt_apex, if any. + installFile android.Path } var _ SdkLibraryDependency = (*SdkLibraryImport)(nil) @@ -2136,6 +2140,9 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo var deapexerModule android.Module + // Assume that source module(sdk_library) is installed in /<sdk_library partition>/framework + module.installFile = android.PathForModuleInstall(ctx, "framework", module.Stem()+".jar") + // Record the paths to the prebuilt stubs library and stubs source. ctx.VisitDirectDeps(func(to android.Module) { tag := ctx.OtherModuleDependencyTag(to) @@ -2195,6 +2202,7 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo) if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(module.BaseModuleName())); dexOutputPath != nil { module.dexJarFile = dexOutputPath + module.installFile = android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(module.BaseModuleName())) module.initHiddenAPI(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil) } else { // This should never happen as a variant for a prebuilt_apex is only created if the @@ -2249,11 +2257,7 @@ func (module *SdkLibraryImport) DexJarBuildPath() android.Path { // to satisfy UsesLibraryDependency interface func (module *SdkLibraryImport) DexJarInstallPath() android.Path { - if module.implLibraryModule == nil { - return nil - } else { - return module.implLibraryModule.DexJarInstallPath() - } + return module.installFile } // to satisfy UsesLibraryDependency interface diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go index 252c6151f..28a5a2cf2 100644 --- a/java/systemserver_classpath_fragment.go +++ b/java/systemserver_classpath_fragment.go @@ -106,21 +106,12 @@ func (s *SystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.Mo func (s *SystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList { global := dexpreopt.GetGlobalConfig(ctx) - // Convert content names to their appropriate stems, in case a test library is overriding an actual boot jar - var stems []string - for _, name := range s.properties.Contents { - dep := ctx.GetDirectDepWithTag(name, systemServerClasspathFragmentContentDepTag) - if m, ok := dep.(ModuleWithStem); ok { - stems = append(stems, m.Stem()) - } else { - ctx.PropertyErrorf("contents", "%v is not a ModuleWithStem", name) - } - } + possibleUpdatableModules := gatherPossibleUpdatableModuleNamesAndStems(ctx, s.properties.Contents, systemServerClasspathFragmentContentDepTag) // Only create configs for updatable boot jars. Non-updatable system server jars must be part of the // platform_systemserverclasspath's classpath proto config to guarantee that they come before any // updatable jars at runtime. - return global.UpdatableSystemServerJars.Filter(stems) + return global.UpdatableSystemServerJars.Filter(possibleUpdatableModules) } type systemServerClasspathFragmentContentDependencyTag struct { diff --git a/mk2rbc/android_products.go b/mk2rbc/android_products.go new file mode 100644 index 000000000..de38391fc --- /dev/null +++ b/mk2rbc/android_products.go @@ -0,0 +1,110 @@ +// Copyright 2021 Google LLC +// +// 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 mk2rbc + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + mkparser "android/soong/androidmk/parser" +) + +// Implements mkparser.Scope, to be used by mkparser.Value.Value() +type localDirEval struct { + localDir string + hasErrors bool +} + +func (l *localDirEval) Get(name string) string { + if name == "LOCAL_DIR" { + return l.localDir + } + l.hasErrors = true + return fmt.Sprintf("$(%s)", name) +} + +func (l *localDirEval) Set(_, _ string) { +} + +func (l *localDirEval) Call(_ string, _ []string) []string { + l.hasErrors = true + return []string{"$(call ...)"} +} + +func (l *localDirEval) SetFunc(_ string, _ func([]string) []string) { +} + +// UpdateProductConfigMap builds product configuration map. +// The product configuration map maps a product name (i.e., the value of the +// TARGET_PRODUCT variable) to the top-level configuration file. +// In the Android's Make-based build machinery, the equivalent of the +// product configuration map is $(PRODUCT_MAKEFILES), which is the list +// of <product>:<configuration makefile> pairs (if <product>: is missing, +// <product> is the basename of the configuration makefile). +// UpdateProductConfigMap emulates this build logic by processing the +// assignments to PRODUCT_MAKEFILES in the file passed to it. +func UpdateProductConfigMap(configMap map[string]string, configMakefile string) error { + contents, err := ioutil.ReadFile(configMakefile) + if err != nil { + return err + } + parser := mkparser.NewParser(configMakefile, bytes.NewBuffer(contents)) + nodes, errs := parser.Parse() + if len(errs) > 0 { + for _, e := range errs { + fmt.Fprintln(os.Stderr, "ERROR:", e) + } + return fmt.Errorf("cannot parse %s", configMakefile) + } + + ldEval := &localDirEval{localDir: filepath.Dir(configMakefile)} + + for _, node := range nodes { + // We are interested in assignments to 'PRODUCT_MAKEFILES' + asgn, ok := node.(*mkparser.Assignment) + if !ok { + continue + } + if !(asgn.Name.Const() && asgn.Name.Strings[0] == "PRODUCT_MAKEFILES") { + continue + } + + // Resolve the references to $(LOCAL_DIR) in $(PRODUCT_MAKEFILES). + ldEval.hasErrors = false + value := asgn.Value.Value(ldEval) + if ldEval.hasErrors { + return fmt.Errorf("cannot evaluate %s", asgn.Value.Dump()) + } + // Each item is either <product>:<configuration makefile>, or + // just <configuration makefile> + for _, token := range strings.Fields(value) { + var product, config_path string + if n := strings.Index(token, ":"); n >= 0 { + product = token[0:n] + config_path = token[n+1:] + } else { + config_path = token + product = filepath.Base(config_path) + product = strings.TrimSuffix(product, filepath.Ext(product)) + } + configMap[product] = config_path + } + } + return nil +} diff --git a/mk2rbc/android_products_test.go b/mk2rbc/android_products_test.go new file mode 100644 index 000000000..f8c930aeb --- /dev/null +++ b/mk2rbc/android_products_test.go @@ -0,0 +1,38 @@ +// Copyright 2021 Google LLC +// +// 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 mk2rbc + +import ( + "path/filepath" + "reflect" + "testing" +) + +func TestProductsMakefile(t *testing.T) { + testDir := getTestDirectory() + abspath := func(relPath string) string { return filepath.Join(testDir, relPath) } + actualProducts := make(map[string]string) + if err := UpdateProductConfigMap(actualProducts, abspath("android_products.mk.test")); err != nil { + t.Fatal(err) + } + expectedProducts := map[string]string{ + "aosp_cf_x86_tv": abspath("vsoc_x86/tv/device.mk"), + "aosp_tv_arm": abspath("aosp_tv_arm.mk"), + "aosp_tv_arm64": abspath("aosp_tv_arm64.mk"), + } + if !reflect.DeepEqual(actualProducts, expectedProducts) { + t.Errorf("\nExpected: %v\n Actual: %v", expectedProducts, actualProducts) + } +} diff --git a/mk2rbc/config_variables.go b/mk2rbc/config_variables.go new file mode 100644 index 000000000..dac509c93 --- /dev/null +++ b/mk2rbc/config_variables.go @@ -0,0 +1,67 @@ +// Copyright 2021 Google LLC +// +// 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 mk2rbc + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "strings" + + mkparser "android/soong/androidmk/parser" +) + +// Extracts the list of product config variables from a file, calling +// given registrar for each variable. +func FindConfigVariables(mkFile string, vr variableRegistrar) error { + mkContents, err := ioutil.ReadFile(mkFile) + if err != nil { + return err + } + parser := mkparser.NewParser(mkFile, bytes.NewBuffer(mkContents)) + nodes, errs := parser.Parse() + if len(errs) > 0 { + for _, e := range errs { + fmt.Fprintln(os.Stderr, "ERROR:", e) + } + return fmt.Errorf("cannot parse %s", mkFile) + } + for _, node := range nodes { + asgn, ok := node.(*mkparser.Assignment) + if !ok { + continue + } + // We are looking for a variable called '_product_list_vars' + // or '_product_single_value_vars'. + if !asgn.Name.Const() { + continue + } + varName := asgn.Name.Strings[0] + var starType starlarkType + if varName == "_product_list_vars" { + starType = starlarkTypeList + } else if varName == "_product_single_value_vars" { + starType = starlarkTypeUnknown + } else { + continue + } + for _, name := range strings.Fields(asgn.Value.Dump()) { + vr.NewVariable(name, VarClassConfig, starType) + } + + } + return nil +} diff --git a/mk2rbc/config_variables_test.go b/mk2rbc/config_variables_test.go new file mode 100644 index 000000000..f5a518049 --- /dev/null +++ b/mk2rbc/config_variables_test.go @@ -0,0 +1,60 @@ +// Copyright 2021 Google LLC +// +// 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 mk2rbc + +import ( + "path/filepath" + "reflect" + "runtime" + "testing" +) + +type testVar struct { + name string + cl varClass + ty starlarkType +} + +type testVariables struct { + v []testVar +} + +func (v *testVariables) NewVariable(name string, varClass varClass, valueType starlarkType) { + v.v = append(v.v, testVar{name, varClass, valueType}) +} + +// getTestDirectory returns the test directory, which should be the test/ subdirectory +func getTestDirectory() string { + _, myFile, _, _ := runtime.Caller(1) + return filepath.Join(filepath.Dir(myFile), "test") +} + +func TestConfigVariables(t *testing.T) { + testFile := filepath.Join(getTestDirectory(), "config_variables.mk.test") + var actual testVariables + if err := FindConfigVariables(testFile, &actual); err != nil { + t.Fatal(err) + } + expected := testVariables{[]testVar{ + {"PRODUCT_NAME", VarClassConfig, starlarkTypeUnknown}, + {"PRODUCT_MODEL", VarClassConfig, starlarkTypeUnknown}, + {"PRODUCT_LOCALES", VarClassConfig, starlarkTypeList}, + {"PRODUCT_AAPT_CONFIG", VarClassConfig, starlarkTypeList}, + {"PRODUCT_AAPT_PREF_CONFIG", VarClassConfig, starlarkTypeUnknown}, + }} + if !reflect.DeepEqual(expected, actual) { + t.Errorf("\nExpected: %v\n Actual: %v", expected, actual) + } +} diff --git a/mk2rbc/soong_variables.go b/mk2rbc/soong_variables.go new file mode 100644 index 000000000..de4692528 --- /dev/null +++ b/mk2rbc/soong_variables.go @@ -0,0 +1,151 @@ +// Copyright 2021 Google LLC +// +// 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 mk2rbc + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "regexp" + "strings" + + mkparser "android/soong/androidmk/parser" +) + +type context struct { + includeFileScope mkparser.Scope + registrar variableRegistrar +} + +// Scans the makefile Soong uses to generate soong.variables file, +// collecting variable names and types from the lines that look like this: +// $(call add_json_XXX, <...>, $(VAR)) +// +func FindSoongVariables(mkFile string, includeFileScope mkparser.Scope, registrar variableRegistrar) error { + ctx := context{includeFileScope, registrar} + return ctx.doFind(mkFile) +} + +func (ctx *context) doFind(mkFile string) error { + mkContents, err := ioutil.ReadFile(mkFile) + if err != nil { + return err + } + parser := mkparser.NewParser(mkFile, bytes.NewBuffer(mkContents)) + nodes, errs := parser.Parse() + if len(errs) > 0 { + for _, e := range errs { + fmt.Fprintln(os.Stderr, "ERROR:", e) + } + return fmt.Errorf("cannot parse %s", mkFile) + } + for _, node := range nodes { + switch t := node.(type) { + case *mkparser.Variable: + ctx.handleVariable(t) + case *mkparser.Directive: + ctx.handleInclude(t) + } + } + return nil +} + +func (ctx context) NewSoongVariable(name, typeString string) { + var valueType starlarkType + switch typeString { + case "bool": + valueType = starlarkTypeBool + case "csv": + // Only PLATFORM_VERSION_ALL_CODENAMES, and it's a list + valueType = starlarkTypeList + case "list": + valueType = starlarkTypeList + case "str": + valueType = starlarkTypeString + case "val": + // Only PLATFORM_SDK_VERSION uses this, and it's integer + valueType = starlarkTypeInt + default: + panic(fmt.Errorf("unknown Soong variable type %s", typeString)) + } + + ctx.registrar.NewVariable(name, VarClassSoong, valueType) +} + +func (ctx context) handleInclude(t *mkparser.Directive) { + if t.Name != "include" && t.Name != "-include" { + return + } + includedPath := t.Args.Value(ctx.includeFileScope) + err := ctx.doFind(includedPath) + if err != nil && t.Name == "include" { + fmt.Fprintf(os.Stderr, "cannot include %s: %s", includedPath, err) + } +} + +var callFuncRex = regexp.MustCompile("^call +add_json_(str|val|bool|csv|list) *,") + +func (ctx context) handleVariable(t *mkparser.Variable) { + // From the variable reference looking as follows: + // $(call json_add_TYPE,arg1,$(VAR)) + // we infer that the type of $(VAR) is TYPE + // VAR can be a simple variable name, or another call + // (e.g., $(call invert_bool, $(X)), from which we can infer + // that the type of X is bool + if prefix, v, ok := prefixedVariable(t.Name); ok && strings.HasPrefix(prefix, "call add_json") { + if match := callFuncRex.FindStringSubmatch(prefix); match != nil { + ctx.inferSoongVariableType(match[1], v) + // NOTE(asmundak): sometimes arg1 (the name of the Soong variable defined + // in this statement) may indicate that there is a Make counterpart. E.g, from + // $(call add_json_bool, DisablePreopt, $(call invert_bool,$(ENABLE_PREOPT))) + // it may be inferred that there is a Make boolean variable DISABLE_PREOPT. + // Unfortunately, Soong variable names have no 1:1 correspondence to Make variables, + // for instance, + // $(call add_json_list, PatternsOnSystemOther, $(SYSTEM_OTHER_ODEX_FILTER)) + // does not mean that there is PATTERNS_ON_SYSTEM_OTHER + // Our main interest lies in finding the variables whose values are lists, and + // so far there are none that can be found this way, so it is not important. + } else { + panic(fmt.Errorf("cannot match the call: %s", prefix)) + } + } +} + +var ( + callInvertBoolRex = regexp.MustCompile("^call +invert_bool *, *$") + callFilterBoolRex = regexp.MustCompile("^(filter|filter-out) +(true|false), *$") +) + +func (ctx context) inferSoongVariableType(vType string, n *mkparser.MakeString) { + if n.Const() { + ctx.NewSoongVariable(n.Strings[0], vType) + return + } + if prefix, v, ok := prefixedVariable(n); ok { + if callInvertBoolRex.MatchString(prefix) || callFilterBoolRex.MatchString(prefix) { + // It is $(call invert_bool, $(VAR)) or $(filter[-out] [false|true],$(VAR)) + ctx.inferSoongVariableType("bool", v) + } + } +} + +// If MakeString is foo$(BAR), returns 'foo', BAR(as *MakeString) and true +func prefixedVariable(s *mkparser.MakeString) (string, *mkparser.MakeString, bool) { + if len(s.Strings) != 2 || s.Strings[1] != "" { + return "", nil, false + } + return s.Strings[0], s.Variables[0].Name, true +} diff --git a/mk2rbc/soong_variables_test.go b/mk2rbc/soong_variables_test.go new file mode 100644 index 000000000..c883882c4 --- /dev/null +++ b/mk2rbc/soong_variables_test.go @@ -0,0 +1,51 @@ +// Copyright 2021 Google LLC +// +// 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 mk2rbc + +import ( + "fmt" + "path/filepath" + "reflect" + "testing" +) + +type dirResolverForTest struct { + ScopeBase +} + +func (t dirResolverForTest) Get(name string) string { + if name != "BUILD_SYSTEM" { + return fmt.Sprintf("$(%s)", name) + } + return getTestDirectory() +} + +func TestSoongVariables(t *testing.T) { + testFile := filepath.Join(getTestDirectory(), "soong_variables.mk.test") + var actual testVariables + if err := FindSoongVariables(testFile, dirResolverForTest{}, &actual); err != nil { + t.Fatal(err) + } + expected := testVariables{[]testVar{ + {"BUILD_ID", VarClassSoong, starlarkTypeString}, + {"PLATFORM_SDK_VERSION", VarClassSoong, starlarkTypeInt}, + {"DEVICE_PACKAGE_OVERLAYS", VarClassSoong, starlarkTypeList}, + {"ENABLE_CFI", VarClassSoong, starlarkTypeBool}, + {"ENABLE_PREOPT", VarClassSoong, starlarkTypeBool}, + }} + if !reflect.DeepEqual(expected, actual) { + t.Errorf("\nExpected: %v\n Actual: %v", expected, actual) + } +} diff --git a/mk2rbc/test/android_products.mk.test b/mk2rbc/test/android_products.mk.test new file mode 100644 index 000000000..a2220edac --- /dev/null +++ b/mk2rbc/test/android_products.mk.test @@ -0,0 +1,4 @@ +PRODUCT_MAKEFILES := \ + $(LOCAL_DIR)/aosp_tv_arm.mk \ + $(LOCAL_DIR)/aosp_tv_arm64.mk \ + aosp_cf_x86_tv:$(LOCAL_DIR)/vsoc_x86/tv/device.mk
\ No newline at end of file diff --git a/mk2rbc/test/config_variables.mk.test b/mk2rbc/test/config_variables.mk.test new file mode 100644 index 000000000..e5cd0e928 --- /dev/null +++ b/mk2rbc/test/config_variables.mk.test @@ -0,0 +1,12 @@ +_product_single_value_vars := + +# Variables that are lists of values. +_product_list_vars := + +_product_single_value_vars += PRODUCT_NAME +_product_single_value_vars += PRODUCT_MODEL + +# The resoure configuration options to use for this product. +_product_list_vars += PRODUCT_LOCALES +_product_list_vars += PRODUCT_AAPT_CONFIG +_product_single_value_vars += PRODUCT_AAPT_PREF_CONFIG diff --git a/mk2rbc/test/soong_included.mk.test b/mk2rbc/test/soong_included.mk.test new file mode 100644 index 000000000..255ecc59d --- /dev/null +++ b/mk2rbc/test/soong_included.mk.test @@ -0,0 +1 @@ + $(call add_json_bool, DisablePreopt, $(call invert_bool,$(ENABLE_PREOPT))) diff --git a/mk2rbc/test/soong_variables.mk.test b/mk2rbc/test/soong_variables.mk.test new file mode 100644 index 000000000..ca60c9c37 --- /dev/null +++ b/mk2rbc/test/soong_variables.mk.test @@ -0,0 +1,5 @@ +$(call add_json_str, BuildId, $(BUILD_ID)) +$(call add_json_val, Platform_sdk_version, $(PLATFORM_SDK_VERSION)) +$(call add_json_list, DeviceResourceOverlays, $(DEVICE_PACKAGE_OVERLAYS)) +$(call add_json_bool, EnableCFI, $(call invert_bool,$(filter false,$(ENABLE_CFI)))) +include $(BUILD_SYSTEM)/soong_included.mk.test diff --git a/mk2rbc/types.go b/mk2rbc/types.go new file mode 100644 index 000000000..22c8b58f7 --- /dev/null +++ b/mk2rbc/types.go @@ -0,0 +1,55 @@ +// Copyright 2021 Google LLC +// +// 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 mk2rbc + +// Starlark expression types we use +type starlarkType int + +const ( + starlarkTypeUnknown starlarkType = iota + starlarkTypeList starlarkType = iota + starlarkTypeString starlarkType = iota + starlarkTypeInt starlarkType = iota + starlarkTypeBool starlarkType = iota + starlarkTypeVoid starlarkType = iota +) + +type varClass int + +const ( + VarClassConfig varClass = iota + VarClassSoong varClass = iota + VarClassLocal varClass = iota +) + +type variableRegistrar interface { + NewVariable(name string, varClass varClass, valueType starlarkType) +} + +// ScopeBase is a dummy implementation of the mkparser.Scope. +// All our scopes are read-only and resolve only simple variables. +type ScopeBase struct{} + +func (s ScopeBase) Set(_, _ string) { + panic("implement me") +} + +func (s ScopeBase) Call(_ string, _ []string) []string { + panic("implement me") +} + +func (s ScopeBase) SetFunc(_ string, _ func([]string) []string) { + panic("implement me") +} diff --git a/rust/binary.go b/rust/binary.go index 8d0a0a7a0..2c3f54835 100644 --- a/rust/binary.go +++ b/rust/binary.go @@ -155,7 +155,3 @@ func (binary *binaryDecorator) stdLinkage(ctx *depsContext) RustLinkage { } return binary.baseCompiler.stdLinkage(ctx) } - -func (binary *binaryDecorator) isDependencyRoot() bool { - return true -} diff --git a/rust/compiler.go b/rust/compiler.go index 1598ebf9a..0b28135ae 100644 --- a/rust/compiler.go +++ b/rust/compiler.go @@ -289,10 +289,6 @@ func (compiler *baseCompiler) CargoOutDir() android.OptionalPath { return android.OptionalPathForPath(compiler.cargoOutDir) } -func (compiler *baseCompiler) isDependencyRoot() bool { - return false -} - func (compiler *baseCompiler) strippedOutputFilePath() android.OptionalPath { return compiler.strippedOutputFile } diff --git a/rust/rust.go b/rust/rust.go index 05f639939..38f1742d4 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -87,6 +87,7 @@ type BaseProperties struct { // Used by vendor snapshot to record dependencies from snapshot modules. SnapshotSharedLibs []string `blueprint:"mutated"` + SnapshotStaticLibs []string `blueprint:"mutated"` // Make this module available when building for vendor ramdisk. // On device without a dedicated recovery partition, the module is only @@ -171,13 +172,6 @@ func (mod *Module) SanitizePropDefined() bool { return mod.sanitize != nil && mod.compiler != nil } -func (mod *Module) IsDependencyRoot() bool { - if mod.compiler != nil { - return mod.compiler.isDependencyRoot() - } - panic("IsDependencyRoot called on a non-compiler Rust module") -} - func (mod *Module) IsPrebuilt() bool { if _, ok := mod.compiler.(*prebuiltLibraryDecorator); ok { return true @@ -259,6 +253,13 @@ func (mod *Module) Binary() bool { return false } +func (mod *Module) StaticExecutable() bool { + if !mod.Binary() { + return false + } + return Bool(mod.compiler.(*binaryDecorator).Properties.Static_executable) +} + func (mod *Module) Object() bool { // Rust has no modules which produce only object files. return false @@ -441,7 +442,6 @@ type compiler interface { SetDisabled() stdLinkage(ctx *depsContext) RustLinkage - isDependencyRoot() bool strippedOutputFilePath() android.OptionalPath } @@ -990,7 +990,9 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { case procMacroDepTag: directProcMacroDeps = append(directProcMacroDeps, rustDep) mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName) - case android.SourceDepTag: + } + + if android.IsSourceDepTagWithOutputTag(depTag, "") { // Since these deps are added in path_properties.go via AddDependencies, we need to ensure the correct // OS/Arch variant is used. var helper string @@ -1097,6 +1099,7 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { // Record baseLibName for snapshots. mod.Properties.SnapshotSharedLibs = append(mod.Properties.SnapshotSharedLibs, cc.BaseLibName(depName)) + mod.Properties.SnapshotStaticLibs = append(mod.Properties.SnapshotStaticLibs, cc.BaseLibName(depName)) mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, makeLibName) exportDep = true @@ -1119,8 +1122,7 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { } if srcDep, ok := dep.(android.SourceFileProducer); ok { - switch depTag { - case android.SourceDepTag: + if android.IsSourceDepTagWithOutputTag(depTag, "") { // These are usually genrules which don't have per-target variants. directSrcDeps = append(directSrcDeps, srcDep) } diff --git a/rust/snapshot_utils.go b/rust/snapshot_utils.go index bd7ca7f68..8dabd9bf6 100644 --- a/rust/snapshot_utils.go +++ b/rust/snapshot_utils.go @@ -57,6 +57,10 @@ func (mod *Module) SnapshotSharedLibs() []string { return mod.Properties.SnapshotSharedLibs } +func (mod *Module) SnapshotStaticLibs() []string { + return mod.Properties.SnapshotStaticLibs +} + func (mod *Module) Symlinks() []string { // TODO update this to return the list of symlinks when Rust supports defining symlinks return nil diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go index 98697dcf4..60fbccf57 100644 --- a/sdk/cc_sdk_test.go +++ b/sdk/cc_sdk_test.go @@ -347,6 +347,7 @@ func TestSnapshotWithObject(t *testing.T) { cc_object { name: "crtobj", stl: "none", + default_shared_libs: [], sanitize: { never: true, }, @@ -364,6 +365,7 @@ cc_prebuilt_object { apex_available: ["//apex_available:platform"], stl: "none", compile_multilib: "both", + default_shared_libs: [], sanitize: { never: true, }, @@ -388,6 +390,7 @@ cc_prebuilt_object { apex_available: ["//apex_available:platform"], stl: "none", compile_multilib: "both", + default_shared_libs: [], sanitize: { never: true, }, diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index a13b0d7d0..97fb2485f 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -565,6 +565,49 @@ sdk_snapshot { ) }) + t.Run("SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR=module:build_from_source", func(t *testing.T) { + result := android.GroupFixturePreparers( + preparer, + android.FixtureMergeEnv(map[string]string{ + "SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR": "module:build_from_source", + }), + ).RunTest(t) + + checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip") + + CheckSnapshot(t, result, "mysdk", "", + checkAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. + +java_import { + name: "mysdk_myjavalib@current", + sdk_member_name: "myjavalib", + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + jars: ["java/myjavalib.jar"], +} + +java_import { + name: "myjavalib", + prefer: false, + use_source_config_var: { + config_namespace: "module", + var_name: "build_from_source", + }, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + jars: ["java/myjavalib.jar"], +} + +sdk_snapshot { + name: "mysdk@current", + visibility: ["//visibility:public"], + java_header_libs: ["mysdk_myjavalib@current"], +} + `), + ) + }) + t.Run("SOONG_SDK_SNAPSHOT_VERSION=unversioned", func(t *testing.T) { result := android.GroupFixturePreparers( preparer, diff --git a/sdk/update.go b/sdk/update.go index b146b62c8..6da3756be 100644 --- a/sdk/update.go +++ b/sdk/update.go @@ -35,6 +35,37 @@ import ( // By default every unversioned module in the generated snapshot has prefer: false. Building it // with SOONG_SDK_SNAPSHOT_PREFER=true will force them to use prefer: true. // +// SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR +// If set this specifies the Soong config var that can be used to control whether the prebuilt +// modules from the generated snapshot or the original source modules. Values must be a colon +// separated pair of strings, the first of which is the Soong config namespace, and the second +// is the name of the variable within that namespace. +// +// The config namespace and var name are used to set the `use_source_config_var` property. That +// in turn will cause the generated prebuilts to use the soong config variable to select whether +// source or the prebuilt is used. +// e.g. If an sdk snapshot is built using: +// m SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR=acme:build_from_source sdkextensions-sdk +// Then the resulting snapshot will include: +// use_source_config_var: { +// config_namespace: "acme", +// var_name: "build_from_source", +// } +// +// Assuming that the config variable is defined in .mk using something like: +// $(call add_soong_config_namespace,acme) +// $(call add_soong_config_var_value,acme,build_from_source,true) +// +// Then when the snapshot is unpacked in the repository it will have the following behavior: +// m droid - will use the sdkextensions-sdk prebuilts if present. Otherwise, it will use the +// sources. +// m SOONG_CONFIG_acme_build_from_source=true droid - will use the sdkextensions-sdk +// sources, if present. Otherwise, it will use the prebuilts. +// +// This is a temporary mechanism to control the prefer flags and will be removed once a more +// maintainable solution has been implemented. +// TODO(b/174997203): Remove when no longer necessary. +// // SOONG_SDK_SNAPSHOT_VERSION // This provides control over the version of the generated snapshot. // @@ -760,6 +791,8 @@ func (t unversionedToVersionedTransformation) transformModule(module *bpModule) module.insertAfter("name", "sdk_member_name", name) // Remove the prefer property if present as versioned modules never need marking with prefer. module.removeProperty("prefer") + // Ditto for use_source_config_var + module.removeProperty("use_source_config_var") return module } @@ -1627,13 +1660,24 @@ func (s *sdk) createMemberSnapshot(ctx *memberContext, member *sdkMember, bpModu // snapshot to be created that sets prefer: true. // TODO(b/174997203): Remove once the ability to select the modules to prefer can be done // dynamically at build time not at snapshot generation time. - prefer := ctx.sdkMemberContext.Config().IsEnvTrue("SOONG_SDK_SNAPSHOT_PREFER") + config := ctx.sdkMemberContext.Config() + prefer := config.IsEnvTrue("SOONG_SDK_SNAPSHOT_PREFER") // Set prefer. Setting this to false is not strictly required as that is the default but it does // provide a convenient hook to post-process the generated Android.bp file, e.g. in tests to // check the behavior when a prebuilt is preferred. It also makes it explicit what the default // behavior is for the module. bpModule.insertAfter("name", "prefer", prefer) + + configVar := config.Getenv("SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR") + if configVar != "" { + parts := strings.Split(configVar, ":") + cfp := android.ConfigVarProperties{ + Config_namespace: proptools.StringPtr(parts[0]), + Var_name: proptools.StringPtr(parts[1]), + } + bpModule.insertAfter("prefer", "use_source_config_var", cfp) + } } // Group the variants by os type. diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go index 83c8865de..f3c442ec8 100644 --- a/ui/build/dumpvars.go +++ b/ui/build/dumpvars.go @@ -163,6 +163,7 @@ var BannerVars = []string{ "AUX_OS_VARIANT_LIST", "PRODUCT_SOONG_NAMESPACES", "SOONG_SDK_SNAPSHOT_PREFER", + "SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR", "SOONG_SDK_SNAPSHOT_VERSION", } |