diff options
32 files changed, 1349 insertions, 490 deletions
@@ -609,15 +609,15 @@ To load the code of Soong in IntelliJ: Content Root, then add the `build/blueprint` directory. * Optional: also add the `external/golang-protobuf` directory. In practice, IntelliJ seems to work well enough without this, too. + ### Running Soong in a debugger -To make `soong_build` wait for a debugger connection, install `dlv` and then -start the build with `SOONG_DELVE=<listen addr>` in the environment. -For example: -```bash -SOONG_DELVE=5006 m nothing -``` +Both the Android build driver (`soong_ui`) and Soong proper (`soong_build`) are +Go applications and can be debugged with the help of the standard Go debugger +called Delve. A client (e.g., IntelliJ IDEA) communicates with Delve via IP port +that Delve listens to (the port number is passed to it on invocation). +#### Debugging Android Build Driver #### To make `soong_ui` wait for a debugger connection, use the `SOONG_UI_DELVE` variable: @@ -625,11 +625,28 @@ variable: SOONG_UI_DELVE=5006 m nothing ``` +#### Debugging Soong Proper #### -setting or unsetting `SOONG_DELVE` causes a recompilation of `soong_build`. This +To make `soong_build` wait for a debugger connection, install `dlv` and then +start the build with `SOONG_DELVE=<listen addr>` in the environment. +For example: +```bash +SOONG_DELVE=5006 m nothing +``` +Android build driver invokes `soong_build` multiple times, and by default each +invocation is run in the debugger. Setting `SOONG_DELVE_STEPS` controls which +invocations are run in the debugger, e.g., running +```bash +SOONG_DELVE=2345 SOONG_DELVE_STEPS='build,modulegraph' m +``` +results in only `build` (main build step) and `modulegraph` being run in the debugger. +The allowed step names are `api_bp2build`, `bp2build_files`, `bp2build_workspace`, +`build`, `modulegraph`, `queryview`, `soong_docs`. + +Note setting or unsetting `SOONG_DELVE` causes a recompilation of `soong_build`. This is because in order to debug the binary, it needs to be built with debug symbols. - +#### Delve Troubleshooting #### To test the debugger connection, run this command: ``` @@ -648,15 +665,23 @@ using: sudo sysctl -w kernel.yama.ptrace_scope=0 ``` +#### IntelliJ Setup #### To connect to the process using IntelliJ: * Run -> Edit Configurations... * Choose "Go Remote" on the left * Click on the "+" buttion on the top-left -* Give it a nice name and set "Host" to localhost and "Port" to the port in the - environment variable +* Give it a nice _name_ and set "Host" to `localhost` and "Port" to the port in the + environment variable (`SOONG_UI_DELVE` for `soong_ui`, `SOONG_DELVE` for + `soong_build`) +* Set the breakpoints where you want application to stop +* Run the build from the command line +* In IntelliJ, click Run -> Debug _name_ +* Observe _Connecting..._ message in the debugger pane. It changes to + _Connected_ once the communication with the debugger has been established; the + terminal window where the build started will display + `API server listening at ...` message -Debugging works far worse than debugging Java, but is sometimes useful. Sometimes the `dlv` process hangs on connection. A symptom of this is `dlv` spinning a core or two. In that case, `kill -9` `dlv` and try again. diff --git a/android/androidmk.go b/android/androidmk.go index 18e3e7ac4..846d5061c 100644 --- a/android/androidmk.go +++ b/android/androidmk.go @@ -504,6 +504,7 @@ type fillInEntriesContext interface { Config() Config ModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{} ModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool + ModuleType(module blueprint.Module) string } func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) { @@ -527,7 +528,7 @@ func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint fmt.Fprintf(&a.header, distString) } - fmt.Fprintln(&a.header, "\ninclude $(CLEAR_VARS)") + fmt.Fprintln(&a.header, "\ninclude $(CLEAR_VARS) # "+ctx.ModuleType(mod)) // Collect make variable assignment entries. a.SetString("LOCAL_PATH", ctx.ModuleDir(mod)) diff --git a/android/bazel_handler.go b/android/bazel_handler.go index acb81a497..cf74b9cd7 100644 --- a/android/bazel_handler.go +++ b/android/bazel_handler.go @@ -18,7 +18,6 @@ import ( "bytes" "errors" "fmt" - "io/ioutil" "os" "os/exec" "path" @@ -260,11 +259,11 @@ func (m MockBazelContext) GetCcUnstrippedInfo(label string, _ configKey) (cquery return result, nil } -func (m MockBazelContext) InvokeBazel(_ Config, ctx *Context) error { +func (m MockBazelContext) InvokeBazel(_ Config, _ *Context) error { panic("unimplemented") } -func (m MockBazelContext) BazelAllowlisted(moduleName string) bool { +func (m MockBazelContext) BazelAllowlisted(_ string) bool { return true } @@ -356,7 +355,7 @@ func (n noopBazelContext) GetCcUnstrippedInfo(_ string, _ configKey) (cquery.CcU panic("implement me") } -func (n noopBazelContext) InvokeBazel(_ Config, ctx *Context) error { +func (n noopBazelContext) InvokeBazel(_ Config, _ *Context) error { panic("unimplemented") } @@ -364,7 +363,7 @@ func (m noopBazelContext) OutputBase() string { return "" } -func (n noopBazelContext) BazelAllowlisted(moduleName string) bool { +func (n noopBazelContext) BazelAllowlisted(_ string) bool { return false } @@ -403,7 +402,7 @@ func NewBazelContext(c *config) (BazelContext, error) { // Don't use partially-converted cc_library targets in mixed builds, // since mixed builds would generally rely on both static and shared // variants of a cc_library. - for staticOnlyModule, _ := range GetBp2BuildAllowList().ccLibraryStaticOnly { + for staticOnlyModule := range GetBp2BuildAllowList().ccLibraryStaticOnly { disabledModules[staticOnlyModule] = true } for _, disabledDevModule := range allowlists.MixedBuildsDisabledList { @@ -509,7 +508,7 @@ type mockBazelRunner struct { extraFlags []string } -func (r *mockBazelRunner) createBazelCommand(paths *bazelPaths, runName bazel.RunName, +func (r *mockBazelRunner) createBazelCommand(_ *bazelPaths, _ bazel.RunName, command bazelCommand, extraFlags ...string) *exec.Cmd { r.commands = append(r.commands, command) r.extraFlags = append(r.extraFlags, strings.Join(extraFlags, " ")) @@ -534,13 +533,13 @@ type builtinBazelRunner struct{} // Returns (stdout, stderr, error). The first and second return values are strings // containing the stdout and stderr of the run command, and an error is returned if // the invocation returned an error code. - func (r *builtinBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd) (string, string, error) { stderr := &bytes.Buffer{} bazelCmd.Stderr = stderr if output, err := bazelCmd.Output(); err != nil { return "", string(stderr.Bytes()), - fmt.Errorf("bazel command failed. command: [%s], env: [%s], error [%s]", bazelCmd, bazelCmd.Env, stderr) + fmt.Errorf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---", + err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderr) } else { return string(output), string(stderr.Bytes()), nil } @@ -916,17 +915,17 @@ func (context *bazelContext) runCquery(ctx *Context) error { return err } } - if err := ioutil.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666); err != nil { + if err := os.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666); err != nil { return err } - if err := ioutil.WriteFile(filepath.Join(mixedBuildsPath, "main.bzl"), context.mainBzlFileContents(), 0666); err != nil { + if err := os.WriteFile(filepath.Join(mixedBuildsPath, "main.bzl"), context.mainBzlFileContents(), 0666); err != nil { return err } - if err := ioutil.WriteFile(filepath.Join(mixedBuildsPath, "BUILD.bazel"), context.mainBuildFileContents(), 0666); err != nil { + if err := os.WriteFile(filepath.Join(mixedBuildsPath, "BUILD.bazel"), context.mainBuildFileContents(), 0666); err != nil { return err } cqueryFileRelpath := filepath.Join(context.paths.injectedFilesDir(), "buildroot.cquery") - if err := ioutil.WriteFile(absolutePath(cqueryFileRelpath), context.cqueryStarlarkFileContents(), 0666); err != nil { + if err := os.WriteFile(absolutePath(cqueryFileRelpath), context.cqueryStarlarkFileContents(), 0666); err != nil { return err } @@ -937,7 +936,7 @@ func (context *bazelContext) runCquery(ctx *Context) error { return cqueryErr } cqueryCommandPrint := fmt.Sprintf("cquery command line:\n %s \n\n\n", printableCqueryCommand(cqueryCommandWithFlag)) - if err := ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryCommandPrint+cqueryOutput), 0666); err != nil { + if err := os.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryCommandPrint+cqueryOutput), 0666); err != nil { return err } cqueryResults := map[string]string{} @@ -972,7 +971,7 @@ func (context *bazelContext) runAquery(config Config, ctx *Context) error { extraFlags = append(extraFlags, "--collect_code_coverage") paths := make([]string, 0, 2) if p := config.productVariables.NativeCoveragePaths; len(p) > 0 { - for i, _ := range p { + for i := range p { // TODO(b/259404593) convert path wildcard to regex values if p[i] == "*" { p[i] = ".*" @@ -1039,7 +1038,7 @@ func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) { filepath.Dir(ctx.Config().moduleListFile), "bazel.list")) ctx.AddNinjaFileDeps(bazelBuildList) - data, err := ioutil.ReadFile(bazelBuildList) + data, err := os.ReadFile(bazelBuildList) if err != nil { ctx.Errorf(err.Error()) } diff --git a/android/fixture.go b/android/fixture.go index 3f01f5a0d..c2b16f66e 100644 --- a/android/fixture.go +++ b/android/fixture.go @@ -213,6 +213,46 @@ func FixtureCustomPreparer(mutator func(fixture Fixture)) FixturePreparer { }) } +// FixtureTestRunner determines the type of test to run. +// +// If no custom FixtureTestRunner is provided (using the FixtureSetTestRunner) then the default test +// runner will run a standard Soong test that corresponds to what happens when Soong is run on the +// command line. +type FixtureTestRunner interface { + // FinalPreparer is a function that is run immediately before parsing the blueprint files. It is + // intended to perform the initialization needed by PostParseProcessor. + // + // It returns a CustomTestResult that is passed into PostParseProcessor and returned from + // FixturePreparer.RunTestWithCustomResult. If it needs to return some custom data then it must + // provide its own implementation of CustomTestResult and return an instance of that. Otherwise, + // it can just return the supplied *TestResult. + FinalPreparer(result *TestResult) CustomTestResult + + // PostParseProcessor is called after successfully parsing the blueprint files and can do further + // work on the result of parsing the files. + // + // Successfully parsing simply means that no errors were encountered when parsing the blueprint + // files. + // + // This must collate any information useful for testing, e.g. errs, ninja deps and custom data in + // the supplied result. + PostParseProcessor(result CustomTestResult) +} + +// FixtureSetTestRunner sets the FixtureTestRunner in the fixture. +// +// It is an error if more than one of these is applied to a single fixture. If none of these are +// applied then the fixture will use the defaultTestRunner which will run the test as if it was +// being run in `m <target>`. +func FixtureSetTestRunner(testRunner FixtureTestRunner) FixturePreparer { + return newSimpleFixturePreparer(func(fixture *fixture) { + if fixture.testRunner != nil { + panic("fixture test runner has already been set") + } + fixture.testRunner = testRunner + }) +} + // Modify the config func FixtureModifyConfig(mutator func(config Config)) FixturePreparer { return newSimpleFixturePreparer(func(f *fixture) { @@ -391,6 +431,21 @@ type FixturePreparer interface { // Shorthand for Fixture(t).RunTest() RunTest(t *testing.T) *TestResult + // RunTestWithCustomResult runs the test just as RunTest(t) does but instead of returning a + // *TestResult it returns the CustomTestResult that was returned by the custom + // FixtureTestRunner.PostParseProcessor method that ran the test, or the *TestResult if that + // method returned nil. + // + // This method must be used when needing to access custom data collected by the + // FixtureTestRunner.PostParseProcessor method. + // + // e.g. something like this + // + // preparers := ...FixtureSetTestRunner(&myTestRunner)... + // customResult := preparers.RunTestWithCustomResult(t).(*myCustomTestResult) + // doSomething(customResult.data) + RunTestWithCustomResult(t *testing.T) CustomTestResult + // Run the test with the supplied Android.bp file. // // preparer.RunTestWithBp(t, bp) is shorthand for @@ -619,7 +674,7 @@ type Fixture interface { MockFS() MockFS // Run the test, checking any errors reported and returning a TestResult instance. - RunTest() *TestResult + RunTest() CustomTestResult } // Struct to allow TestResult to embed a *TestContext and allow call forwarding to its methods. @@ -642,6 +697,39 @@ type TestResult struct { NinjaDeps []string } +func (r *TestResult) testResult() *TestResult { return r } + +// CustomTestResult is the interface that FixtureTestRunner implementations who wish to return +// custom data must implement. It must embed *TestResult and initialize that to the value passed +// into the method. It is returned from the FixtureTestRunner.FinalPreparer, passed into the +// FixtureTestRunner.PostParseProcessor and returned from FixturePreparer.RunTestWithCustomResult. +// +// e.g. something like this: +// +// type myCustomTestResult struct { +// *android.TestResult +// data []string +// } +// +// func (r *myTestRunner) FinalPreparer(result *TestResult) CustomTestResult { +// ... do some final test preparation ... +// return &myCustomTestResult{TestResult: result) +// } +// +// func (r *myTestRunner) PostParseProcessor(result CustomTestResult) { +// ... +// myData := []string {....} +// ... +// customResult := result.(*myCustomTestResult) +// customResult.data = myData +// } +type CustomTestResult interface { + // testResult returns the embedded *TestResult. + testResult() *TestResult +} + +var _ CustomTestResult = (*TestResult)(nil) + type TestPathContext struct { *TestResult } @@ -696,6 +784,11 @@ func (b *baseFixturePreparer) ExtendWithErrorHandler(errorHandler FixtureErrorHa func (b *baseFixturePreparer) RunTest(t *testing.T) *TestResult { t.Helper() + return b.RunTestWithCustomResult(t).testResult() +} + +func (b *baseFixturePreparer) RunTestWithCustomResult(t *testing.T) CustomTestResult { + t.Helper() fixture := b.self.Fixture(t) return fixture.RunTest() } @@ -724,13 +817,16 @@ func (b *baseFixturePreparer) RunTestWithConfig(t *testing.T, config Config) *Te ctx.SetModuleListFile(ctx.config.mockBpList) } - return fixture.RunTest() + return fixture.RunTest().testResult() } type fixture struct { // The preparers used to create this fixture. preparers []*simpleFixturePreparer + // The test runner used in this fixture, defaults to defaultTestRunner if not set. + testRunner FixtureTestRunner + // The gotest state of the go test within which this was created. t *testing.T @@ -762,7 +858,7 @@ func (f *fixture) MockFS() MockFS { return f.mockFS } -func (f *fixture) RunTest() *TestResult { +func (f *fixture) RunTest() CustomTestResult { f.t.Helper() // If in debug mode output the state of the fixture before running the test. @@ -800,30 +896,59 @@ func (f *fixture) RunTest() *TestResult { // Set the NameResolver in the TestContext. ctx.NameResolver = resolver - ctx.Register() - var ninjaDeps []string - extraNinjaDeps, errs := ctx.ParseBlueprintsFiles("ignored") - if len(errs) == 0 { - ninjaDeps = append(ninjaDeps, extraNinjaDeps...) - extraNinjaDeps, errs = ctx.PrepareBuildActions(f.config) - if len(errs) == 0 { - ninjaDeps = append(ninjaDeps, extraNinjaDeps...) - } + // If test runner has not been set then use the default runner. + if f.testRunner == nil { + f.testRunner = defaultTestRunner } + // Create the result to collate result information. result := &TestResult{ testContext: testContext{ctx}, fixture: f, Config: f.config, - Errs: errs, - NinjaDeps: ninjaDeps, + } + + // Do any last minute preparation before parsing the blueprint files. + customResult := f.testRunner.FinalPreparer(result) + + // Parse the blueprint files adding the information to the result. + extraNinjaDeps, errs := ctx.ParseBlueprintsFiles("ignored") + result.NinjaDeps = append(result.NinjaDeps, extraNinjaDeps...) + result.Errs = append(result.Errs, errs...) + + if len(result.Errs) == 0 { + // If parsing the blueprint files was successful then perform any additional processing. + f.testRunner.PostParseProcessor(customResult) } f.errorHandler.CheckErrors(f.t, result) + return customResult +} + +// standardTestRunner is the implementation of the default test runner +type standardTestRunner struct{} + +func (s *standardTestRunner) FinalPreparer(result *TestResult) CustomTestResult { + // Register the hard coded mutators and singletons used by the standard Soong build as well as + // any additional instances that have been registered with this fixture. + result.TestContext.Register() return result } +func (s *standardTestRunner) PostParseProcessor(customResult CustomTestResult) { + result := customResult.(*TestResult) + ctx := result.TestContext + cfg := result.Config + // Prepare the build actions, i.e. run all the mutators, singletons and then invoke the + // GenerateAndroidBuildActions methods on all the modules. + extraNinjaDeps, errs := ctx.PrepareBuildActions(cfg) + result.NinjaDeps = append(result.NinjaDeps, extraNinjaDeps...) + result.CollateErrs(errs) +} + +var defaultTestRunner FixtureTestRunner = &standardTestRunner{} + func (f *fixture) outputDebugState() { fmt.Printf("Begin Fixture State for %s\n", f.t.Name()) if len(f.config.env) == 0 { @@ -909,3 +1034,10 @@ func (r *TestResult) Preparer() FixturePreparer { func (r *TestResult) Module(name string, variant string) Module { return r.ModuleForTests(name, variant).Module() } + +// CollateErrs adds additional errors to the result and returns true if there is more than one +// error in the result. +func (r *TestResult) CollateErrs(errs []error) bool { + r.Errs = append(r.Errs, errs...) + return len(r.Errs) > 0 +} diff --git a/android/mutator.go b/android/mutator.go index 83d4e6644..d92b87c4d 100644 --- a/android/mutator.go +++ b/android/mutator.go @@ -705,6 +705,28 @@ func (t *topDownMutatorContext) CreateBazelTargetModuleWithRestrictions( t.createBazelTargetModule(bazelProps, commonAttrs, attrs, enabledProperty) } +// ApexAvailableTags converts the apex_available property value of an ApexModule +// module and returns it as a list of keyed tags. +func ApexAvailableTags(mod Module) bazel.StringListAttribute { + attr := bazel.StringListAttribute{} + tags := []string{} + // Transform specific attributes into tags. + if am, ok := mod.(ApexModule); ok { + // TODO(b/218841706): hidl_interface has the apex_available prop, but it's + // defined directly as a prop and not via ApexModule, so this doesn't + // pick those props up. + // TODO(b/260694842): This does not pick up aidl_interface.backend.ndk.apex_available. + for _, a := range am.apexModuleBase().ApexAvailable() { + tags = append(tags, "apex_available="+a) + } + } + if len(tags) > 0 { + // This avoids creating a tags attr with an empty list if there are no tags. + attr.Value = tags + } + return attr +} + func (t *topDownMutatorContext) createBazelTargetModule( bazelProps bazel.BazelTargetModuleProperties, commonAttrs CommonAttributes, diff --git a/android/testing.go b/android/testing.go index 8fcf4409b..29af71fbc 100644 --- a/android/testing.go +++ b/android/testing.go @@ -203,6 +203,10 @@ func (ctx *TestContext) HardCodedPreArchMutators(f RegisterMutatorFunc) { ctx.PreArchMutators(f) } +func (ctx *TestContext) ModuleProvider(m blueprint.Module, p blueprint.ProviderKey) interface{} { + return ctx.Context.ModuleProvider(m, p) +} + func (ctx *TestContext) PreDepsMutators(f RegisterMutatorFunc) { ctx.preDeps = append(ctx.preDeps, f) } diff --git a/apex/androidmk.go b/apex/androidmk.go index 3373211a0..0fc971bb0 100644 --- a/apex/androidmk.go +++ b/apex/androidmk.go @@ -134,7 +134,7 @@ func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, apexName, mo continue } - fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)") + fmt.Fprintln(w, "\ninclude $(CLEAR_VARS) # apex.apexBundle.files") if fi.moduleDir != "" { fmt.Fprintln(w, "LOCAL_PATH :=", fi.moduleDir) } else { @@ -348,7 +348,7 @@ func (a *apexBundle) androidMkForType() android.AndroidMkData { if apexType == flattenedApex { // Only image APEXes can be flattened. - fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)") + fmt.Fprintln(w, "\ninclude $(CLEAR_VARS) # apex.apexBundle.flat") fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir) fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix) data.Entries.WriteLicenseVariables(w) @@ -356,7 +356,7 @@ func (a *apexBundle) androidMkForType() android.AndroidMkData { fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)") } else { - fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)") + fmt.Fprintln(w, "\ninclude $(CLEAR_VARS) # apex.apexBundle") fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir) fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix) data.Entries.WriteLicenseVariables(w) diff --git a/apex/apex.go b/apex/apex.go index b1b4e4716..8e1783ed5 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -2676,6 +2676,10 @@ func (o *OverrideApex) ConvertWithBp2build(ctx android.TopDownMutatorContext) { } attrs, props := convertWithBp2build(a, ctx) + // We just want the name, not module reference. + baseApexName := strings.TrimPrefix(baseApexModuleName, ":") + attrs.Base_apex_name = &baseApexName + for _, p := range o.GetProperties() { overridableProperties, ok := p.(*overridableProperties) if !ok { @@ -3397,6 +3401,7 @@ type bazelApexBundleAttributes struct { Package_name *string Logging_parent *string Tests bazel.LabelListAttribute + Base_apex_name *string } type convertedNativeSharedLibs struct { diff --git a/bp2build/apex_conversion_test.go b/bp2build/apex_conversion_test.go index b6061e46e..714b8488a 100644 --- a/bp2build/apex_conversion_test.go +++ b/bp2build/apex_conversion_test.go @@ -663,6 +663,7 @@ override_apex { ExpectedBazelTargets: []string{ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ "android_manifest": `"ApogeeAndroidManifest.xml"`, + "base_apex_name": `"com.android.apogee"`, "binaries": `[ ":cc_binary_1", ":sh_binary_2", @@ -729,8 +730,9 @@ override_apex { `, ExpectedBazelTargets: []string{ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"//a/b:apex_manifest.json"`, + "base_apex_name": `"com.android.apogee"`, + "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, + "manifest": `"//a/b:apex_manifest.json"`, }), }}) } @@ -763,8 +765,9 @@ override_apex { `, ExpectedBazelTargets: []string{ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"//a/b:apogee_manifest.json"`, + "base_apex_name": `"com.android.apogee"`, + "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, + "manifest": `"//a/b:apogee_manifest.json"`, }), }}) } @@ -795,8 +798,9 @@ override_apex { `, ExpectedBazelTargets: []string{ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apex_manifest.json"`, + "base_apex_name": `"com.android.apogee"`, + "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, + "manifest": `"apex_manifest.json"`, }), }}) } @@ -828,8 +832,9 @@ override_apex { `, ExpectedBazelTargets: []string{ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apogee_manifest.json"`, + "base_apex_name": `"com.android.apogee"`, + "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, + "manifest": `"apogee_manifest.json"`, }), }}) } @@ -861,9 +866,10 @@ override_apex { `, ExpectedBazelTargets: []string{ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apex_manifest.json"`, - "package_name": `"com.google.android.apogee"`, + "base_apex_name": `"com.android.apogee"`, + "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, + "manifest": `"apex_manifest.json"`, + "package_name": `"com.google.android.apogee"`, }), }}) } @@ -900,9 +906,10 @@ override_apex { `, ExpectedBazelTargets: []string{ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apex_manifest.json"`, - "prebuilts": `[":prebuilt_file"]`, + "base_apex_name": `"com.android.apogee"`, + "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, + "manifest": `"apex_manifest.json"`, + "prebuilts": `[":prebuilt_file"]`, }), }}) } @@ -945,9 +952,10 @@ override_apex { `, ExpectedBazelTargets: []string{ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apex_manifest.json"`, - "prebuilts": `[":prebuilt_file2"]`, + "base_apex_name": `"com.android.apogee"`, + "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, + "manifest": `"apex_manifest.json"`, + "prebuilts": `[":prebuilt_file2"]`, }), }}) } @@ -985,9 +993,10 @@ override_apex { `, ExpectedBazelTargets: []string{ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, - "manifest": `"apex_manifest.json"`, - "prebuilts": `[]`, + "base_apex_name": `"com.android.apogee"`, + "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, + "manifest": `"apex_manifest.json"`, + "prebuilts": `[]`, }), }}) } @@ -1019,6 +1028,7 @@ override_apex { `, ExpectedBazelTargets: []string{ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ + "base_apex_name": `"com.android.apogee"`, "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, "manifest": `"apex_manifest.json"`, "logging_parent": `"foo.bar.baz"`, @@ -1054,6 +1064,7 @@ override_apex { `, ExpectedBazelTargets: []string{ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ + "base_apex_name": `"com.android.apogee"`, "file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`, "manifest": `"apex_manifest.json"`, "logging_parent": `"foo.bar.baz.override"`, @@ -1099,8 +1110,9 @@ override_apex { `, ExpectedBazelTargets: []string{ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "file_contexts": `":com.android.apogee-file_contexts"`, - "manifest": `"apogee_manifest.json"`, + "base_apex_name": `"com.android.apogee"`, + "file_contexts": `":com.android.apogee-file_contexts"`, + "manifest": `"apogee_manifest.json"`, }), }}) } @@ -1200,9 +1212,10 @@ override_apex { `, ExpectedBazelTargets: []string{ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ - "file_contexts": `":com.android.apogee-file_contexts"`, - "certificate": `":com.google.android.apogee.certificate"`, - "manifest": `"apogee_manifest.json"`, + "base_apex_name": `"com.android.apogee"`, + "file_contexts": `":com.android.apogee-file_contexts"`, + "certificate": `":com.google.android.apogee.certificate"`, + "manifest": `"apogee_manifest.json"`, }), }}) } @@ -1244,6 +1257,7 @@ override_apex { `, ExpectedBazelTargets: []string{ MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{ + "base_apex_name": `"com.android.apogee"`, "file_contexts": `":com.android.apogee-file_contexts"`, "certificate_name": `"com.google.android.apogee.certificate"`, "manifest": `"apogee_manifest.json"`, diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go index edb0c43ab..a1e83d8c9 100644 --- a/bp2build/cc_library_conversion_test.go +++ b/bp2build/cc_library_conversion_test.go @@ -3645,3 +3645,49 @@ func TestCcLibraryHeaderAbiChecker(t *testing.T) { }, }) } + +func TestCcLibraryApexAvailable(t *testing.T) { + runCcLibraryTestCase(t, Bp2buildTestCase{ + Description: "cc_library apex_available converted to tags", + ModuleTypeUnderTest: "cc_library", + ModuleTypeUnderTestFactory: cc.LibraryFactory, + Blueprint: soongCcLibraryPreamble + ` +cc_library { + name: "a", + srcs: ["a.cpp"], + apex_available: ["com.android.foo"], +} +`, + ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{ + "tags": `["apex_available=com.android.foo"]`, + "srcs": `["a.cpp"]`, + "local_includes": `["."]`, + }), + }, + ) +} + +func TestCcLibraryApexAvailableMultiple(t *testing.T) { + runCcLibraryTestCase(t, Bp2buildTestCase{ + Description: "cc_library apex_available converted to multiple tags", + ModuleTypeUnderTest: "cc_library", + ModuleTypeUnderTestFactory: cc.LibraryFactory, + Blueprint: soongCcLibraryPreamble + ` +cc_library { + name: "a", + srcs: ["a.cpp"], + apex_available: ["com.android.foo", "//apex_available:platform", "com.android.bar"], +} +`, + ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{ + "tags": `[ + "apex_available=com.android.foo", + "apex_available=//apex_available:platform", + "apex_available=com.android.bar", + ]`, + "srcs": `["a.cpp"]`, + "local_includes": `["."]`, + }), + }, + ) +} diff --git a/bp2build/cc_prebuilt_library_conversion_test.go b/bp2build/cc_prebuilt_library_conversion_test.go index 47006ac5a..2fe158eec 100644 --- a/bp2build/cc_prebuilt_library_conversion_test.go +++ b/bp2build/cc_prebuilt_library_conversion_test.go @@ -91,9 +91,9 @@ func TestPrebuiltLibraryAdditionalAttrs(t *testing.T) { ModuleTypeUnderTest: "cc_prebuilt_library", ModuleTypeUnderTestFactory: cc.PrebuiltLibraryFactory, Filesystem: map[string]string{ - "libf.so": "", - "testdir/1/": "", - "testdir/2/": "", + "libf.so": "", + "testdir/1/include.h": "", + "testdir/2/other.h": "", }, Blueprint: ` cc_prebuilt_library { diff --git a/bp2build/testing.go b/bp2build/testing.go index 4e63d1995..c059add95 100644 --- a/bp2build/testing.go +++ b/bp2build/testing.go @@ -91,65 +91,66 @@ type Bp2buildTestCase struct { func RunBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc Bp2buildTestCase) { t.Helper() - bp2buildSetup := func(ctx *android.TestContext) { - registerModuleTypes(ctx) - ctx.RegisterForBazelConversion() - } + bp2buildSetup := android.GroupFixturePreparers( + android.FixtureRegisterWithContext(registerModuleTypes), + SetBp2BuildTestRunner, + ) runBp2BuildTestCaseWithSetup(t, bp2buildSetup, tc) } func RunApiBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc Bp2buildTestCase) { t.Helper() - apiBp2BuildSetup := func(ctx *android.TestContext) { - registerModuleTypes(ctx) - ctx.RegisterForApiBazelConversion() - } + apiBp2BuildSetup := android.GroupFixturePreparers( + android.FixtureRegisterWithContext(registerModuleTypes), + SetApiBp2BuildTestRunner, + ) runBp2BuildTestCaseWithSetup(t, apiBp2BuildSetup, tc) } -func runBp2BuildTestCaseWithSetup(t *testing.T, setup func(ctx *android.TestContext), tc Bp2buildTestCase) { +func runBp2BuildTestCaseWithSetup(t *testing.T, extraPreparer android.FixturePreparer, tc Bp2buildTestCase) { t.Helper() dir := "." filesystem := make(map[string][]byte) - toParse := []string{ - "Android.bp", - } for f, content := range tc.Filesystem { - if strings.HasSuffix(f, "Android.bp") { - toParse = append(toParse, f) - } filesystem[f] = []byte(content) } - config := android.TestConfig(buildDir, nil, tc.Blueprint, filesystem) - ctx := android.NewTestContext(config) - - setup(ctx) - ctx.RegisterModuleType(tc.ModuleTypeUnderTest, tc.ModuleTypeUnderTestFactory) - // A default configuration for tests to not have to specify bp2build_available on top level targets. - bp2buildConfig := android.NewBp2BuildAllowlist().SetDefaultConfig( - allowlists.Bp2BuildConfig{ - android.Bp2BuildTopLevel: allowlists.Bp2BuildDefaultTrueRecursively, - }, - ) - for _, f := range tc.KeepBuildFileForDirs { - bp2buildConfig.SetKeepExistingBuildFile(map[string]bool{ - f: /*recursive=*/ false, - }) + preparers := []android.FixturePreparer{ + extraPreparer, + android.FixtureMergeMockFs(filesystem), + android.FixtureWithRootAndroidBp(tc.Blueprint), + android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { + ctx.RegisterModuleType(tc.ModuleTypeUnderTest, tc.ModuleTypeUnderTestFactory) + }), + android.FixtureModifyContext(func(ctx *android.TestContext) { + // A default configuration for tests to not have to specify bp2build_available on top level + // targets. + bp2buildConfig := android.NewBp2BuildAllowlist().SetDefaultConfig( + allowlists.Bp2BuildConfig{ + android.Bp2BuildTopLevel: allowlists.Bp2BuildDefaultTrueRecursively, + }, + ) + for _, f := range tc.KeepBuildFileForDirs { + bp2buildConfig.SetKeepExistingBuildFile(map[string]bool{ + f: /*recursive=*/ false, + }) + } + ctx.RegisterBp2BuildConfig(bp2buildConfig) + }), + android.FixtureModifyEnv(func(env map[string]string) { + if tc.UnconvertedDepsMode == errorModulesUnconvertedDeps { + env["BP2BUILD_ERROR_UNCONVERTED"] = "true" + } + }), } - ctx.RegisterBp2BuildConfig(bp2buildConfig) - _, parseErrs := ctx.ParseFileList(dir, toParse) - if errored(t, tc, parseErrs) { - return - } - _, resolveDepsErrs := ctx.ResolveDependencies(config) - if errored(t, tc, resolveDepsErrs) { - return + preparer := android.GroupFixturePreparers(preparers...) + if tc.ExpectedErr != nil { + pattern := "\\Q" + tc.ExpectedErr.Error() + "\\E" + preparer = preparer.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(pattern)) } - - parseAndResolveErrs := append(parseErrs, resolveDepsErrs...) - if tc.ExpectedErr != nil && checkError(t, parseAndResolveErrs, tc.ExpectedErr) { + result := preparer.RunTestWithCustomResult(t).(*BazelTestResult) + if len(result.Errs) > 0 { return } @@ -157,27 +158,115 @@ func runBp2BuildTestCaseWithSetup(t *testing.T, setup func(ctx *android.TestCont if tc.Dir != "" { checkDir = tc.Dir } + expectedTargets := map[string][]string{ + checkDir: tc.ExpectedBazelTargets, + } + + result.CompareAllBazelTargets(t, tc.Description, expectedTargets, true) +} + +// SetBp2BuildTestRunner customizes the test fixture mechanism to run tests in Bp2Build mode. +var SetBp2BuildTestRunner = android.FixtureSetTestRunner(&bazelTestRunner{Bp2Build}) + +// SetApiBp2BuildTestRunner customizes the test fixture mechanism to run tests in ApiBp2build mode. +var SetApiBp2BuildTestRunner = android.FixtureSetTestRunner(&bazelTestRunner{ApiBp2build}) + +// bazelTestRunner customizes the test fixture mechanism to run tests of the bp2build and +// apiBp2build build modes. +type bazelTestRunner struct { + mode CodegenMode +} + +func (b *bazelTestRunner) FinalPreparer(result *android.TestResult) android.CustomTestResult { + ctx := result.TestContext + switch b.mode { + case Bp2Build: + ctx.RegisterForBazelConversion() + case ApiBp2build: + ctx.RegisterForApiBazelConversion() + default: + panic(fmt.Errorf("unknown build mode: %d", b.mode)) + } + + return &BazelTestResult{TestResult: result} +} + +func (b *bazelTestRunner) PostParseProcessor(result android.CustomTestResult) { + bazelResult := result.(*BazelTestResult) + ctx := bazelResult.TestContext + config := bazelResult.Config + _, errs := ctx.ResolveDependencies(config) + if bazelResult.CollateErrs(errs) { + return + } + codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build) - codegenCtx.unconvertedDepMode = tc.UnconvertedDepsMode - bazelTargets, errs := generateBazelTargetsForDir(codegenCtx, checkDir) - if tc.ExpectedErr != nil { - if checkError(t, errs, tc.ExpectedErr) { - return + res, errs := GenerateBazelTargets(codegenCtx, false) + if bazelResult.CollateErrs(errs) { + return + } + + // Store additional data for access by tests. + bazelResult.conversionResults = res +} + +// BazelTestResult is a wrapper around android.TestResult to provide type safe access to the bazel +// specific data stored by the bazelTestRunner. +type BazelTestResult struct { + *android.TestResult + + // The result returned by the GenerateBazelTargets function. + conversionResults +} + +// CompareAllBazelTargets compares the BazelTargets produced by the test for all the directories +// with the supplied set of expected targets. +// +// If ignoreUnexpected=false then this enforces an exact match where every BazelTarget produced must +// have a corresponding expected BazelTarget. +// +// If ignoreUnexpected=true then it will ignore directories for which there are no expected targets. +func (b BazelTestResult) CompareAllBazelTargets(t *testing.T, description string, expectedTargets map[string][]string, ignoreUnexpected bool) { + actualTargets := b.buildFileToTargets + + // Generate the sorted set of directories to check. + dirsToCheck := android.SortedStringKeys(expectedTargets) + if !ignoreUnexpected { + // This needs to perform an exact match so add the directories in which targets were + // produced to the list of directories to check. + dirsToCheck = append(dirsToCheck, android.SortedStringKeys(actualTargets)...) + dirsToCheck = android.SortedUniqueStrings(dirsToCheck) + } + + for _, dir := range dirsToCheck { + expected := expectedTargets[dir] + actual := actualTargets[dir] + + if expected == nil { + if actual != nil { + t.Errorf("did not expect any bazel modules in %q but found %d", dir, len(actual)) + } + } else if actual == nil { + expectedCount := len(expected) + if expectedCount > 0 { + t.Errorf("expected %d bazel modules in %q but did not find any", expectedCount, dir) + } } else { - t.Errorf("Expected error: %q, got: %q and %q", tc.ExpectedErr, errs, parseAndResolveErrs) + b.CompareBazelTargets(t, description, expected, actual) } - } else { - android.FailIfErrored(t, errs) } - if actualCount, expectedCount := len(bazelTargets), len(tc.ExpectedBazelTargets); actualCount != expectedCount { +} + +func (b BazelTestResult) CompareBazelTargets(t *testing.T, description string, expectedContents []string, actualTargets BazelTargets) { + if actualCount, expectedCount := len(actualTargets), len(expectedContents); actualCount != expectedCount { t.Errorf("%s: Expected %d bazel target (%s), got %d (%s)", - tc.Description, expectedCount, tc.ExpectedBazelTargets, actualCount, bazelTargets) + description, expectedCount, expectedContents, actualCount, actualTargets) } else { - for i, target := range bazelTargets { - if w, g := tc.ExpectedBazelTargets[i], target.content; w != g { + for i, actualTarget := range actualTargets { + if w, g := expectedContents[i], actualTarget.content; w != g { t.Errorf( - "%s: Expected generated Bazel target to be `%s`, got `%s`", - tc.Description, w, g) + "%s[%d]: Expected generated Bazel target to be `%s`, got `%s`", + description, i, w, g) } } } diff --git a/bpf/bpf.go b/bpf/bpf.go index a840fa3df..d91180b91 100644 --- a/bpf/bpf.go +++ b/bpf/bpf.go @@ -227,7 +227,7 @@ func (bpf *bpf) AndroidMk() android.AndroidMkData { for _, obj := range bpf.objs { objName := name + "_" + obj.Base() names = append(names, objName) - fmt.Fprintln(w, "include $(CLEAR_VARS)") + fmt.Fprintln(w, "include $(CLEAR_VARS)", " # bpf.bpf.obj") fmt.Fprintln(w, "LOCAL_MODULE := ", objName) data.Entries.WriteLicenseVariables(w) fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", obj.String()) @@ -237,7 +237,7 @@ func (bpf *bpf) AndroidMk() android.AndroidMkData { fmt.Fprintln(w, "include $(BUILD_PREBUILT)") fmt.Fprintln(w) } - fmt.Fprintln(w, "include $(CLEAR_VARS)") + fmt.Fprintln(w, "include $(CLEAR_VARS)", " # bpf.bpf") fmt.Fprintln(w, "LOCAL_MODULE := ", name) data.Entries.WriteLicenseVariables(w) fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(names, " ")) diff --git a/cc/androidmk.go b/cc/androidmk.go index aaf21e9c2..ce35b5c44 100644 --- a/cc/androidmk.go +++ b/cc/androidmk.go @@ -530,8 +530,10 @@ func (c *snapshotLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entrie entries.SubName = "" - if c.sanitizerProperties.CfiEnabled { + if c.isSanitizerEnabled(cfi) { entries.SubName += ".cfi" + } else if c.isSanitizerEnabled(Hwasan) { + entries.SubName += ".hwasan" } entries.SubName += c.baseProperties.Androidmk_suffix diff --git a/cc/binary.go b/cc/binary.go index c2868e7bb..998934e17 100644 --- a/cc/binary.go +++ b/cc/binary.go @@ -655,11 +655,12 @@ func binaryBp2build(ctx android.TopDownMutatorContext, m *Module) { // shared with cc_test binaryAttrs := binaryBp2buildAttrs(ctx, m) + tags := android.ApexAvailableTags(m) ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{ Rule_class: "cc_binary", Bzl_load_location: "//build/bazel/rules/cc:cc_binary.bzl", }, - android.CommonAttributes{Name: m.Name()}, + android.CommonAttributes{Name: m.Name(), Tags: tags}, &binaryAttrs) } diff --git a/cc/fuzz.go b/cc/fuzz.go index 64bb7dd60..3da76512e 100644 --- a/cc/fuzz.go +++ b/cc/fuzz.go @@ -301,7 +301,6 @@ func NewFuzzer(hod android.HostOrDeviceSupported) *Module { baseInstallerPath := "fuzz" binary.baseInstaller = NewBaseInstaller(baseInstallerPath, baseInstallerPath, InstallInData) - module.sanitize.SetSanitizer(Fuzzer, true) fuzzBin := &fuzzBinary{ binaryDecorator: binary, @@ -315,7 +314,11 @@ func NewFuzzer(hod android.HostOrDeviceSupported) *Module { // The fuzzer runtime is not present for darwin host modules, disable cc_fuzz modules when targeting darwin. android.AddLoadHook(module, func(ctx android.LoadHookContext) { - disableDarwinAndLinuxBionic := struct { + + extraProps := struct { + Sanitize struct { + Fuzzer *bool + } Target struct { Darwin struct { Enabled *bool @@ -325,9 +328,10 @@ func NewFuzzer(hod android.HostOrDeviceSupported) *Module { } } }{} - disableDarwinAndLinuxBionic.Target.Darwin.Enabled = BoolPtr(false) - disableDarwinAndLinuxBionic.Target.Linux_bionic.Enabled = BoolPtr(false) - ctx.AppendProperties(&disableDarwinAndLinuxBionic) + extraProps.Sanitize.Fuzzer = BoolPtr(true) + extraProps.Target.Darwin.Enabled = BoolPtr(false) + extraProps.Target.Linux_bionic.Enabled = BoolPtr(false) + ctx.AppendProperties(&extraProps) targetFramework := fuzz.GetFramework(ctx, fuzz.Cc) if !fuzz.IsValidFrameworkForModule(targetFramework, fuzz.Cc, fuzzBin.fuzzPackagedModule.FuzzProperties.Fuzzing_frameworks) { diff --git a/cc/genrule.go b/cc/genrule.go index 4ef990c35..d1c4c2a2f 100644 --- a/cc/genrule.go +++ b/cc/genrule.go @@ -48,6 +48,8 @@ type GenruleExtraProperties struct { // // CC_NATIVE_BRIDGE the name of the subdirectory that native bridge libraries are stored in if // the architecture has native bridge enabled, empty if it is disabled. +// +// CC_OS the name of the OS the command is being executed for. func GenRuleFactory() android.Module { module := genrule.NewGenRule() @@ -68,8 +70,9 @@ func GenRuleFactory() android.Module { func genruleCmdModifier(ctx android.ModuleContext, cmd string) string { target := ctx.Target() arch := target.Arch.ArchType - return fmt.Sprintf("CC_ARCH=%s CC_NATIVE_BRIDGE=%s CC_MULTILIB=%s && %s", - arch.Name, target.NativeBridgeRelativePath, arch.Multilib, cmd) + osName := target.Os.Name + return fmt.Sprintf("CC_ARCH=%s CC_NATIVE_BRIDGE=%s CC_MULTILIB=%s CC_OS=%s && %s", + arch.Name, target.NativeBridgeRelativePath, arch.Multilib, osName, cmd) } var _ android.ImageInterface = (*GenruleExtraProperties)(nil) diff --git a/cc/library.go b/cc/library.go index 365f3922c..d1d19452f 100644 --- a/cc/library.go +++ b/cc/library.go @@ -448,11 +448,18 @@ func libraryBp2Build(ctx android.TopDownMutatorContext, m *Module) { Bzl_load_location: "//build/bazel/rules/cc:cc_library_shared.bzl", } + tags := android.ApexAvailableTags(m) ctx.CreateBazelTargetModuleWithRestrictions(staticProps, - android.CommonAttributes{Name: m.Name() + "_bp2build_cc_library_static"}, + android.CommonAttributes{ + Name: m.Name() + "_bp2build_cc_library_static", + Tags: tags, + }, staticTargetAttrs, staticAttrs.Enabled) ctx.CreateBazelTargetModuleWithRestrictions(sharedProps, - android.CommonAttributes{Name: m.Name()}, + android.CommonAttributes{ + Name: m.Name(), + Tags: tags, + }, sharedTargetAttrs, sharedAttrs.Enabled) createStubsBazelTargetIfNeeded(ctx, m, compilerAttrs, exportedIncludes, baseAttributes) @@ -2918,7 +2925,8 @@ func sharedOrStaticLibraryBp2Build(ctx android.TopDownMutatorContext, module *Mo Bzl_load_location: fmt.Sprintf("//build/bazel/rules/cc:%s.bzl", modType), } - ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs) + tags := android.ApexAvailableTags(module) + ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name(), Tags: tags}, attrs) } // TODO(b/199902614): Can this be factored to share with the other Attributes? diff --git a/cc/library_stub.go b/cc/library_stub.go index 22e61a768..c61e2d1cc 100644 --- a/cc/library_stub.go +++ b/cc/library_stub.go @@ -172,7 +172,7 @@ func (d *apiLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps // Copy LLDNK properties to cc_api_library module d.libraryDecorator.flagExporter.Properties.Export_include_dirs = append( d.libraryDecorator.flagExporter.Properties.Export_include_dirs, - variantMod.exportProperties.Export_headers...) + variantMod.exportProperties.Export_include_dirs...) // Export headers as system include dirs if specified. Mostly for libc if Bool(variantMod.exportProperties.Export_headers_as_system) { @@ -203,7 +203,7 @@ func (d *apiLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps // Copy NDK properties to cc_api_library module d.libraryDecorator.flagExporter.Properties.Export_include_dirs = append( d.libraryDecorator.flagExporter.Properties.Export_include_dirs, - variantMod.exportProperties.Export_headers...) + variantMod.exportProperties.Export_include_dirs...) } } } @@ -362,7 +362,7 @@ type ccApiexportProperties struct { type variantExporterProperties struct { // Header directory to export - Export_headers []string `android:"arch_variant"` + Export_include_dirs []string `android:"arch_variant"` // Export all headers as system include Export_headers_as_system *bool diff --git a/cc/library_stub_test.go b/cc/library_stub_test.go index e3728606a..868447a8f 100644 --- a/cc/library_stub_test.go +++ b/cc/library_stub_test.go @@ -308,7 +308,7 @@ func TestApiLibraryWithLlndkVariant(t *testing.T) { name: "libbar", variant: "llndk", src: "libbar_llndk.so", - export_headers: ["libbar_llndk_include"] + export_include_dirs: ["libbar_llndk_include"] } api_imports { @@ -370,7 +370,7 @@ func TestApiLibraryWithNdkVariant(t *testing.T) { variant: "ndk", version: "29", src: "libbar_ndk_29.so", - export_headers: ["libbar_ndk_29_include"] + export_include_dirs: ["libbar_ndk_29_include"] } cc_api_variant { @@ -378,7 +378,7 @@ func TestApiLibraryWithNdkVariant(t *testing.T) { variant: "ndk", version: "30", src: "libbar_ndk_30.so", - export_headers: ["libbar_ndk_30_include"] + export_include_dirs: ["libbar_ndk_30_include"] } cc_api_variant { @@ -386,7 +386,7 @@ func TestApiLibraryWithNdkVariant(t *testing.T) { variant: "ndk", version: "current", src: "libbar_ndk_current.so", - export_headers: ["libbar_ndk_current_include"] + export_include_dirs: ["libbar_ndk_current_include"] } api_imports { @@ -458,7 +458,7 @@ func TestApiLibraryWithMultipleVariants(t *testing.T) { variant: "ndk", version: "29", src: "libbar_ndk_29.so", - export_headers: ["libbar_ndk_29_include"] + export_include_dirs: ["libbar_ndk_29_include"] } cc_api_variant { @@ -466,7 +466,7 @@ func TestApiLibraryWithMultipleVariants(t *testing.T) { variant: "ndk", version: "30", src: "libbar_ndk_30.so", - export_headers: ["libbar_ndk_30_include"] + export_include_dirs: ["libbar_ndk_30_include"] } cc_api_variant { @@ -474,14 +474,14 @@ func TestApiLibraryWithMultipleVariants(t *testing.T) { variant: "ndk", version: "current", src: "libbar_ndk_current.so", - export_headers: ["libbar_ndk_current_include"] + export_include_dirs: ["libbar_ndk_current_include"] } cc_api_variant { name: "libbar", variant: "llndk", src: "libbar_llndk.so", - export_headers: ["libbar_llndk_include"] + export_include_dirs: ["libbar_llndk_include"] } api_imports { diff --git a/cc/prebuilt.go b/cc/prebuilt.go index 1842e5abe..9fbf8794e 100644 --- a/cc/prebuilt.go +++ b/cc/prebuilt.go @@ -388,7 +388,9 @@ func prebuiltLibraryStaticBp2Build(ctx android.TopDownMutatorContext, module *Mo if fullBuild { name += "_bp2build_cc_library_static" } - ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name}, attrs, prebuiltAttrs.Enabled) + + tags := android.ApexAvailableTags(module) + ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name, Tags: tags}, attrs, prebuiltAttrs.Enabled) } type bazelPrebuiltLibrarySharedAttributes struct { @@ -408,7 +410,8 @@ func prebuiltLibrarySharedBp2Build(ctx android.TopDownMutatorContext, module *Mo } name := android.RemoveOptionalPrebuiltPrefix(module.Name()) - ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name}, attrs, prebuiltAttrs.Enabled) + tags := android.ApexAvailableTags(module) + ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name, Tags: tags}, attrs, prebuiltAttrs.Enabled) } type prebuiltObjectProperties struct { @@ -740,7 +743,8 @@ func prebuiltBinaryBp2Build(ctx android.TopDownMutatorContext, module *Module) { } name := android.RemoveOptionalPrebuiltPrefix(module.Name()) - ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, attrs) + tags := android.ApexAvailableTags(module) + ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name, Tags: tags}, attrs) } type Sanitized struct { @@ -759,10 +763,10 @@ func srcsForSanitizer(sanitize *sanitize, sanitized Sanitized) []string { if sanitize == nil { return nil } - if Bool(sanitize.Properties.Sanitize.Address) && sanitized.Address.Srcs != nil { + if sanitize.isSanitizerEnabled(Asan) && sanitized.Address.Srcs != nil { return sanitized.Address.Srcs } - if Bool(sanitize.Properties.Sanitize.Hwaddress) && sanitized.Hwaddress.Srcs != nil { + if sanitize.isSanitizerEnabled(Hwasan) && sanitized.Hwaddress.Srcs != nil { return sanitized.Hwaddress.Srcs } return sanitized.None.Srcs diff --git a/cc/proto.go b/cc/proto.go index cf5ed04f2..27f37cbbd 100644 --- a/cc/proto.go +++ b/cc/proto.go @@ -205,13 +205,13 @@ func bp2buildProto(ctx android.Bp2buildMutatorContext, m *Module, protoSrcs baze protoAttrs.Deps.SetValue(protoInfo.Proto_libs) name := m.Name() + suffix - + tags := android.ApexAvailableTags(m) ctx.CreateBazelTargetModule( bazel.BazelTargetModuleProperties{ Rule_class: rule_class, Bzl_load_location: "//build/bazel/rules/cc:cc_proto.bzl", }, - android.CommonAttributes{Name: name}, + android.CommonAttributes{Name: name, Tags: tags}, &protoAttrs) var privateHdrs bool diff --git a/cc/sanitize.go b/cc/sanitize.go index d3fc221b4..eba709bc2 100644 --- a/cc/sanitize.go +++ b/cc/sanitize.go @@ -171,6 +171,20 @@ func (t SanitizerType) registerMutators(ctx android.RegisterMutatorsContext) { } } +// shouldPropagateToSharedLibraryDeps returns whether a sanitizer type should propagate to share +// dependencies. In most cases, sanitizers only propagate to static dependencies; however, some +// sanitizers also must be enabled for shared libraries for linking. +func (t SanitizerType) shouldPropagateToSharedLibraryDeps() bool { + switch t { + case Fuzzer: + // Typically, shared libs are not split. However, for fuzzer, we split even for shared libs + // because a library sanitized for fuzzer can't be linked from a library that isn't sanitized + // for fuzzer. + return true + default: + return false + } +} func (*Module) SanitizerSupported(t SanitizerType) bool { switch t { case Asan: @@ -286,15 +300,72 @@ type SanitizeUserProps struct { Blocklist *string } +type sanitizeMutatedProperties struct { + // Whether sanitizers can be enabled on this module + Never *bool `blueprint:"mutated"` + + // Whether ASan (Address sanitizer) is enabled for this module. + // Hwaddress sanitizer takes precedence over this sanitizer. + Address *bool `blueprint:"mutated"` + // Whether TSan (Thread sanitizer) is enabled for this module + Thread *bool `blueprint:"mutated"` + // Whether HWASan (Hardware Address sanitizer) is enabled for this module + Hwaddress *bool `blueprint:"mutated"` + + // Whether Undefined behavior sanitizer is enabled for this module + All_undefined *bool `blueprint:"mutated"` + // Whether undefined behavior sanitizer subset is enabled for this module + Undefined *bool `blueprint:"mutated"` + // List of specific undefined behavior sanitizers enabled for this module + Misc_undefined []string `blueprint:"mutated"` + // Whether Fuzzeris enabled for this module + Fuzzer *bool `blueprint:"mutated"` + // whether safe-stack sanitizer is enabled for this module + Safestack *bool `blueprint:"mutated"` + // Whether cfi sanitizer is enabled for this module + Cfi *bool `blueprint:"mutated"` + // Whether signed/unsigned integer overflow sanitizer is enabled for this module + Integer_overflow *bool `blueprint:"mutated"` + // Whether scudo sanitizer is enabled for this module + Scudo *bool `blueprint:"mutated"` + // Whether shadow-call-stack sanitizer is enabled for this module. + Scs *bool `blueprint:"mutated"` + // Whether Memory-tagging is enabled for this module + Memtag_heap *bool `blueprint:"mutated"` + // Whether Memory-tagging stack instrumentation is enabled for this module + Memtag_stack *bool `blueprint:"mutated"` + + // Whether a modifier for ASAN and HWASAN for write only instrumentation is enabled for this + // module + Writeonly *bool `blueprint:"mutated"` + + // Sanitizers to run in the diagnostic mode (as opposed to the release mode). + Diag struct { + // Whether Undefined behavior sanitizer, diagnostic mode is enabled for this module + Undefined *bool `blueprint:"mutated"` + // Whether cfi sanitizer, diagnostic mode is enabled for this module + Cfi *bool `blueprint:"mutated"` + // Whether signed/unsigned integer overflow sanitizer, diagnostic mode is enabled for this + // module + Integer_overflow *bool `blueprint:"mutated"` + // Whether Memory-tagging, diagnostic mode is enabled for this module + Memtag_heap *bool `blueprint:"mutated"` + // List of specific undefined behavior sanitizers enabled in diagnostic mode + Misc_undefined []string `blueprint:"mutated"` + } `blueprint:"mutated"` +} + type SanitizeProperties struct { - Sanitize SanitizeUserProps `android:"arch_variant"` - SanitizerEnabled bool `blueprint:"mutated"` - MinimalRuntimeDep bool `blueprint:"mutated"` - BuiltinsDep bool `blueprint:"mutated"` - UbsanRuntimeDep bool `blueprint:"mutated"` - InSanitizerDir bool `blueprint:"mutated"` - Sanitizers []string `blueprint:"mutated"` - DiagSanitizers []string `blueprint:"mutated"` + Sanitize SanitizeUserProps `android:"arch_variant"` + SanitizeMutated sanitizeMutatedProperties `blueprint:"mutated"` + + SanitizerEnabled bool `blueprint:"mutated"` + MinimalRuntimeDep bool `blueprint:"mutated"` + BuiltinsDep bool `blueprint:"mutated"` + UbsanRuntimeDep bool `blueprint:"mutated"` + InSanitizerDir bool `blueprint:"mutated"` + Sanitizers []string `blueprint:"mutated"` + DiagSanitizers []string `blueprint:"mutated"` } type sanitize struct { @@ -317,8 +388,42 @@ func (sanitize *sanitize) props() []interface{} { return []interface{}{&sanitize.Properties} } +func (p *sanitizeMutatedProperties) copyUserPropertiesToMutated(userProps *SanitizeUserProps) { + p.Never = userProps.Never + p.Address = userProps.Address + p.All_undefined = userProps.All_undefined + p.Cfi = userProps.Cfi + p.Fuzzer = userProps.Fuzzer + p.Hwaddress = userProps.Hwaddress + p.Integer_overflow = userProps.Integer_overflow + p.Memtag_heap = userProps.Memtag_heap + p.Memtag_stack = userProps.Memtag_stack + p.Safestack = userProps.Safestack + p.Scs = userProps.Scs + p.Scudo = userProps.Scudo + p.Thread = userProps.Thread + p.Undefined = userProps.Undefined + p.Writeonly = userProps.Writeonly + + p.Misc_undefined = make([]string, 0, len(userProps.Misc_undefined)) + for _, v := range userProps.Misc_undefined { + p.Misc_undefined = append(p.Misc_undefined, v) + } + + p.Diag.Cfi = userProps.Diag.Cfi + p.Diag.Integer_overflow = userProps.Diag.Integer_overflow + p.Diag.Memtag_heap = userProps.Diag.Memtag_heap + p.Diag.Undefined = userProps.Diag.Undefined + + p.Diag.Misc_undefined = make([]string, 0, len(userProps.Diag.Misc_undefined)) + for _, v := range userProps.Diag.Misc_undefined { + p.Diag.Misc_undefined = append(p.Diag.Misc_undefined, v) + } +} + func (sanitize *sanitize) begin(ctx BaseModuleContext) { - s := &sanitize.Properties.Sanitize + s := &sanitize.Properties.SanitizeMutated + s.copyUserPropertiesToMutated(&sanitize.Properties.Sanitize) // Don't apply sanitizers to NDK code. if ctx.useSdk() { @@ -614,20 +719,13 @@ func toDisableUnsignedShiftBaseChange(flags []string) bool { return false } -func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags { - if !sanitize.Properties.SanitizerEnabled && !sanitize.Properties.UbsanRuntimeDep { +func (s *sanitize) flags(ctx ModuleContext, flags Flags) Flags { + if !s.Properties.SanitizerEnabled && !s.Properties.UbsanRuntimeDep { return flags } + sanProps := &s.Properties.SanitizeMutated - // Currently unwinding through tagged frames for exceptions is broken, so disable memtag stack - // in that case, so we don't end up tagging those. - // TODO(b/174878242): Remove once https://r.android.com/2251926 is included in toolchain. - if android.InList("-fexceptions", flags.Local.CFlags) || android.InList("-fexceptions", flags.Global.CFlags) { - sanitize.Properties.Sanitize.Memtag_stack = nil - _, sanitize.Properties.Sanitizers = android.RemoveFromList("memtag-stack", sanitize.Properties.Sanitizers) - } - - if Bool(sanitize.Properties.Sanitize.Address) { + if Bool(sanProps.Address) { if ctx.Arch().ArchType == android.Arm { // Frame pointer based unwinder in ASan requires ARM frame setup. // TODO: put in flags? @@ -636,7 +734,7 @@ func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags { flags.Local.CFlags = append(flags.Local.CFlags, asanCflags...) flags.Local.LdFlags = append(flags.Local.LdFlags, asanLdflags...) - if Bool(sanitize.Properties.Sanitize.Writeonly) { + if Bool(sanProps.Writeonly) { flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-asan-instrument-reads=0") } @@ -657,7 +755,7 @@ func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags { } } - if Bool(sanitize.Properties.Sanitize.Hwaddress) { + if Bool(sanProps.Hwaddress) { flags.Local.CFlags = append(flags.Local.CFlags, hwasanCflags...) for _, flag := range hwasanCommonflags { @@ -667,12 +765,12 @@ func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags { flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,"+flag) } - if Bool(sanitize.Properties.Sanitize.Writeonly) { + if Bool(sanProps.Writeonly) { flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-hwasan-instrument-reads=0") } } - if Bool(sanitize.Properties.Sanitize.Fuzzer) { + if Bool(sanProps.Fuzzer) { flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize=fuzzer-no-link") // TODO(b/131771163): LTO and Fuzzer support is mutually incompatible. @@ -701,7 +799,7 @@ func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags { flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN`) } - if Bool(sanitize.Properties.Sanitize.Cfi) { + if Bool(sanProps.Cfi) { if ctx.Arch().ArchType == android.Arm { // __cfi_check needs to be built as Thumb (see the code in linker_cfi.cpp). LLVM is not set up // to do this on a function basis, so force Thumb on the entire module. @@ -710,7 +808,7 @@ func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags { flags.Local.CFlags = append(flags.Local.CFlags, cfiCflags...) flags.Local.AsFlags = append(flags.Local.AsFlags, cfiAsflags...) - if Bool(sanitize.Properties.Sanitize.Config.Cfi_assembly_support) { + if Bool(s.Properties.Sanitize.Config.Cfi_assembly_support) { flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-cfi-canonical-jump-tables") } // Only append the default visibility flag if -fvisibility has not already been set @@ -726,7 +824,7 @@ func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags { } } - if Bool(sanitize.Properties.Sanitize.Memtag_stack) { + if Bool(sanProps.Memtag_stack) { flags.Local.CFlags = append(flags.Local.CFlags, memtagStackCommonFlags...) // TODO(fmayer): remove -Wno-error once https://reviews.llvm.org/D127917 is in Android toolchain. flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-error=frame-larger-than") @@ -737,20 +835,20 @@ func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags { flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-fatal-warnings") } - if (Bool(sanitize.Properties.Sanitize.Memtag_heap) || Bool(sanitize.Properties.Sanitize.Memtag_stack)) && ctx.binary() { - if Bool(sanitize.Properties.Sanitize.Diag.Memtag_heap) { + if (Bool(sanProps.Memtag_heap) || Bool(sanProps.Memtag_stack)) && ctx.binary() { + if Bool(sanProps.Diag.Memtag_heap) { flags.Local.LdFlags = append(flags.Local.LdFlags, "-fsanitize-memtag-mode=sync") } else { flags.Local.LdFlags = append(flags.Local.LdFlags, "-fsanitize-memtag-mode=async") } } - if Bool(sanitize.Properties.Sanitize.Integer_overflow) { + if Bool(sanProps.Integer_overflow) { flags.Local.CFlags = append(flags.Local.CFlags, intOverflowCflags...) } - if len(sanitize.Properties.Sanitizers) > 0 { - sanitizeArg := "-fsanitize=" + strings.Join(sanitize.Properties.Sanitizers, ",") + if len(s.Properties.Sanitizers) > 0 { + sanitizeArg := "-fsanitize=" + strings.Join(s.Properties.Sanitizers, ",") flags.Local.CFlags = append(flags.Local.CFlags, sanitizeArg) flags.Local.AsFlags = append(flags.Local.AsFlags, sanitizeArg) flags.Local.LdFlags = append(flags.Local.LdFlags, sanitizeArg) @@ -774,7 +872,7 @@ func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags { flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=vptr,function") } - if Bool(sanitize.Properties.Sanitize.Fuzzer) { + if Bool(sanProps.Fuzzer) { // When fuzzing, we wish to crash with diagnostics on any bug. flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap=all", "-fno-sanitize-recover=all") } else if ctx.Host() { @@ -783,7 +881,7 @@ func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags { flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-trap=all", "-ftrap-function=abort") } - if enableMinimalRuntime(sanitize) { + if enableMinimalRuntime(s) { flags.Local.CFlags = append(flags.Local.CFlags, strings.Join(minimalRuntimeFlags, " ")) } @@ -797,22 +895,22 @@ func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags { } } - if len(sanitize.Properties.DiagSanitizers) > 0 { - flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap="+strings.Join(sanitize.Properties.DiagSanitizers, ",")) + if len(s.Properties.DiagSanitizers) > 0 { + flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap="+strings.Join(s.Properties.DiagSanitizers, ",")) } // FIXME: enable RTTI if diag + (cfi or vptr) - if sanitize.Properties.Sanitize.Recover != nil { + if s.Properties.Sanitize.Recover != nil { flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-recover="+ - strings.Join(sanitize.Properties.Sanitize.Recover, ",")) + strings.Join(s.Properties.Sanitize.Recover, ",")) } - if sanitize.Properties.Sanitize.Diag.No_recover != nil { + if s.Properties.Sanitize.Diag.No_recover != nil { flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-recover="+ - strings.Join(sanitize.Properties.Sanitize.Diag.No_recover, ",")) + strings.Join(s.Properties.Sanitize.Diag.No_recover, ",")) } - blocklist := android.OptionalPathForModuleSrc(ctx, sanitize.Properties.Sanitize.Blocklist) + blocklist := android.OptionalPathForModuleSrc(ctx, s.Properties.Sanitize.Blocklist) if blocklist.Valid() { flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-ignorelist="+blocklist.String()) flags.CFlagsDeps = append(flags.CFlagsDeps, blocklist.Path()) @@ -821,47 +919,47 @@ func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags { return flags } -func (sanitize *sanitize) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) { +func (s *sanitize) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) { // Add a suffix for cfi/hwasan/scs-enabled static/header libraries to allow surfacing // both the sanitized and non-sanitized variants to make without a name conflict. if entries.Class == "STATIC_LIBRARIES" || entries.Class == "HEADER_LIBRARIES" { - if Bool(sanitize.Properties.Sanitize.Cfi) { + if Bool(s.Properties.SanitizeMutated.Cfi) { entries.SubName += ".cfi" } - if Bool(sanitize.Properties.Sanitize.Hwaddress) { + if Bool(s.Properties.SanitizeMutated.Hwaddress) { entries.SubName += ".hwasan" } - if Bool(sanitize.Properties.Sanitize.Scs) { + if Bool(s.Properties.SanitizeMutated.Scs) { entries.SubName += ".scs" } } } -func (sanitize *sanitize) inSanitizerDir() bool { - return sanitize.Properties.InSanitizerDir +func (s *sanitize) inSanitizerDir() bool { + return s.Properties.InSanitizerDir } // getSanitizerBoolPtr returns the SanitizerTypes associated bool pointer from SanitizeProperties. -func (sanitize *sanitize) getSanitizerBoolPtr(t SanitizerType) *bool { +func (s *sanitize) getSanitizerBoolPtr(t SanitizerType) *bool { switch t { case Asan: - return sanitize.Properties.Sanitize.Address + return s.Properties.SanitizeMutated.Address case Hwasan: - return sanitize.Properties.Sanitize.Hwaddress + return s.Properties.SanitizeMutated.Hwaddress case tsan: - return sanitize.Properties.Sanitize.Thread + return s.Properties.SanitizeMutated.Thread case intOverflow: - return sanitize.Properties.Sanitize.Integer_overflow + return s.Properties.SanitizeMutated.Integer_overflow case cfi: - return sanitize.Properties.Sanitize.Cfi + return s.Properties.SanitizeMutated.Cfi case scs: - return sanitize.Properties.Sanitize.Scs + return s.Properties.SanitizeMutated.Scs case Memtag_heap: - return sanitize.Properties.Sanitize.Memtag_heap + return s.Properties.SanitizeMutated.Memtag_heap case Memtag_stack: - return sanitize.Properties.Sanitize.Memtag_stack + return s.Properties.SanitizeMutated.Memtag_stack case Fuzzer: - return sanitize.Properties.Sanitize.Fuzzer + return s.Properties.SanitizeMutated.Fuzzer default: panic(fmt.Errorf("unknown SanitizerType %d", t)) } @@ -894,28 +992,28 @@ func (sanitize *sanitize) SetSanitizer(t SanitizerType, b bool) { } switch t { case Asan: - sanitize.Properties.Sanitize.Address = bPtr + sanitize.Properties.SanitizeMutated.Address = bPtr // For ASAN variant, we need to disable Memtag_stack - sanitize.Properties.Sanitize.Memtag_stack = nil + sanitize.Properties.SanitizeMutated.Memtag_stack = nil case Hwasan: - sanitize.Properties.Sanitize.Hwaddress = bPtr + sanitize.Properties.SanitizeMutated.Hwaddress = bPtr // For HWAsan variant, we need to disable Memtag_stack - sanitize.Properties.Sanitize.Memtag_stack = nil + sanitize.Properties.SanitizeMutated.Memtag_stack = nil case tsan: - sanitize.Properties.Sanitize.Thread = bPtr + sanitize.Properties.SanitizeMutated.Thread = bPtr case intOverflow: - sanitize.Properties.Sanitize.Integer_overflow = bPtr + sanitize.Properties.SanitizeMutated.Integer_overflow = bPtr case cfi: - sanitize.Properties.Sanitize.Cfi = bPtr + sanitize.Properties.SanitizeMutated.Cfi = bPtr case scs: - sanitize.Properties.Sanitize.Scs = bPtr + sanitize.Properties.SanitizeMutated.Scs = bPtr case Memtag_heap: - sanitize.Properties.Sanitize.Memtag_heap = bPtr + sanitize.Properties.SanitizeMutated.Memtag_heap = bPtr case Memtag_stack: - sanitize.Properties.Sanitize.Memtag_stack = bPtr + sanitize.Properties.SanitizeMutated.Memtag_stack = bPtr // We do not need to disable ASAN or HWASan here, as there is no Memtag_stack variant. case Fuzzer: - sanitize.Properties.Sanitize.Fuzzer = bPtr + sanitize.Properties.SanitizeMutated.Fuzzer = bPtr default: panic(fmt.Errorf("unknown SanitizerType %d", t)) } @@ -1065,7 +1163,7 @@ func (s *sanitizerSplitMutator) Split(ctx android.BaseModuleContext) []string { //TODO: When Rust modules have vendor support, enable this path for PlatformSanitizeable // Check if it's a snapshot module supporting sanitizer - if ss, ok := c.linker.(snapshotSanitizer); ok && ss.isSanitizerEnabled(s.sanitizer) { + if ss, ok := c.linker.(snapshotSanitizer); ok && ss.isSanitizerAvailable(s.sanitizer) { return []string{"", s.sanitizer.variationName()} } else { return []string{""} @@ -1097,7 +1195,7 @@ func (s *sanitizerSplitMutator) OutgoingTransition(ctx android.OutgoingTransitio func (s *sanitizerSplitMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string { if d, ok := ctx.Module().(PlatformSanitizeable); ok { if dm, ok := ctx.Module().(*Module); ok { - if ss, ok := dm.linker.(snapshotSanitizer); ok && ss.isSanitizerEnabled(s.sanitizer) { + if ss, ok := dm.linker.(snapshotSanitizer); ok && ss.isSanitizerAvailable(s.sanitizer) { return incomingVariation } } @@ -1127,7 +1225,8 @@ func (s *sanitizerSplitMutator) IncomingTransition(ctx android.IncomingTransitio return s.sanitizer.variationName() } - if s.sanitizer == cfi || s.sanitizer == Hwasan || s.sanitizer == scs || s.sanitizer == Asan { + // Some sanitizers do not propagate to shared dependencies + if !s.sanitizer.shouldPropagateToSharedLibraryDeps() { return "" } } @@ -1212,14 +1311,23 @@ func (s *sanitizerSplitMutator) Mutate(mctx android.BottomUpMutatorContext, vari sanitizeable.AddSanitizerDependencies(mctx, s.sanitizer.name()) } } else if c, ok := mctx.Module().(*Module); ok { - if ss, ok := c.linker.(snapshotSanitizer); ok && ss.isSanitizerEnabled(s.sanitizer) { + if ss, ok := c.linker.(snapshotSanitizer); ok && ss.isSanitizerAvailable(s.sanitizer) { + if !ss.isUnsanitizedVariant() { + // Snapshot sanitizer may have only one variantion. + // Skip exporting the module if it already has a sanitizer variation. + c.SetPreventInstall() + c.SetHideFromMake() + return + } c.linker.(snapshotSanitizer).setSanitizerVariation(s.sanitizer, sanitizerVariation) // Export the static lib name to make if c.static() && c.ExportedToMake() { + // use BaseModuleName which is the name for Make. if s.sanitizer == cfi { - // use BaseModuleName which is the name for Make. cfiStaticLibs(mctx.Config()).add(c, c.BaseModuleName()) + } else if s.sanitizer == Hwasan { + hwasanStaticLibs(mctx.Config()).add(c, c.BaseModuleName()) } } } @@ -1227,7 +1335,7 @@ func (s *sanitizerSplitMutator) Mutate(mctx android.BottomUpMutatorContext, vari } func (c *Module) SanitizeNever() bool { - return Bool(c.sanitize.Properties.Sanitize.Never) + return Bool(c.sanitize.Properties.SanitizeMutated.Never) } func (c *Module) IsSanitizerExplicitlyDisabled(t SanitizerType) bool { @@ -1295,10 +1403,12 @@ func sanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) { var sanitizers []string var diagSanitizers []string - if Bool(c.sanitize.Properties.Sanitize.All_undefined) { + sanProps := &c.sanitize.Properties.SanitizeMutated + + if Bool(sanProps.All_undefined) { sanitizers = append(sanitizers, "undefined") } else { - if Bool(c.sanitize.Properties.Sanitize.Undefined) { + if Bool(sanProps.Undefined) { sanitizers = append(sanitizers, "bool", "integer-divide-by-zero", @@ -1323,66 +1433,66 @@ func sanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) { // "object-size", ) } - sanitizers = append(sanitizers, c.sanitize.Properties.Sanitize.Misc_undefined...) + sanitizers = append(sanitizers, sanProps.Misc_undefined...) } - if Bool(c.sanitize.Properties.Sanitize.Diag.Undefined) { + if Bool(sanProps.Diag.Undefined) { diagSanitizers = append(diagSanitizers, "undefined") } - diagSanitizers = append(diagSanitizers, c.sanitize.Properties.Sanitize.Diag.Misc_undefined...) + diagSanitizers = append(diagSanitizers, sanProps.Diag.Misc_undefined...) - if Bool(c.sanitize.Properties.Sanitize.Address) { + if Bool(sanProps.Address) { sanitizers = append(sanitizers, "address") diagSanitizers = append(diagSanitizers, "address") } - if Bool(c.sanitize.Properties.Sanitize.Hwaddress) { + if Bool(sanProps.Hwaddress) { sanitizers = append(sanitizers, "hwaddress") } - if Bool(c.sanitize.Properties.Sanitize.Thread) { + if Bool(sanProps.Thread) { sanitizers = append(sanitizers, "thread") } - if Bool(c.sanitize.Properties.Sanitize.Safestack) { + if Bool(sanProps.Safestack) { sanitizers = append(sanitizers, "safe-stack") } - if Bool(c.sanitize.Properties.Sanitize.Cfi) { + if Bool(sanProps.Cfi) { sanitizers = append(sanitizers, "cfi") - if Bool(c.sanitize.Properties.Sanitize.Diag.Cfi) { + if Bool(sanProps.Diag.Cfi) { diagSanitizers = append(diagSanitizers, "cfi") } } - if Bool(c.sanitize.Properties.Sanitize.Integer_overflow) { + if Bool(sanProps.Integer_overflow) { sanitizers = append(sanitizers, "unsigned-integer-overflow") sanitizers = append(sanitizers, "signed-integer-overflow") - if Bool(c.sanitize.Properties.Sanitize.Diag.Integer_overflow) { + if Bool(sanProps.Diag.Integer_overflow) { diagSanitizers = append(diagSanitizers, "unsigned-integer-overflow") diagSanitizers = append(diagSanitizers, "signed-integer-overflow") } } - if Bool(c.sanitize.Properties.Sanitize.Scudo) { + if Bool(sanProps.Scudo) { sanitizers = append(sanitizers, "scudo") } - if Bool(c.sanitize.Properties.Sanitize.Scs) { + if Bool(sanProps.Scs) { sanitizers = append(sanitizers, "shadow-call-stack") } - if Bool(c.sanitize.Properties.Sanitize.Memtag_heap) && c.Binary() { + if Bool(sanProps.Memtag_heap) && c.Binary() { sanitizers = append(sanitizers, "memtag-heap") } - if Bool(c.sanitize.Properties.Sanitize.Memtag_stack) { + if Bool(sanProps.Memtag_stack) { sanitizers = append(sanitizers, "memtag-stack") } - if Bool(c.sanitize.Properties.Sanitize.Fuzzer) { + if Bool(sanProps.Fuzzer) { sanitizers = append(sanitizers, "fuzzer-no-link") } @@ -1401,27 +1511,27 @@ func sanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) { alwaysStaticRuntime := false var extraStaticDeps []string toolchain := c.toolchain(mctx) - if Bool(c.sanitize.Properties.Sanitize.Address) { + if Bool(sanProps.Address) { runtimeLibrary = config.AddressSanitizerRuntimeLibrary(toolchain) - } else if Bool(c.sanitize.Properties.Sanitize.Hwaddress) { + } else if Bool(sanProps.Hwaddress) { if c.staticBinary() { runtimeLibrary = config.HWAddressSanitizerStaticLibrary(toolchain) extraStaticDeps = []string{"libdl"} } else { runtimeLibrary = config.HWAddressSanitizerRuntimeLibrary(toolchain) } - } else if Bool(c.sanitize.Properties.Sanitize.Thread) { + } else if Bool(sanProps.Thread) { runtimeLibrary = config.ThreadSanitizerRuntimeLibrary(toolchain) - } else if Bool(c.sanitize.Properties.Sanitize.Scudo) { + } else if Bool(sanProps.Scudo) { if len(diagSanitizers) == 0 && !c.sanitize.Properties.UbsanRuntimeDep { runtimeLibrary = config.ScudoMinimalRuntimeLibrary(toolchain) } else { runtimeLibrary = config.ScudoRuntimeLibrary(toolchain) } } else if len(diagSanitizers) > 0 || c.sanitize.Properties.UbsanRuntimeDep || - Bool(c.sanitize.Properties.Sanitize.Fuzzer) || - Bool(c.sanitize.Properties.Sanitize.Undefined) || - Bool(c.sanitize.Properties.Sanitize.All_undefined) { + Bool(sanProps.Fuzzer) || + Bool(sanProps.Undefined) || + Bool(sanProps.All_undefined) { runtimeLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain) if c.staticBinary() || toolchain.Musl() { // Use a static runtime for static binaries. @@ -1632,21 +1742,27 @@ func hwasanStaticLibs(config android.Config) *sanitizerStaticLibsMap { } func enableMinimalRuntime(sanitize *sanitize) bool { - if !Bool(sanitize.Properties.Sanitize.Address) && - !Bool(sanitize.Properties.Sanitize.Hwaddress) && - !Bool(sanitize.Properties.Sanitize.Fuzzer) && - (Bool(sanitize.Properties.Sanitize.Integer_overflow) || - len(sanitize.Properties.Sanitize.Misc_undefined) > 0 || - Bool(sanitize.Properties.Sanitize.Undefined) || - Bool(sanitize.Properties.Sanitize.All_undefined)) && - !(Bool(sanitize.Properties.Sanitize.Diag.Integer_overflow) || - Bool(sanitize.Properties.Sanitize.Diag.Cfi) || - Bool(sanitize.Properties.Sanitize.Diag.Undefined) || - len(sanitize.Properties.Sanitize.Diag.Misc_undefined) > 0) { + if sanitize.isSanitizerEnabled(Asan) { + return false + } else if sanitize.isSanitizerEnabled(Hwasan) { + return false + } else if sanitize.isSanitizerEnabled(Fuzzer) { + return false + } - return true + if enableUbsanRuntime(sanitize) { + return false } - return false + + sanitizeProps := &sanitize.Properties.SanitizeMutated + if Bool(sanitizeProps.Diag.Cfi) { + return false + } + + return Bool(sanitizeProps.Integer_overflow) || + len(sanitizeProps.Misc_undefined) > 0 || + Bool(sanitizeProps.Undefined) || + Bool(sanitizeProps.All_undefined) } func (m *Module) UbsanRuntimeNeeded() bool { @@ -1658,9 +1774,10 @@ func (m *Module) MinimalRuntimeNeeded() bool { } func enableUbsanRuntime(sanitize *sanitize) bool { - return Bool(sanitize.Properties.Sanitize.Diag.Integer_overflow) || - Bool(sanitize.Properties.Sanitize.Diag.Undefined) || - len(sanitize.Properties.Sanitize.Diag.Misc_undefined) > 0 + sanitizeProps := &sanitize.Properties.SanitizeMutated + return Bool(sanitizeProps.Diag.Integer_overflow) || + Bool(sanitizeProps.Diag.Undefined) || + len(sanitizeProps.Diag.Misc_undefined) > 0 } func cfiMakeVarsProvider(ctx android.MakeVarsContext) { diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go index 2393f3e18..580adfac7 100644 --- a/cc/sanitize_test.go +++ b/cc/sanitize_test.go @@ -21,6 +21,8 @@ import ( "testing" "android/soong/android" + + "github.com/google/blueprint" ) var prepareForAsanTest = android.FixtureAddFile("asan/Android.bp", []byte(` @@ -29,6 +31,60 @@ var prepareForAsanTest = android.FixtureAddFile("asan/Android.bp", []byte(` } `)) +var prepareForTsanTest = android.FixtureAddFile("tsan/Android.bp", []byte(` + cc_library_shared { + name: "libclang_rt.tsan", + } +`)) + +type providerInterface interface { + ModuleProvider(blueprint.Module, blueprint.ProviderKey) interface{} +} + +// expectSharedLinkDep verifies that the from module links against the to module as a +// shared library. +func expectSharedLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) { + t.Helper() + fromLink := from.Description("link") + toInfo := ctx.ModuleProvider(to.Module(), SharedLibraryInfoProvider).(SharedLibraryInfo) + + if g, w := fromLink.OrderOnly.Strings(), toInfo.SharedLibrary.RelativeToTop().String(); !android.InList(w, g) { + t.Errorf("%s should link against %s, expected %q, got %q", + from.Module(), to.Module(), w, g) + } +} + +// expectStaticLinkDep verifies that the from module links against the to module as a +// static library. +func expectStaticLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) { + t.Helper() + fromLink := from.Description("link") + toInfo := ctx.ModuleProvider(to.Module(), StaticLibraryInfoProvider).(StaticLibraryInfo) + + if g, w := fromLink.Implicits.Strings(), toInfo.StaticLibrary.RelativeToTop().String(); !android.InList(w, g) { + t.Errorf("%s should link against %s, expected %q, got %q", + from.Module(), to.Module(), w, g) + } + +} + +// expectInstallDep verifies that the install rule of the from module depends on the +// install rule of the to module. +func expectInstallDep(t *testing.T, from, to android.TestingModule) { + t.Helper() + fromInstalled := from.Description("install") + toInstalled := to.Description("install") + + // combine implicits and order-only dependencies, host uses implicit but device uses + // order-only. + got := append(fromInstalled.Implicits.Strings(), fromInstalled.OrderOnly.Strings()...) + want := toInstalled.Output.String() + if !android.InList(want, got) { + t.Errorf("%s installation should depend on %s, expected %q, got %q", + from.Module(), to.Module(), want, got) + } +} + func TestAsan(t *testing.T) { bp := ` cc_binary { @@ -111,6 +167,7 @@ func TestAsan(t *testing.T) { ).RunTestWithBp(t, bp) check := func(t *testing.T, result *android.TestResult, variant string) { + ctx := result.TestContext asanVariant := variant + "_asan" sharedVariant := variant + "_shared" sharedAsanVariant := sharedVariant + "_asan" @@ -140,82 +197,352 @@ func TestAsan(t *testing.T) { libStaticAsan := result.ModuleForTests("libstatic_asan", staticAsanVariant) libStaticAsanNoAsanVariant := result.ModuleForTests("libstatic_asan", staticVariant) - // expectSharedLinkDep verifies that the from module links against the to module as a - // shared library. - expectSharedLinkDep := func(from, to android.TestingModule) { - t.Helper() - fromLink := from.Description("link") - toLink := to.Description("strip") + expectSharedLinkDep(t, ctx, binWithAsan, libShared) + expectSharedLinkDep(t, ctx, binWithAsan, libAsan) + expectSharedLinkDep(t, ctx, libShared, libTransitive) + expectSharedLinkDep(t, ctx, libAsan, libTransitive) + + expectStaticLinkDep(t, ctx, binWithAsan, libStaticAsanVariant) + expectStaticLinkDep(t, ctx, binWithAsan, libNoAsan) + expectStaticLinkDep(t, ctx, binWithAsan, libStaticAsan) + + expectInstallDep(t, binWithAsan, libShared) + expectInstallDep(t, binWithAsan, libAsan) + expectInstallDep(t, binWithAsan, libTransitive) + expectInstallDep(t, libShared, libTransitive) + expectInstallDep(t, libAsan, libTransitive) + + expectSharedLinkDep(t, ctx, binNoAsan, libShared) + expectSharedLinkDep(t, ctx, binNoAsan, libAsan) + expectSharedLinkDep(t, ctx, libShared, libTransitive) + expectSharedLinkDep(t, ctx, libAsan, libTransitive) + + expectStaticLinkDep(t, ctx, binNoAsan, libStaticNoAsanVariant) + expectStaticLinkDep(t, ctx, binNoAsan, libNoAsan) + expectStaticLinkDep(t, ctx, binNoAsan, libStaticAsanNoAsanVariant) + + expectInstallDep(t, binNoAsan, libShared) + expectInstallDep(t, binNoAsan, libAsan) + expectInstallDep(t, binNoAsan, libTransitive) + expectInstallDep(t, libShared, libTransitive) + expectInstallDep(t, libAsan, libTransitive) + } + + t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) }) + t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") }) +} + +func TestTsan(t *testing.T) { + bp := ` + cc_binary { + name: "bin_with_tsan", + host_supported: true, + shared_libs: [ + "libshared", + "libtsan", + ], + sanitize: { + thread: true, + } + } + + cc_binary { + name: "bin_no_tsan", + host_supported: true, + shared_libs: [ + "libshared", + "libtsan", + ], + } + + cc_library_shared { + name: "libshared", + host_supported: true, + shared_libs: ["libtransitive"], + } + + cc_library_shared { + name: "libtsan", + host_supported: true, + shared_libs: ["libtransitive"], + sanitize: { + thread: true, + } + } + + cc_library_shared { + name: "libtransitive", + host_supported: true, + } +` + + result := android.GroupFixturePreparers( + prepareForCcTest, + prepareForTsanTest, + ).RunTestWithBp(t, bp) + + check := func(t *testing.T, result *android.TestResult, variant string) { + ctx := result.TestContext + tsanVariant := variant + "_tsan" + sharedVariant := variant + "_shared" + sharedTsanVariant := sharedVariant + "_tsan" + + // The binaries, one with tsan and one without + binWithTsan := result.ModuleForTests("bin_with_tsan", tsanVariant) + binNoTsan := result.ModuleForTests("bin_no_tsan", variant) + + // Shared libraries that don't request tsan + libShared := result.ModuleForTests("libshared", sharedVariant) + libTransitive := result.ModuleForTests("libtransitive", sharedVariant) + + // Shared library that requests tsan + libTsan := result.ModuleForTests("libtsan", sharedTsanVariant) + + expectSharedLinkDep(t, ctx, binWithTsan, libShared) + expectSharedLinkDep(t, ctx, binWithTsan, libTsan) + expectSharedLinkDep(t, ctx, libShared, libTransitive) + expectSharedLinkDep(t, ctx, libTsan, libTransitive) + + expectSharedLinkDep(t, ctx, binNoTsan, libShared) + expectSharedLinkDep(t, ctx, binNoTsan, libTsan) + expectSharedLinkDep(t, ctx, libShared, libTransitive) + expectSharedLinkDep(t, ctx, libTsan, libTransitive) + } + + t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) }) + t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") }) +} + +func TestMiscUndefined(t *testing.T) { + bp := ` + cc_binary { + name: "bin_with_ubsan", + srcs: ["src.cc"], + host_supported: true, + static_libs: [ + "libstatic", + "libubsan", + ], + sanitize: { + misc_undefined: ["integer"], + } + } + + cc_binary { + name: "bin_no_ubsan", + host_supported: true, + srcs: ["src.cc"], + static_libs: [ + "libstatic", + "libubsan", + ], + } + + cc_library_static { + name: "libstatic", + host_supported: true, + srcs: ["src.cc"], + static_libs: ["libtransitive"], + } + + cc_library_static { + name: "libubsan", + host_supported: true, + srcs: ["src.cc"], + whole_static_libs: ["libtransitive"], + sanitize: { + misc_undefined: ["integer"], + } + } + + cc_library_static { + name: "libtransitive", + host_supported: true, + srcs: ["src.cc"], + } +` + + result := android.GroupFixturePreparers( + prepareForCcTest, + ).RunTestWithBp(t, bp) + + check := func(t *testing.T, result *android.TestResult, variant string) { + ctx := result.TestContext + staticVariant := variant + "_static" - if g, w := fromLink.OrderOnly.Strings(), toLink.Output.String(); !android.InList(w, g) { - t.Errorf("%s should link against %s, expected %q, got %q", - from.Module(), to.Module(), w, g) + // The binaries, one with ubsan and one without + binWithUbsan := result.ModuleForTests("bin_with_ubsan", variant) + binNoUbsan := result.ModuleForTests("bin_no_ubsan", variant) + + // Static libraries that don't request ubsan + libStatic := result.ModuleForTests("libstatic", staticVariant) + libTransitive := result.ModuleForTests("libtransitive", staticVariant) + + libUbsan := result.ModuleForTests("libubsan", staticVariant) + + libUbsanMinimal := result.ModuleForTests("libclang_rt.ubsan_minimal", staticVariant) + + expectStaticLinkDep(t, ctx, binWithUbsan, libStatic) + expectStaticLinkDep(t, ctx, binWithUbsan, libUbsan) + expectStaticLinkDep(t, ctx, binWithUbsan, libUbsanMinimal) + + miscUndefinedSanFlag := "-fsanitize=integer" + binWithUbsanCflags := binWithUbsan.Rule("cc").Args["cFlags"] + if !strings.Contains(binWithUbsanCflags, miscUndefinedSanFlag) { + t.Errorf("'bin_with_ubsan' Expected %q to be in flags %q, was not", miscUndefinedSanFlag, binWithUbsanCflags) + } + libStaticCflags := libStatic.Rule("cc").Args["cFlags"] + if strings.Contains(libStaticCflags, miscUndefinedSanFlag) { + t.Errorf("'libstatic' Expected %q to NOT be in flags %q, was", miscUndefinedSanFlag, binWithUbsanCflags) + } + libUbsanCflags := libUbsan.Rule("cc").Args["cFlags"] + if !strings.Contains(libUbsanCflags, miscUndefinedSanFlag) { + t.Errorf("'libubsan' Expected %q to be in flags %q, was not", miscUndefinedSanFlag, binWithUbsanCflags) + } + libTransitiveCflags := libTransitive.Rule("cc").Args["cFlags"] + if strings.Contains(libTransitiveCflags, miscUndefinedSanFlag) { + t.Errorf("'libtransitive': Expected %q to NOT be in flags %q, was", miscUndefinedSanFlag, binWithUbsanCflags) + } + + expectStaticLinkDep(t, ctx, binNoUbsan, libStatic) + expectStaticLinkDep(t, ctx, binNoUbsan, libUbsan) + } + + t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) }) + t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") }) +} + +func TestFuzz(t *testing.T) { + bp := ` + cc_binary { + name: "bin_with_fuzzer", + host_supported: true, + shared_libs: [ + "libshared", + "libfuzzer", + ], + static_libs: [ + "libstatic", + "libnofuzzer", + "libstatic_fuzzer", + ], + sanitize: { + fuzzer: true, } } - // expectStaticLinkDep verifies that the from module links against the to module as a - // static library. - expectStaticLinkDep := func(from, to android.TestingModule) { - t.Helper() - fromLink := from.Description("link") - toLink := to.Description("static link") + cc_binary { + name: "bin_no_fuzzer", + host_supported: true, + shared_libs: [ + "libshared", + "libfuzzer", + ], + static_libs: [ + "libstatic", + "libnofuzzer", + "libstatic_fuzzer", + ], + } - if g, w := fromLink.Implicits.Strings(), toLink.Output.String(); !android.InList(w, g) { - t.Errorf("%s should link against %s, expected %q, got %q", - from.Module(), to.Module(), w, g) + cc_library_shared { + name: "libshared", + host_supported: true, + shared_libs: ["libtransitive"], + } + + cc_library_shared { + name: "libfuzzer", + host_supported: true, + shared_libs: ["libtransitive"], + sanitize: { + fuzzer: true, } + } + cc_library_shared { + name: "libtransitive", + host_supported: true, } - // expectInstallDep verifies that the install rule of the from module depends on the - // install rule of the to module. - expectInstallDep := func(from, to android.TestingModule) { - t.Helper() - fromInstalled := from.Description("install") - toInstalled := to.Description("install") + cc_library_static { + name: "libstatic", + host_supported: true, + } - // combine implicits and order-only dependencies, host uses implicit but device uses - // order-only. - got := append(fromInstalled.Implicits.Strings(), fromInstalled.OrderOnly.Strings()...) - want := toInstalled.Output.String() - if !android.InList(want, got) { - t.Errorf("%s installation should depend on %s, expected %q, got %q", - from.Module(), to.Module(), want, got) + cc_library_static { + name: "libnofuzzer", + host_supported: true, + sanitize: { + fuzzer: false, } } - expectSharedLinkDep(binWithAsan, libShared) - expectSharedLinkDep(binWithAsan, libAsan) - expectSharedLinkDep(libShared, libTransitive) - expectSharedLinkDep(libAsan, libTransitive) + cc_library_static { + name: "libstatic_fuzzer", + host_supported: true, + } + + ` + + result := android.GroupFixturePreparers( + prepareForCcTest, + ).RunTestWithBp(t, bp) + + check := func(t *testing.T, result *android.TestResult, variant string) { + ctx := result.TestContext + fuzzerVariant := variant + "_fuzzer" + sharedVariant := variant + "_shared" + sharedFuzzerVariant := sharedVariant + "_fuzzer" + staticVariant := variant + "_static" + staticFuzzerVariant := staticVariant + "_fuzzer" + + // The binaries, one with fuzzer and one without + binWithFuzzer := result.ModuleForTests("bin_with_fuzzer", fuzzerVariant) + binNoFuzzer := result.ModuleForTests("bin_no_fuzzer", variant) + + // Shared libraries that don't request fuzzer + libShared := result.ModuleForTests("libshared", sharedVariant) + libTransitive := result.ModuleForTests("libtransitive", sharedVariant) + + // Shared libraries that don't request fuzzer + libSharedFuzzer := result.ModuleForTests("libshared", sharedFuzzerVariant) + libTransitiveFuzzer := result.ModuleForTests("libtransitive", sharedFuzzerVariant) + + // Shared library that requests fuzzer + libFuzzer := result.ModuleForTests("libfuzzer", sharedFuzzerVariant) + + // Static library that uses an fuzzer variant for bin_with_fuzzer and a non-fuzzer variant + // for bin_no_fuzzer. + libStaticFuzzerVariant := result.ModuleForTests("libstatic", staticFuzzerVariant) + libStaticNoFuzzerVariant := result.ModuleForTests("libstatic", staticVariant) - expectStaticLinkDep(binWithAsan, libStaticAsanVariant) - expectStaticLinkDep(binWithAsan, libNoAsan) - expectStaticLinkDep(binWithAsan, libStaticAsan) + // Static library that never uses fuzzer. + libNoFuzzer := result.ModuleForTests("libnofuzzer", staticVariant) - expectInstallDep(binWithAsan, libShared) - expectInstallDep(binWithAsan, libAsan) - expectInstallDep(binWithAsan, libTransitive) - expectInstallDep(libShared, libTransitive) - expectInstallDep(libAsan, libTransitive) + // Static library that specifies fuzzer + libStaticFuzzer := result.ModuleForTests("libstatic_fuzzer", staticFuzzerVariant) + libStaticFuzzerNoFuzzerVariant := result.ModuleForTests("libstatic_fuzzer", staticVariant) - expectSharedLinkDep(binNoAsan, libShared) - expectSharedLinkDep(binNoAsan, libAsan) - expectSharedLinkDep(libShared, libTransitive) - expectSharedLinkDep(libAsan, libTransitive) + expectSharedLinkDep(t, ctx, binWithFuzzer, libSharedFuzzer) + expectSharedLinkDep(t, ctx, binWithFuzzer, libFuzzer) + expectSharedLinkDep(t, ctx, libSharedFuzzer, libTransitiveFuzzer) + expectSharedLinkDep(t, ctx, libFuzzer, libTransitiveFuzzer) - expectStaticLinkDep(binNoAsan, libStaticNoAsanVariant) - expectStaticLinkDep(binNoAsan, libNoAsan) - expectStaticLinkDep(binNoAsan, libStaticAsanNoAsanVariant) + expectStaticLinkDep(t, ctx, binWithFuzzer, libStaticFuzzerVariant) + expectStaticLinkDep(t, ctx, binWithFuzzer, libNoFuzzer) + expectStaticLinkDep(t, ctx, binWithFuzzer, libStaticFuzzer) - expectInstallDep(binNoAsan, libShared) - expectInstallDep(binNoAsan, libAsan) - expectInstallDep(binNoAsan, libTransitive) - expectInstallDep(libShared, libTransitive) - expectInstallDep(libAsan, libTransitive) + expectSharedLinkDep(t, ctx, binNoFuzzer, libShared) + expectSharedLinkDep(t, ctx, binNoFuzzer, libFuzzer) + expectSharedLinkDep(t, ctx, libShared, libTransitive) + expectSharedLinkDep(t, ctx, libFuzzer, libTransitiveFuzzer) + + expectStaticLinkDep(t, ctx, binNoFuzzer, libStaticNoFuzzerVariant) + expectStaticLinkDep(t, ctx, binNoFuzzer, libNoFuzzer) + expectStaticLinkDep(t, ctx, binNoFuzzer, libStaticFuzzerNoFuzzerVariant) } - t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) }) t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") }) } diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go index 792ffe364..570300b91 100644 --- a/cc/snapshot_prebuilt.go +++ b/cc/snapshot_prebuilt.go @@ -18,6 +18,7 @@ package cc // snapshot mutators and snapshot information maps which are also defined in this file. import ( + "fmt" "strings" "android/soong/android" @@ -399,8 +400,10 @@ type SnapshotLibraryProperties struct { } type snapshotSanitizer interface { - isSanitizerEnabled(t SanitizerType) bool + isSanitizerAvailable(t SanitizerType) bool setSanitizerVariation(t SanitizerType, enabled bool) + isSanitizerEnabled(t SanitizerType) bool + isUnsanitizedVariant() bool } type snapshotLibraryDecorator struct { @@ -408,10 +411,13 @@ type snapshotLibraryDecorator struct { *libraryDecorator properties SnapshotLibraryProperties sanitizerProperties struct { - CfiEnabled bool `blueprint:"mutated"` + SanitizerVariation SanitizerType `blueprint:"mutated"` // Library flags for cfi variant. Cfi SnapshotLibraryProperties `android:"arch_variant"` + + // Library flags for hwasan variant. + Hwasan SnapshotLibraryProperties `android:"arch_variant"` } } @@ -450,8 +456,10 @@ func (p *snapshotLibraryDecorator) link(ctx ModuleContext, flags Flags, deps Pat return p.libraryDecorator.link(ctx, flags, deps, objs) } - if p.sanitizerProperties.CfiEnabled { + if p.isSanitizerEnabled(cfi) { p.properties = p.sanitizerProperties.Cfi + } else if p.isSanitizerEnabled(Hwasan) { + p.properties = p.sanitizerProperties.Hwasan } if !p.MatchesWithDevice(ctx.DeviceConfig()) { @@ -514,25 +522,34 @@ func (p *snapshotLibraryDecorator) nativeCoverage() bool { return false } -func (p *snapshotLibraryDecorator) isSanitizerEnabled(t SanitizerType) bool { +func (p *snapshotLibraryDecorator) isSanitizerAvailable(t SanitizerType) bool { switch t { case cfi: return p.sanitizerProperties.Cfi.Src != nil + case Hwasan: + return p.sanitizerProperties.Hwasan.Src != nil default: return false } } func (p *snapshotLibraryDecorator) setSanitizerVariation(t SanitizerType, enabled bool) { - if !enabled { + if !enabled || p.isSanitizerEnabled(t) { return } - switch t { - case cfi: - p.sanitizerProperties.CfiEnabled = true - default: - return + if !p.isUnsanitizedVariant() { + panic(fmt.Errorf("snapshot Sanitizer must be one of Cfi or Hwasan but not both")) } + p.sanitizerProperties.SanitizerVariation = t +} + +func (p *snapshotLibraryDecorator) isSanitizerEnabled(t SanitizerType) bool { + return p.sanitizerProperties.SanitizerVariation == t +} + +func (p *snapshotLibraryDecorator) isUnsanitizedVariant() bool { + return !p.isSanitizerEnabled(Asan) && + !p.isSanitizerEnabled(Hwasan) } func snapshotLibraryFactory(image SnapshotImage, moduleSuffix string) (*Module, *snapshotLibraryDecorator) { diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go index 6a9877885..79405e9c4 100644 --- a/cc/vendor_snapshot_test.go +++ b/cc/vendor_snapshot_test.go @@ -1053,6 +1053,7 @@ func TestVendorSnapshotSanitizer(t *testing.T) { }, }, } + vendor_snapshot_static { name: "libsnapshot", vendor: true, @@ -1063,7 +1064,10 @@ func TestVendorSnapshotSanitizer(t *testing.T) { src: "libsnapshot.a", cfi: { src: "libsnapshot.cfi.a", - } + }, + hwasan: { + src: "libsnapshot.hwasan.a", + }, }, }, } @@ -1098,6 +1102,7 @@ func TestVendorSnapshotSanitizer(t *testing.T) { "vendor/libc++demangle.a": nil, "vendor/libsnapshot.a": nil, "vendor/libsnapshot.cfi.a": nil, + "vendor/libsnapshot.hwasan.a": nil, "vendor/note_memtag_heap_sync.a": nil, } @@ -1106,15 +1111,25 @@ func TestVendorSnapshotSanitizer(t *testing.T) { config.TestProductVariables.Platform_vndk_version = StringPtr("29") ctx := testCcWithConfig(t, config) - // Check non-cfi and cfi variant. + // Check non-cfi, cfi and hwasan variant. staticVariant := "android_vendor.28_arm64_armv8-a_static" staticCfiVariant := "android_vendor.28_arm64_armv8-a_static_cfi" + staticHwasanVariant := "android_vendor.28_arm64_armv8-a_static_hwasan" + staticHwasanCfiVariant := "android_vendor.28_arm64_armv8-a_static_hwasan_cfi" staticModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticVariant).Module().(*Module) assertString(t, staticModule.outputFile.Path().Base(), "libsnapshot.a") staticCfiModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticCfiVariant).Module().(*Module) assertString(t, staticCfiModule.outputFile.Path().Base(), "libsnapshot.cfi.a") + + staticHwasanModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticHwasanVariant).Module().(*Module) + assertString(t, staticHwasanModule.outputFile.Path().Base(), "libsnapshot.hwasan.a") + + staticHwasanCfiModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticHwasanCfiVariant).Module().(*Module) + if !staticHwasanCfiModule.HiddenFromMake() || !staticHwasanCfiModule.PreventInstall() { + t.Errorf("Hwasan and Cfi cannot enabled at the same time.") + } } func TestVendorSnapshotExclude(t *testing.T) { diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go index 3bc311b68..029bbb4b4 100644 --- a/cmd/soong_build/main.go +++ b/cmd/soong_build/main.go @@ -629,40 +629,41 @@ func bazelArtifacts() []string { // symlink tree creation binary. Then the latter would not need to depend on // the very heavy-weight machinery of soong_build . func runSymlinkForestCreation(configuration android.Config, ctx *android.Context, extraNinjaDeps []string, metricsDir string) string { - var ninjaDeps []string - ninjaDeps = append(ninjaDeps, extraNinjaDeps...) + ctx.EventHandler.Do("symlink_forest", func() { + var ninjaDeps []string + ninjaDeps = append(ninjaDeps, extraNinjaDeps...) - generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build") - workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace") + generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build") + workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace") - excludes := bazelArtifacts() + excludes := bazelArtifacts() - if outDir[0] != '/' { - excludes = append(excludes, outDir) - } + if outDir[0] != '/' { + excludes = append(excludes, outDir) + } - existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir) - if err != nil { - fmt.Fprintf(os.Stderr, "Error determining existing Bazel-related files: %s\n", err) - os.Exit(1) - } + existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir) + if err != nil { + fmt.Fprintf(os.Stderr, "Error determining existing Bazel-related files: %s\n", err) + os.Exit(1) + } - pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(configuration.Bp2buildPackageConfig, topDir, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE")) - excludes = append(excludes, pathsToIgnoredBuildFiles...) - excludes = append(excludes, getTemporaryExcludes()...) + pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(configuration.Bp2buildPackageConfig, topDir, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE")) + excludes = append(excludes, pathsToIgnoredBuildFiles...) + excludes = append(excludes, getTemporaryExcludes()...) + + // PlantSymlinkForest() returns all the directories that were readdir()'ed. + // Such a directory SHOULD be added to `ninjaDeps` so that a child directory + // or file created/deleted under it would trigger an update of the symlink forest. + ctx.EventHandler.Do("plant", func() { + symlinkForestDeps := bp2build.PlantSymlinkForest( + configuration.IsEnvTrue("BP2BUILD_VERBOSE"), topDir, workspaceRoot, generatedRoot, excludes) + ninjaDeps = append(ninjaDeps, symlinkForestDeps...) + }) - // PlantSymlinkForest() returns all the directories that were readdir()'ed. - // Such a directory SHOULD be added to `ninjaDeps` so that a child directory - // or file created/deleted under it would trigger an update of the symlink - // forest. - ctx.EventHandler.Do("symlink_forest", func() { - symlinkForestDeps := bp2build.PlantSymlinkForest( - configuration.IsEnvTrue("BP2BUILD_VERBOSE"), topDir, workspaceRoot, generatedRoot, excludes) - ninjaDeps = append(ninjaDeps, symlinkForestDeps...) + writeDepFile(symlinkForestMarker, ctx.EventHandler, ninjaDeps) + touch(shared.JoinPath(topDir, symlinkForestMarker)) }) - - writeDepFile(symlinkForestMarker, ctx.EventHandler, ninjaDeps) - touch(shared.JoinPath(topDir, symlinkForestMarker)) codegenMetrics := bp2build.ReadCodegenMetrics(metricsDir) if codegenMetrics == nil { m := bp2build.CreateCodegenMetrics() diff --git a/java/base.go b/java/base.go index 5d2498169..55d77dc4a 100644 --- a/java/base.go +++ b/java/base.go @@ -868,7 +868,7 @@ func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.Opt flags = append(flags, genAidlIncludeFlags(ctx, aidlSrcs, includeDirs)) sdkVersion := (j.SdkVersion(ctx)).Kind - defaultTrace := ((sdkVersion == android.SdkSystemServer) || (sdkVersion == android.SdkCore) || (sdkVersion == android.SdkCorePlatform) || (sdkVersion == android.SdkModule)) + defaultTrace := ((sdkVersion == android.SdkSystemServer) || (sdkVersion == android.SdkCore) || (sdkVersion == android.SdkCorePlatform) || (sdkVersion == android.SdkModule) || (sdkVersion == android.SdkSystem)) if proptools.BoolDefault(j.deviceProperties.Aidl.Generate_traces, defaultTrace) { flags = append(flags, "-t") } diff --git a/java/robolectric.go b/java/robolectric.go index 2cb07981e..1d567083d 100644 --- a/java/robolectric.go +++ b/java/robolectric.go @@ -327,7 +327,7 @@ func (r *robolectricTest) AndroidMkEntries() []android.AndroidMkEntries { func (r *robolectricTest) writeTestRunner(w io.Writer, module, name string, tests []string) { fmt.Fprintln(w, "") - fmt.Fprintln(w, "include $(CLEAR_VARS)") + fmt.Fprintln(w, "include $(CLEAR_VARS)", " # java.robolectricTest") fmt.Fprintln(w, "LOCAL_MODULE :=", name) fmt.Fprintln(w, "LOCAL_JAVA_LIBRARIES :=", module) fmt.Fprintln(w, "LOCAL_JAVA_LIBRARIES += ", strings.Join(r.libs, " ")) diff --git a/phony/phony.go b/phony/phony.go index a31d402af..760b79b8a 100644 --- a/phony/phony.go +++ b/phony/phony.go @@ -49,7 +49,7 @@ func (p *phony) GenerateAndroidBuildActions(ctx android.ModuleContext) { func (p *phony) AndroidMk() android.AndroidMkData { return android.AndroidMkData{ Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { - fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)") + fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)", " # phony.phony") fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir) fmt.Fprintln(w, "LOCAL_MODULE :=", name) data.Entries.WriteLicenseVariables(w) diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go index 1f0d28d56..0edbb7c37 100644 --- a/sysprop/sysprop_library.go +++ b/sysprop/sysprop_library.go @@ -336,8 +336,8 @@ func (m *syspropLibrary) AndroidMk() android.AndroidMkData { Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { // sysprop_library module itself is defined as a FAKE module to perform API check. // Actual implementation libraries are created on LoadHookMutator - fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)") - fmt.Fprintf(w, "LOCAL_MODULE := %s\n", m.Name()) + fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)", " # sysprop.syspropLibrary") + fmt.Fprintln(w, "LOCAL_MODULE :=", m.Name()) data.Entries.WriteLicenseVariables(w) fmt.Fprintf(w, "LOCAL_MODULE_CLASS := FAKE\n") fmt.Fprintf(w, "LOCAL_MODULE_TAGS := optional\n") diff --git a/ui/build/soong.go b/ui/build/soong.go index abaf5aecd..837f0a4f4 100644 --- a/ui/build/soong.go +++ b/ui/build/soong.go @@ -18,7 +18,6 @@ import ( "errors" "fmt" "io/fs" - "io/ioutil" "os" "path/filepath" "strconv" @@ -56,13 +55,13 @@ const ( bootstrapEpoch = 1 ) -func writeEnvironmentFile(ctx Context, envFile string, envDeps map[string]string) error { +func writeEnvironmentFile(_ Context, envFile string, envDeps map[string]string) error { data, err := shared.EnvFileContents(envDeps) if err != nil { return err } - return ioutil.WriteFile(envFile, data, 0644) + return os.WriteFile(envFile, data, 0644) } // This uses Android.bp files and various tools to generate <builddir>/build.ninja. @@ -141,7 +140,7 @@ func writeEmptyFile(ctx Context, path string) { if exists, err := fileExists(path); err != nil { ctx.Fatalf("Failed to check if file '%s' exists: %s", path, err) } else if !exists { - err = ioutil.WriteFile(path, nil, 0666) + err = os.WriteFile(path, nil, 0666) if err != nil { ctx.Fatalf("Failed to create empty file '%s': %s", path, err) } @@ -157,24 +156,28 @@ func fileExists(path string) (bool, error) { return true, nil } -func primaryBuilderInvocation( - config Config, - name string, - output string, - specificArgs []string, - description string) bootstrap.PrimaryBuilderInvocation { +type PrimaryBuilderFactory struct { + name string + description string + config Config + output string + specificArgs []string + debugPort string +} + +func (pb PrimaryBuilderFactory) primaryBuilderInvocation() bootstrap.PrimaryBuilderInvocation { commonArgs := make([]string, 0, 0) - if !config.skipSoongTests { + if !pb.config.skipSoongTests { commonArgs = append(commonArgs, "-t") } - commonArgs = append(commonArgs, "-l", filepath.Join(config.FileListDir(), "Android.bp.list")) + commonArgs = append(commonArgs, "-l", filepath.Join(pb.config.FileListDir(), "Android.bp.list")) invocationEnv := make(map[string]string) - if os.Getenv("SOONG_DELVE") != "" { + if pb.debugPort != "" { //debug mode - commonArgs = append(commonArgs, "--delve_listen", os.Getenv("SOONG_DELVE")) - commonArgs = append(commonArgs, "--delve_path", shared.ResolveDelveBinary()) + commonArgs = append(commonArgs, "--delve_listen", pb.debugPort, + "--delve_path", shared.ResolveDelveBinary()) // GODEBUG=asyncpreemptoff=1 disables the preemption of goroutines. This // is useful because the preemption happens by sending SIGURG to the OS // thread hosting the goroutine in question and each signal results in @@ -188,26 +191,26 @@ func primaryBuilderInvocation( } var allArgs []string - allArgs = append(allArgs, specificArgs...) + allArgs = append(allArgs, pb.specificArgs...) allArgs = append(allArgs, - "--globListDir", name, - "--globFile", config.NamedGlobFile(name)) + "--globListDir", pb.name, + "--globFile", pb.config.NamedGlobFile(pb.name)) allArgs = append(allArgs, commonArgs...) - allArgs = append(allArgs, environmentArgs(config, name)...) + allArgs = append(allArgs, environmentArgs(pb.config, pb.name)...) if profileCpu := os.Getenv("SOONG_PROFILE_CPU"); profileCpu != "" { - allArgs = append(allArgs, "--cpuprofile", profileCpu+"."+name) + allArgs = append(allArgs, "--cpuprofile", profileCpu+"."+pb.name) } if profileMem := os.Getenv("SOONG_PROFILE_MEM"); profileMem != "" { - allArgs = append(allArgs, "--memprofile", profileMem+"."+name) + allArgs = append(allArgs, "--memprofile", profileMem+"."+pb.name) } allArgs = append(allArgs, "Android.bp") return bootstrap.PrimaryBuilderInvocation{ Inputs: []string{"Android.bp"}, - Outputs: []string{output}, + Outputs: []string{pb.output}, Args: allArgs, - Description: description, + Description: pb.description, // NB: Changing the value of this environment variable will not result in a // rebuild. The bootstrap Ninja file will change, but apparently Ninja does // not consider changing the pool specified in a statement a change that's @@ -270,90 +273,117 @@ func bootstrapBlueprint(ctx Context, config Config) { if config.bazelStagingMode { mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--bazel-mode-staging") } + queryviewDir := filepath.Join(config.SoongOutDir(), "queryview") + // The BUILD files will be generated in out/soong/.api_bp2build (no symlinks to src files) + // The final workspace will be generated in out/soong/api_bp2build + apiBp2buildDir := filepath.Join(config.SoongOutDir(), ".api_bp2build") - mainSoongBuildInvocation := primaryBuilderInvocation( - config, - soongBuildTag, - config.SoongNinjaFile(), - mainSoongBuildExtraArgs, - fmt.Sprintf("analyzing Android.bp files and generating ninja file at %s", config.SoongNinjaFile()), - ) - - if config.BazelBuildEnabled() { - // Mixed builds call Bazel from soong_build and they therefore need the - // Bazel workspace to be available. Make that so by adding a dependency on - // the bp2build marker file to the action that invokes soong_build . - mainSoongBuildInvocation.OrderOnlyInputs = append(mainSoongBuildInvocation.OrderOnlyInputs, - config.Bp2BuildWorkspaceMarkerFile()) - } - - bp2buildInvocation := primaryBuilderInvocation( - config, - bp2buildFilesTag, - config.Bp2BuildFilesMarkerFile(), - []string{ - "--bp2build_marker", config.Bp2BuildFilesMarkerFile(), + pbfs := []PrimaryBuilderFactory{ + { + name: soongBuildTag, + description: fmt.Sprintf("analyzing Android.bp files and generating ninja file at %s", config.SoongNinjaFile()), + config: config, + output: config.SoongNinjaFile(), + specificArgs: mainSoongBuildExtraArgs, }, - fmt.Sprintf("converting Android.bp files to BUILD files at %s/bp2build", config.SoongOutDir()), - ) - - bp2buildWorkspaceInvocation := primaryBuilderInvocation( - config, - bp2buildWorkspaceTag, - config.Bp2BuildWorkspaceMarkerFile(), - []string{ - "--symlink_forest_marker", config.Bp2BuildWorkspaceMarkerFile(), + { + name: bp2buildFilesTag, + description: fmt.Sprintf("converting Android.bp files to BUILD files at %s/bp2build", config.SoongOutDir()), + config: config, + output: config.Bp2BuildFilesMarkerFile(), + specificArgs: []string{"--bp2build_marker", config.Bp2BuildFilesMarkerFile()}, }, - fmt.Sprintf("Creating Bazel symlink forest"), - ) - - bp2buildWorkspaceInvocation.Inputs = append(bp2buildWorkspaceInvocation.Inputs, - config.Bp2BuildFilesMarkerFile(), filepath.Join(config.FileListDir(), "bazel.list")) - - jsonModuleGraphInvocation := primaryBuilderInvocation( - config, - jsonModuleGraphTag, - config.ModuleGraphFile(), - []string{ - "--module_graph_file", config.ModuleGraphFile(), - "--module_actions_file", config.ModuleActionsFile(), + { + name: bp2buildWorkspaceTag, + description: "Creating Bazel symlink forest", + config: config, + output: config.Bp2BuildWorkspaceMarkerFile(), + specificArgs: []string{"--symlink_forest_marker", config.Bp2BuildWorkspaceMarkerFile()}, }, - fmt.Sprintf("generating the Soong module graph at %s", config.ModuleGraphFile()), - ) - - queryviewDir := filepath.Join(config.SoongOutDir(), "queryview") - queryviewInvocation := primaryBuilderInvocation( - config, - queryviewTag, - config.QueryviewMarkerFile(), - []string{ - "--bazel_queryview_dir", queryviewDir, + { + name: jsonModuleGraphTag, + description: fmt.Sprintf("generating the Soong module graph at %s", config.ModuleGraphFile()), + config: config, + output: config.ModuleGraphFile(), + specificArgs: []string{ + "--module_graph_file", config.ModuleGraphFile(), + "--module_actions_file", config.ModuleActionsFile(), + }, }, - fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir), - ) - - // The BUILD files will be generated in out/soong/.api_bp2build (no symlinks to src files) - // The final workspace will be generated in out/soong/api_bp2build - apiBp2buildDir := filepath.Join(config.SoongOutDir(), ".api_bp2build") - apiBp2buildInvocation := primaryBuilderInvocation( - config, - apiBp2buildTag, - config.ApiBp2buildMarkerFile(), - []string{ - "--bazel_api_bp2build_dir", apiBp2buildDir, + { + name: queryviewTag, + description: fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir), + config: config, + output: config.QueryviewMarkerFile(), + specificArgs: []string{"--bazel_queryview_dir", queryviewDir}, }, - fmt.Sprintf("generating BUILD files for API contributions at %s", apiBp2buildDir), - ) - - soongDocsInvocation := primaryBuilderInvocation( - config, - soongDocsTag, - config.SoongDocsHtml(), - []string{ - "--soong_docs", config.SoongDocsHtml(), + { + name: apiBp2buildTag, + description: fmt.Sprintf("generating BUILD files for API contributions at %s", apiBp2buildDir), + config: config, + output: config.ApiBp2buildMarkerFile(), + specificArgs: []string{"--bazel_api_bp2build_dir", apiBp2buildDir}, }, - fmt.Sprintf("generating Soong docs at %s", config.SoongDocsHtml()), - ) + { + name: soongDocsTag, + description: fmt.Sprintf("generating Soong docs at %s", config.SoongDocsHtml()), + config: config, + output: config.SoongDocsHtml(), + specificArgs: []string{"--soong_docs", config.SoongDocsHtml()}, + }, + } + + // Figure out which invocations will be run under the debugger: + // * SOONG_DELVE if set specifies listening port + // * SOONG_DELVE_STEPS if set specifies specific invocations to be debugged, otherwise all are + debuggedInvocations := make(map[string]bool) + delvePort := os.Getenv("SOONG_DELVE") + if delvePort != "" { + if steps := os.Getenv("SOONG_DELVE_STEPS"); steps != "" { + var validSteps []string + for _, pbf := range pbfs { + debuggedInvocations[pbf.name] = false + validSteps = append(validSteps, pbf.name) + + } + for _, step := range strings.Split(steps, ",") { + if _, ok := debuggedInvocations[step]; ok { + debuggedInvocations[step] = true + } else { + ctx.Fatalf("SOONG_DELVE_STEPS contains unknown soong_build step %s\n"+ + "Valid steps are %v", step, validSteps) + } + } + } else { + // SOONG_DELVE_STEPS is not set, run all steps in the debugger + for _, pbf := range pbfs { + debuggedInvocations[pbf.name] = true + } + } + } + + var invocations []bootstrap.PrimaryBuilderInvocation + for _, pbf := range pbfs { + if debuggedInvocations[pbf.name] { + pbf.debugPort = delvePort + } + pbi := pbf.primaryBuilderInvocation() + // Some invocations require adjustment: + switch pbf.name { + case soongBuildTag: + if config.BazelBuildEnabled() { + // Mixed builds call Bazel from soong_build and they therefore need the + // Bazel workspace to be available. Make that so by adding a dependency on + // the bp2build marker file to the action that invokes soong_build . + pbi.OrderOnlyInputs = append(pbi.OrderOnlyInputs, config.Bp2BuildWorkspaceMarkerFile()) + } + case bp2buildWorkspaceTag: + pbi.Inputs = append(pbi.Inputs, + config.Bp2BuildFilesMarkerFile(), + filepath.Join(config.FileListDir(), "bazel.list")) + } + invocations = append(invocations, pbi) + } // The glob .ninja files are subninja'd. However, they are generated during // the build itself so we write an empty file if the file does not exist yet @@ -362,11 +392,11 @@ func bootstrapBlueprint(ctx Context, config Config) { writeEmptyFile(ctx, globFile) } - var blueprintArgs bootstrap.Args - - blueprintArgs.ModuleListFile = filepath.Join(config.FileListDir(), "Android.bp.list") - blueprintArgs.OutFile = shared.JoinPath(config.SoongOutDir(), "bootstrap.ninja") - blueprintArgs.EmptyNinjaFile = false + blueprintArgs := bootstrap.Args{ + ModuleListFile: filepath.Join(config.FileListDir(), "Android.bp.list"), + OutFile: shared.JoinPath(config.SoongOutDir(), "bootstrap.ninja"), + EmptyNinjaFile: false, + } blueprintCtx := blueprint.NewContext() blueprintCtx.SetIgnoreUnknownModuleTypes(true) @@ -376,16 +406,9 @@ func bootstrapBlueprint(ctx Context, config Config) { outDir: config.OutDir(), runGoTests: !config.skipSoongTests, // If we want to debug soong_build, we need to compile it for debugging - debugCompilation: os.Getenv("SOONG_DELVE") != "", - subninjas: bootstrapGlobFileList(config), - primaryBuilderInvocations: []bootstrap.PrimaryBuilderInvocation{ - mainSoongBuildInvocation, - bp2buildInvocation, - bp2buildWorkspaceInvocation, - jsonModuleGraphInvocation, - queryviewInvocation, - apiBp2buildInvocation, - soongDocsInvocation}, + debugCompilation: delvePort != "", + subninjas: bootstrapGlobFileList(config), + primaryBuilderInvocations: invocations, } // since `bootstrap.ninja` is regenerated unconditionally, we ignore the deps, i.e. little |