diff options
| -rw-r--r-- | README.md | 13 | ||||
| -rw-r--r-- | android/env.go | 11 | ||||
| -rw-r--r-- | android/hooks.go | 2 | ||||
| -rw-r--r-- | android/module.go | 25 | ||||
| -rw-r--r-- | android/mutator.go | 34 | ||||
| -rw-r--r-- | android/mutator_test.go | 120 | ||||
| -rw-r--r-- | android/neverallow.go | 126 | ||||
| -rw-r--r-- | android/notices.go | 43 | ||||
| -rw-r--r-- | android/package_test.go | 12 | ||||
| -rw-r--r-- | android/path_properties_test.go | 8 | ||||
| -rw-r--r-- | apex/apex.go | 74 | ||||
| -rw-r--r-- | apex/apex_test.go | 36 | ||||
| -rw-r--r-- | cmd/soong_build/main.go | 53 | ||||
| -rw-r--r-- | java/androidmk.go | 12 | ||||
| -rw-r--r-- | java/app.go | 18 | ||||
| -rw-r--r-- | java/app_test.go | 16 | ||||
| -rw-r--r-- | ui/build/config.go | 4 | ||||
| -rw-r--r-- | ui/build/config_test.go | 56 | ||||
| -rw-r--r-- | ui/build/exec.go | 34 | ||||
| -rw-r--r-- | ui/build/ninja.go | 2 | ||||
| -rw-r--r-- | ui/build/paths/config.go | 1 | ||||
| -rw-r--r-- | ui/build/sandbox_linux.go | 4 | ||||
| -rw-r--r-- | ui/build/soong.go | 2 | ||||
| -rw-r--r-- | ui/terminal/util.go | 6 |
24 files changed, 580 insertions, 132 deletions
@@ -337,6 +337,19 @@ build/soong/scripts/setup_go_workspace_for_soong.sh This will bind mount the Soong source directories into the directory in the layout expected by the IDE. +### Running Soong in a debugger + +To run the soong_build process in a debugger, install `dlv` and then start the build with +`SOONG_DELVE=<listen addr>` in the environment. +For examle: +```bash +SOONG_DELVE=:1234 m nothing +``` +and then in another terminal: +``` +dlv connect :1234 +``` + ## Contact Email android-building@googlegroups.com (external) for any questions, or see diff --git a/android/env.go b/android/env.go index 469dfffed..d9f2db2f3 100644 --- a/android/env.go +++ b/android/env.go @@ -16,6 +16,7 @@ package android import ( "os" + "os/exec" "strings" "android/soong/env" @@ -29,8 +30,16 @@ import ( // a manifest regeneration. var originalEnv map[string]string +var SoongDelveListen string +var SoongDelvePath string func init() { + // Delve support needs to read this environment variable very early, before NewConfig has created a way to + // access originalEnv with dependencies. Store the value where soong_build can find it, it will manually + // ensure the dependencies are created. + SoongDelveListen = os.Getenv("SOONG_DELVE") + SoongDelvePath, _ = exec.LookPath("dlv") + originalEnv = make(map[string]string) for _, env := range os.Environ() { idx := strings.IndexRune(env, '=') @@ -38,6 +47,8 @@ func init() { originalEnv[env[:idx]] = env[idx+1:] } } + // Clear the environment to prevent use of os.Getenv(), which would not provide dependencies on environment + // variable values. The environment is available through ctx.Config().Getenv, ctx.Config().IsEnvTrue, etc. os.Clearenv() } diff --git a/android/hooks.go b/android/hooks.go index 2d2f797bd..58109961b 100644 --- a/android/hooks.go +++ b/android/hooks.go @@ -129,6 +129,8 @@ func registerLoadHookMutator(ctx RegisterMutatorsContext) { func LoadHookMutator(ctx TopDownMutatorContext) { if m, ok := ctx.Module().(Module); ok { + m.base().commonProperties.DebugName = ctx.ModuleName() + // Cast through *topDownMutatorContext because AppendProperties is implemented // on *topDownMutatorContext but not exposed through TopDownMutatorContext var loadHookCtx LoadHookContext = ctx.(*topDownMutatorContext) diff --git a/android/module.go b/android/module.go index 43b8763d2..2d4c1b593 100644 --- a/android/module.go +++ b/android/module.go @@ -203,6 +203,9 @@ type Module interface { RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams VariablesForTests() map[string]string + // String returns a string that includes the module name and variants for printing during debugging. + String() string + // Get the qualified module id for this module. qualifiedModuleId(ctx BaseModuleContext) qualifiedModuleName @@ -408,6 +411,11 @@ type commonProperties struct { NamespaceExportedToMake bool `blueprint:"mutated"` MissingDeps []string `blueprint:"mutated"` + + // Name and variant strings stored by mutators to enable Module.String() + DebugName string `blueprint:"mutated"` + DebugMutators []string `blueprint:"mutated"` + DebugVariations []string `blueprint:"mutated"` } type hostAndDeviceProperties struct { @@ -625,6 +633,23 @@ func (m *ModuleBase) Name() string { return String(m.nameProperties.Name) } +// String returns a string that includes the module name and variants for printing during debugging. +func (m *ModuleBase) String() string { + sb := strings.Builder{} + sb.WriteString(m.commonProperties.DebugName) + sb.WriteString("{") + for i := range m.commonProperties.DebugMutators { + if i != 0 { + sb.WriteString(",") + } + sb.WriteString(m.commonProperties.DebugMutators[i]) + sb.WriteString(":") + sb.WriteString(m.commonProperties.DebugVariations[i]) + } + sb.WriteString("}") + return sb.String() +} + // BaseModuleName returns the name of the module as specified in the blueprints file. func (m *ModuleBase) BaseModuleName() string { return String(m.nameProperties.Name) diff --git a/android/mutator.go b/android/mutator.go index 070b420ff..b7994329a 100644 --- a/android/mutator.go +++ b/android/mutator.go @@ -117,6 +117,8 @@ type TopDownMutator func(TopDownMutatorContext) type TopDownMutatorContext interface { BaseModuleContext + MutatorName() string + Rename(name string) CreateModule(blueprint.ModuleFactory, ...interface{}) @@ -132,6 +134,8 @@ type BottomUpMutator func(BottomUpMutatorContext) type BottomUpMutatorContext interface { BaseModuleContext + MutatorName() string + Rename(name string) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) @@ -229,16 +233,26 @@ func (t *topDownMutatorContext) PrependProperties(props ...interface{}) { // non-overridden method has to be forwarded. There are fewer non-overridden methods, so use the latter. The following // methods forward to the identical blueprint versions for topDownMutatorContext and bottomUpMutatorContext. +func (t *topDownMutatorContext) MutatorName() string { + return t.bp.MutatorName() +} + func (t *topDownMutatorContext) Rename(name string) { t.bp.Rename(name) + t.Module().base().commonProperties.DebugName = name } func (t *topDownMutatorContext) CreateModule(factory blueprint.ModuleFactory, props ...interface{}) { t.bp.CreateModule(factory, props...) } +func (b *bottomUpMutatorContext) MutatorName() string { + return b.bp.MutatorName() +} + func (b *bottomUpMutatorContext) Rename(name string) { b.bp.Rename(name) + b.Module().base().commonProperties.DebugName = name } func (b *bottomUpMutatorContext) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) { @@ -250,11 +264,27 @@ func (b *bottomUpMutatorContext) AddReverseDependency(module blueprint.Module, t } func (b *bottomUpMutatorContext) CreateVariations(variations ...string) []blueprint.Module { - return b.bp.CreateVariations(variations...) + modules := b.bp.CreateVariations(variations...) + + for i := range variations { + base := modules[i].(Module).base() + base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, b.MutatorName()) + base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variations[i]) + } + + return modules } func (b *bottomUpMutatorContext) CreateLocalVariations(variations ...string) []blueprint.Module { - return b.bp.CreateLocalVariations(variations...) + modules := b.bp.CreateLocalVariations(variations...) + + for i := range variations { + base := modules[i].(Module).base() + base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, b.MutatorName()) + base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variations[i]) + } + + return modules } func (b *bottomUpMutatorContext) SetDependencyVariation(variation string) { diff --git a/android/mutator_test.go b/android/mutator_test.go index 4cef40006..0b23434af 100644 --- a/android/mutator_test.go +++ b/android/mutator_test.go @@ -15,8 +15,6 @@ package android import ( - "io/ioutil" - "os" "reflect" "testing" @@ -26,6 +24,8 @@ import ( type mutatorTestModule struct { ModuleBase props struct { + Deps_missing_deps []string + Mutator_missing_deps []string } missingDeps []string @@ -48,20 +48,14 @@ func (m *mutatorTestModule) GenerateAndroidBuildActions(ctx ModuleContext) { } func (m *mutatorTestModule) DepsMutator(ctx BottomUpMutatorContext) { - ctx.AddDependency(ctx.Module(), nil, "regular_missing_dep") + ctx.AddDependency(ctx.Module(), nil, m.props.Deps_missing_deps...) } func addMissingDependenciesMutator(ctx TopDownMutatorContext) { - ctx.AddMissingDependencies([]string{"added_missing_dep"}) + ctx.AddMissingDependencies(ctx.Module().(*mutatorTestModule).props.Mutator_missing_deps) } func TestMutatorAddMissingDependencies(t *testing.T) { - buildDir, err := ioutil.TempDir("", "soong_mutator_test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(buildDir) - config := TestConfig(buildDir, nil) config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true) @@ -76,6 +70,8 @@ func TestMutatorAddMissingDependencies(t *testing.T) { bp := ` test { name: "foo", + deps_missing_deps: ["regular_missing_dep"], + mutator_missing_deps: ["added_missing_dep"], } ` @@ -97,3 +93,107 @@ func TestMutatorAddMissingDependencies(t *testing.T) { t.Errorf("want foo missing deps %q, got %q", w, g) } } + +func TestModuleString(t *testing.T) { + ctx := NewTestContext() + + var moduleStrings []string + + ctx.PreArchMutators(func(ctx RegisterMutatorsContext) { + ctx.BottomUp("pre_arch", func(ctx BottomUpMutatorContext) { + moduleStrings = append(moduleStrings, ctx.Module().String()) + ctx.CreateVariations("a", "b") + }) + ctx.TopDown("rename_top_down", func(ctx TopDownMutatorContext) { + moduleStrings = append(moduleStrings, ctx.Module().String()) + ctx.Rename(ctx.Module().base().Name() + "_renamed1") + }) + }) + + ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { + ctx.BottomUp("pre_deps", func(ctx BottomUpMutatorContext) { + moduleStrings = append(moduleStrings, ctx.Module().String()) + ctx.CreateVariations("c", "d") + }) + }) + + ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) { + ctx.BottomUp("post_deps", func(ctx BottomUpMutatorContext) { + moduleStrings = append(moduleStrings, ctx.Module().String()) + ctx.CreateLocalVariations("e", "f") + }) + ctx.BottomUp("rename_bottom_up", func(ctx BottomUpMutatorContext) { + moduleStrings = append(moduleStrings, ctx.Module().String()) + ctx.Rename(ctx.Module().base().Name() + "_renamed2") + }) + ctx.BottomUp("final", func(ctx BottomUpMutatorContext) { + moduleStrings = append(moduleStrings, ctx.Module().String()) + }) + }) + + ctx.RegisterModuleType("test", ModuleFactoryAdaptor(mutatorTestModuleFactory)) + + bp := ` + test { + name: "foo", + } + ` + + mockFS := map[string][]byte{ + "Android.bp": []byte(bp), + } + + ctx.MockFileSystem(mockFS) + + ctx.Register() + + config := TestConfig(buildDir, nil) + + _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) + FailIfErrored(t, errs) + _, errs = ctx.PrepareBuildActions(config) + FailIfErrored(t, errs) + + want := []string{ + // Initial name. + "foo{}", + + // After pre_arch (reversed because rename_top_down is TopDown so it visits in reverse order). + "foo{pre_arch:b}", + "foo{pre_arch:a}", + + // After rename_top_down. + "foo_renamed1{pre_arch:a}", + "foo_renamed1{pre_arch:b}", + + // After pre_deps. + "foo_renamed1{pre_arch:a,pre_deps:c}", + "foo_renamed1{pre_arch:a,pre_deps:d}", + "foo_renamed1{pre_arch:b,pre_deps:c}", + "foo_renamed1{pre_arch:b,pre_deps:d}", + + // After post_deps. + "foo_renamed1{pre_arch:a,pre_deps:c,post_deps:e}", + "foo_renamed1{pre_arch:a,pre_deps:c,post_deps:f}", + "foo_renamed1{pre_arch:a,pre_deps:d,post_deps:e}", + "foo_renamed1{pre_arch:a,pre_deps:d,post_deps:f}", + "foo_renamed1{pre_arch:b,pre_deps:c,post_deps:e}", + "foo_renamed1{pre_arch:b,pre_deps:c,post_deps:f}", + "foo_renamed1{pre_arch:b,pre_deps:d,post_deps:e}", + "foo_renamed1{pre_arch:b,pre_deps:d,post_deps:f}", + + // After rename_bottom_up. + "foo_renamed2{pre_arch:a,pre_deps:c,post_deps:e}", + "foo_renamed2{pre_arch:a,pre_deps:c,post_deps:f}", + "foo_renamed2{pre_arch:a,pre_deps:d,post_deps:e}", + "foo_renamed2{pre_arch:a,pre_deps:d,post_deps:f}", + "foo_renamed2{pre_arch:b,pre_deps:c,post_deps:e}", + "foo_renamed2{pre_arch:b,pre_deps:c,post_deps:f}", + "foo_renamed2{pre_arch:b,pre_deps:d,post_deps:e}", + "foo_renamed2{pre_arch:b,pre_deps:d,post_deps:f}", + } + + if !reflect.DeepEqual(moduleStrings, want) { + t.Errorf("want module String() values:\n%q\ngot:\n%q", want, moduleStrings) + } +} diff --git a/android/neverallow.go b/android/neverallow.go index f35d1fed9..1893e8b76 100644 --- a/android/neverallow.go +++ b/android/neverallow.go @@ -31,61 +31,64 @@ import ( // work regardless of these restrictions. // // A module is disallowed if all of the following are true: -// - it is in one of the "in" paths -// - it is not in one of the "notIn" paths -// - it has all "with" properties matched +// - it is in one of the "In" paths +// - it is not in one of the "NotIn" paths +// - it has all "With" properties matched // - - values are matched in their entirety // - - nil is interpreted as an empty string // - - nested properties are separated with a '.' // - - if the property is a list, any of the values in the list being matches // counts as a match -// - it has none of the "without" properties matched (same rules as above) +// - it has none of the "Without" properties matched (same rules as above) func registerNeverallowMutator(ctx RegisterMutatorsContext) { ctx.BottomUp("neverallow", neverallowMutator).Parallel() } -var neverallows = createNeverAllows() +var neverallows = []Rule{} -func createNeverAllows() []*rule { - rules := []*rule{} - rules = append(rules, createTrebleRules()...) - rules = append(rules, createLibcoreRules()...) - rules = append(rules, createJavaDeviceForHostRules()...) - return rules +func init() { + AddNeverAllowRules(createTrebleRules()...) + AddNeverAllowRules(createLibcoreRules()...) + AddNeverAllowRules(createJavaDeviceForHostRules()...) +} + +// Add a NeverAllow rule to the set of rules to apply. +func AddNeverAllowRules(rules ...Rule) { + neverallows = append(neverallows, rules...) } -func createTrebleRules() []*rule { - return []*rule{ - neverallow(). - in("vendor", "device"). - with("vndk.enabled", "true"). - without("vendor", "true"). - because("the VNDK can never contain a library that is device dependent."), - neverallow(). - with("vndk.enabled", "true"). - without("vendor", "true"). - without("owner", ""). - because("a VNDK module can never have an owner."), +func createTrebleRules() []Rule { + return []Rule{ + NeverAllow(). + In("vendor", "device"). + With("vndk.enabled", "true"). + Without("vendor", "true"). + Because("the VNDK can never contain a library that is device dependent."), + NeverAllow(). + With("vndk.enabled", "true"). + Without("vendor", "true"). + Without("owner", ""). + Because("a VNDK module can never have an owner."), // TODO(b/67974785): always enforce the manifest - neverallow(). - without("name", "libhidltransport"). - with("product_variables.enforce_vintf_manifest.cflags", "*"). - because("manifest enforcement should be independent of ."), + NeverAllow(). + Without("name", "libhidltransport"). + With("product_variables.enforce_vintf_manifest.cflags", "*"). + Because("manifest enforcement should be independent of ."), // TODO(b/67975799): vendor code should always use /vendor/bin/sh - neverallow(). - without("name", "libc_bionic_ndk"). - with("product_variables.treble_linker_namespaces.cflags", "*"). - because("nothing should care if linker namespaces are enabled or not"), + NeverAllow(). + Without("name", "libc_bionic_ndk"). + With("product_variables.treble_linker_namespaces.cflags", "*"). + Because("nothing should care if linker namespaces are enabled or not"), // Example: - // *neverallow().with("Srcs", "main.cpp")) + // *NeverAllow().with("Srcs", "main.cpp")) } } -func createLibcoreRules() []*rule { +func createLibcoreRules() []Rule { var coreLibraryProjects = []string{ "libcore", "external/apache-harmony", @@ -102,27 +105,27 @@ func createLibcoreRules() []*rule { // Core library constraints. The sdk_version: "none" can only be used in core library projects. // Access to core library targets is restricted using visibility rules. - rules := []*rule{ - neverallow(). - notIn(coreLibraryProjects...). - with("sdk_version", "none"), + rules := []Rule{ + NeverAllow(). + NotIn(coreLibraryProjects...). + With("sdk_version", "none"), } return rules } -func createJavaDeviceForHostRules() []*rule { +func createJavaDeviceForHostRules() []Rule { javaDeviceForHostProjectsWhitelist := []string{ "external/guava", "external/robolectric-shadows", "framework/layoutlib", } - return []*rule{ - neverallow(). - notIn(javaDeviceForHostProjectsWhitelist...). - moduleType("java_device_for_host", "java_host_for_device"). - because("java_device_for_host can only be used in whitelisted projects"), + return []Rule{ + NeverAllow(). + NotIn(javaDeviceForHostProjectsWhitelist...). + ModuleType("java_device_for_host", "java_host_for_device"). + Because("java_device_for_host can only be used in whitelisted projects"), } } @@ -135,7 +138,8 @@ func neverallowMutator(ctx BottomUpMutatorContext) { dir := ctx.ModuleDir() + "/" properties := m.GetProperties() - for _, n := range neverallows { + for _, r := range neverallows { + n := r.(*rule) if !n.appliesToPath(dir) { continue } @@ -157,6 +161,23 @@ type ruleProperty struct { value string // e.x.: true } +// A NeverAllow rule. +type Rule interface { + In(path ...string) Rule + + NotIn(path ...string) Rule + + ModuleType(types ...string) Rule + + NotModuleType(types ...string) Rule + + With(properties, value string) Rule + + Without(properties, value string) Rule + + Because(reason string) Rule +} + type rule struct { // User string for why this is a thing. reason string @@ -171,31 +192,32 @@ type rule struct { unlessProps []ruleProperty } -func neverallow() *rule { +// Create a new NeverAllow rule. +func NeverAllow() Rule { return &rule{} } -func (r *rule) in(path ...string) *rule { +func (r *rule) In(path ...string) Rule { r.paths = append(r.paths, cleanPaths(path)...) return r } -func (r *rule) notIn(path ...string) *rule { +func (r *rule) NotIn(path ...string) Rule { r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...) return r } -func (r *rule) moduleType(types ...string) *rule { +func (r *rule) ModuleType(types ...string) Rule { r.moduleTypes = append(r.moduleTypes, types...) return r } -func (r *rule) notModuleType(types ...string) *rule { +func (r *rule) NotModuleType(types ...string) Rule { r.unlessModuleTypes = append(r.unlessModuleTypes, types...) return r } -func (r *rule) with(properties, value string) *rule { +func (r *rule) With(properties, value string) Rule { r.props = append(r.props, ruleProperty{ fields: fieldNamesForProperties(properties), value: value, @@ -203,7 +225,7 @@ func (r *rule) with(properties, value string) *rule { return r } -func (r *rule) without(properties, value string) *rule { +func (r *rule) Without(properties, value string) Rule { r.unlessProps = append(r.unlessProps, ruleProperty{ fields: fieldNamesForProperties(properties), value: value, @@ -211,7 +233,7 @@ func (r *rule) without(properties, value string) *rule { return r } -func (r *rule) because(reason string) *rule { +func (r *rule) Because(reason string) Rule { r.reason = reason return r } diff --git a/android/notices.go b/android/notices.go index dbb88fc1a..850359328 100644 --- a/android/notices.go +++ b/android/notices.go @@ -27,6 +27,13 @@ func init() { pctx.HostBinToolVariable("minigzip", "minigzip") } +type NoticeOutputs struct { + Merged OptionalPath + TxtOutput OptionalPath + HtmlOutput OptionalPath + HtmlGzOutput OptionalPath +} + var ( mergeNoticesRule = pctx.AndroidStaticRule("mergeNoticesRule", blueprint.RuleParams{ Command: `${merge_notices} --output $out $in`, @@ -35,13 +42,13 @@ var ( }) generateNoticeRule = pctx.AndroidStaticRule("generateNoticeRule", blueprint.RuleParams{ - Command: `rm -rf $tmpDir $$(dirname $out) && ` + - `mkdir -p $tmpDir $$(dirname $out) && ` + - `${generate_notice} --text-output $tmpDir/NOTICE.txt --html-output $tmpDir/NOTICE.html -t "$title" -s $inputDir && ` + - `${minigzip} -c $tmpDir/NOTICE.html > $out`, + Command: `rm -rf $$(dirname $txtOut) $$(dirname htmlOut) $$(dirname $out) && ` + + `mkdir -p $$(dirname $txtOut) $$(dirname htmlOut) $$(dirname $out) && ` + + `${generate_notice} --text-output $txtOut --html-output $htmlOut -t "$title" -s $inputDir && ` + + `${minigzip} -c $htmlOut > $out`, CommandDeps: []string{"${generate_notice}", "${minigzip}"}, Description: "produce notice file $out", - }, "tmpDir", "title", "inputDir") + }, "txtOut", "htmlOut", "title", "inputDir") ) func MergeNotices(ctx ModuleContext, mergedNotice WritablePath, noticePaths []Path) { @@ -54,7 +61,7 @@ func MergeNotices(ctx ModuleContext, mergedNotice WritablePath, noticePaths []Pa } func BuildNoticeOutput(ctx ModuleContext, installPath OutputPath, installFilename string, - noticePaths []Path) ModuleOutPath { + noticePaths []Path) NoticeOutputs { // Merge all NOTICE files into one. // TODO(jungjw): We should just produce a well-formatted NOTICE.html file in a single pass. // @@ -68,20 +75,28 @@ func BuildNoticeOutput(ctx ModuleContext, installPath OutputPath, installFilenam MergeNotices(ctx, mergedNotice, noticePaths) // Transform the merged NOTICE file into a gzipped HTML file. - noticeOutput := PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz") - tmpDir := PathForModuleOut(ctx, "NOTICE_tmp") + txtOuptut := PathForModuleOut(ctx, "NOTICE_txt", "NOTICE.txt") + htmlOutput := PathForModuleOut(ctx, "NOTICE_html", "NOTICE.html") + htmlGzOutput := PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz") title := "Notices for " + ctx.ModuleName() ctx.Build(pctx, BuildParams{ - Rule: generateNoticeRule, - Description: "generate notice output", - Input: mergedNotice, - Output: noticeOutput, + Rule: generateNoticeRule, + Description: "generate notice output", + Input: mergedNotice, + Output: htmlGzOutput, + ImplicitOutputs: WritablePaths{txtOuptut, htmlOutput}, Args: map[string]string{ - "tmpDir": tmpDir.String(), + "txtOut": txtOuptut.String(), + "htmlOut": htmlOutput.String(), "title": title, "inputDir": PathForModuleOut(ctx, "NOTICE_FILES/src").String(), }, }) - return noticeOutput + return NoticeOutputs{ + Merged: OptionalPathForPath(mergedNotice), + TxtOutput: OptionalPathForPath(txtOuptut), + HtmlOutput: OptionalPathForPath(htmlOutput), + HtmlGzOutput: OptionalPathForPath(htmlGzOutput), + } } diff --git a/android/package_test.go b/android/package_test.go index f1f47acb4..e5b055667 100644 --- a/android/package_test.go +++ b/android/package_test.go @@ -1,8 +1,6 @@ package android import ( - "io/ioutil" - "os" "testing" ) @@ -58,15 +56,9 @@ var packageTests = []struct { } func TestPackage(t *testing.T) { - buildDir, err := ioutil.TempDir("", "soong_package_test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(buildDir) - for _, test := range packageTests { t.Run(test.name, func(t *testing.T) { - _, errs := testPackage(buildDir, test.fs) + _, errs := testPackage(test.fs) expectedErrors := test.expectedErrors if expectedErrors == nil { @@ -89,7 +81,7 @@ func TestPackage(t *testing.T) { } } -func testPackage(buildDir string, fs map[string][]byte) (*TestContext, []error) { +func testPackage(fs map[string][]byte) (*TestContext, []error) { // Create a new config per test as visibility information is stored in the config. config := TestArchConfig(buildDir, nil) diff --git a/android/path_properties_test.go b/android/path_properties_test.go index fa187fa6f..e98c1365b 100644 --- a/android/path_properties_test.go +++ b/android/path_properties_test.go @@ -15,8 +15,6 @@ package android import ( - "io/ioutil" - "os" "reflect" "testing" ) @@ -85,12 +83,6 @@ func TestPathDepsMutator(t *testing.T) { }, } - buildDir, err := ioutil.TempDir("", "soong_path_properties_test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(buildDir) - for _, test := range tests { t.Run(test.name, func(t *testing.T) { config := TestArchConfig(buildDir, nil) diff --git a/apex/apex.go b/apex/apex.go index a77e29584..f683f9693 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -108,6 +108,7 @@ var ( executableTag = dependencyTag{name: "executable"} javaLibTag = dependencyTag{name: "javaLib"} prebuiltTag = dependencyTag{name: "prebuilt"} + testTag = dependencyTag{name: "test"} keyTag = dependencyTag{name: "key"} certificateTag = dependencyTag{name: "certificate"} ) @@ -192,6 +193,8 @@ type apexNativeDependencies struct { Native_shared_libs []string // List of native executables Binaries []string + // List of native tests + Tests []string } type apexMultilibProperties struct { // Native dependencies whose compile_multilib is "first" @@ -232,7 +235,7 @@ type apexBundleProperties struct { // List of native shared libs that are embedded inside this APEX bundle Native_shared_libs []string - // List of native executables that are embedded inside this APEX bundle + // List of executables that are embedded inside this APEX bundle Binaries []string // List of java libraries that are embedded inside this APEX bundle @@ -241,6 +244,9 @@ type apexBundleProperties struct { // List of prebuilt files that are embedded inside this APEX bundle Prebuilts []string + // List of tests that are embedded inside this APEX bundle + Tests []string + // Name of the apex_key module that provides the private key to sign APEX Key *string @@ -299,6 +305,7 @@ const ( pyBinary goBinary javaSharedLib + nativeTest ) type apexPackaging int @@ -361,6 +368,8 @@ func (class apexFileClass) NameInMake() string { return "EXECUTABLES" case javaSharedLib: return "JAVA_LIBRARIES" + case nativeTest: + return "NATIVE_TESTS" default: panic(fmt.Errorf("unkonwn class %d", class)) } @@ -406,7 +415,8 @@ type apexBundle struct { } func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, - native_shared_libs []string, binaries []string, arch string, imageVariation string) { + native_shared_libs []string, binaries []string, tests []string, + arch string, imageVariation string) { // Use *FarVariation* to be able to depend on modules having // conflicting variations with this module. This is required since // arch variant of an APEX bundle is 'common' but it is 'arm' or 'arm64' @@ -422,6 +432,11 @@ func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, {Mutator: "arch", Variation: arch}, {Mutator: "image", Variation: imageVariation}, }, executableTag, binaries...) + + ctx.AddFarVariationDependencies([]blueprint.Variation{ + {Mutator: "arch", Variation: arch}, + {Mutator: "image", Variation: imageVariation}, + }, testTag, tests...) } func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) { @@ -459,10 +474,19 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) { {Mutator: "link", Variation: "shared"}, }, sharedLibTag, a.properties.Native_shared_libs...) + // When multilib.* is omitted for tests, it implies + // multilib.both. + ctx.AddFarVariationDependencies([]blueprint.Variation{ + {Mutator: "arch", Variation: target.String()}, + {Mutator: "image", Variation: a.getImageVariation(config)}, + }, testTag, a.properties.Tests...) + // Add native modules targetting both ABIs addDependenciesForNativeModules(ctx, a.properties.Multilib.Both.Native_shared_libs, - a.properties.Multilib.Both.Binaries, target.String(), + a.properties.Multilib.Both.Binaries, + a.properties.Multilib.Both.Tests, + target.String(), a.getImageVariation(config)) isPrimaryAbi := i == 0 @@ -477,7 +501,9 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) { // Add native modules targetting the first ABI addDependenciesForNativeModules(ctx, a.properties.Multilib.First.Native_shared_libs, - a.properties.Multilib.First.Binaries, target.String(), + a.properties.Multilib.First.Binaries, + a.properties.Multilib.First.Tests, + target.String(), a.getImageVariation(config)) // When multilib.* is omitted for prebuilts, it implies multilib.first. @@ -491,24 +517,32 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) { // Add native modules targetting 32-bit ABI addDependenciesForNativeModules(ctx, a.properties.Multilib.Lib32.Native_shared_libs, - a.properties.Multilib.Lib32.Binaries, target.String(), + a.properties.Multilib.Lib32.Binaries, + a.properties.Multilib.Lib32.Tests, + target.String(), a.getImageVariation(config)) addDependenciesForNativeModules(ctx, a.properties.Multilib.Prefer32.Native_shared_libs, - a.properties.Multilib.Prefer32.Binaries, target.String(), + a.properties.Multilib.Prefer32.Binaries, + a.properties.Multilib.Prefer32.Tests, + target.String(), a.getImageVariation(config)) case "lib64": // Add native modules targetting 64-bit ABI addDependenciesForNativeModules(ctx, a.properties.Multilib.Lib64.Native_shared_libs, - a.properties.Multilib.Lib64.Binaries, target.String(), + a.properties.Multilib.Lib64.Binaries, + a.properties.Multilib.Lib64.Tests, + target.String(), a.getImageVariation(config)) if !has32BitTarget { addDependenciesForNativeModules(ctx, a.properties.Multilib.Prefer32.Native_shared_libs, - a.properties.Multilib.Prefer32.Binaries, target.String(), + a.properties.Multilib.Prefer32.Binaries, + a.properties.Multilib.Prefer32.Tests, + target.String(), a.getImageVariation(config)) } @@ -517,7 +551,7 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) { if sanitizer == "hwaddress" { addDependenciesForNativeModules(ctx, []string{"libclang_rt.hwasan-aarch64-android"}, - nil, target.String(), a.getImageVariation(config)) + nil, nil, target.String(), a.getImageVariation(config)) break } } @@ -686,6 +720,11 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { return } + if len(a.properties.Tests) > 0 && !a.testApex { + ctx.PropertyErrorf("tests", "property not allowed in apex module type") + return + } + handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case) ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool { @@ -747,6 +786,14 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { } else { ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName) } + case testTag: + if cc, ok := child.(*cc.Module); ok { + fileToCopy, dirInApex := getCopyManifestForExecutable(cc) + filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeTest, cc, nil}) + return true + } else { + ctx.PropertyErrorf("tests", "%q is not a cc module", depName) + } case keyTag: if key, ok := child.(*apexKey); ok { a.private_key_file = key.private_key_file @@ -858,8 +905,7 @@ func (a *apexBundle) buildNoticeFile(ctx android.ModuleContext, apexFileName str return android.OptionalPath{} } - return android.OptionalPathForPath( - android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.FirstUniquePaths(noticeFiles))) + return android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.FirstUniquePaths(noticeFiles)).HtmlGzOutput } func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, apexType apexPackaging) { @@ -916,7 +962,7 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, apexType ap var executablePaths []string // this also includes dirs for _, f := range a.filesInfo { pathInApex := filepath.Join(f.installDir, f.builtFile.Base()) - if f.installDir == "bin" { + if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") { executablePaths = append(executablePaths, pathInApex) for _, s := range f.symlinks { executablePaths = append(executablePaths, filepath.Join("bin", s)) @@ -1253,11 +1299,11 @@ func (a *apexBundle) androidMkForType(apexType apexPackaging) android.AndroidMkD } func testApexBundleFactory() android.Module { - return ApexBundleFactory( /*testApex*/ true) + return ApexBundleFactory(true /*testApex*/) } func apexBundleFactory() android.Module { - return ApexBundleFactory( /*testApex*/ false) + return ApexBundleFactory(false /*testApex*/) } func ApexBundleFactory(testApex bool) android.Module { diff --git a/apex/apex_test.go b/apex/apex_test.go index 2e44db7c7..b410425ff 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -57,6 +57,7 @@ func testApex(t *testing.T, bp string) *android.TestContext { ctx.RegisterModuleType("cc_library_headers", android.ModuleFactoryAdaptor(cc.LibraryHeaderFactory)) ctx.RegisterModuleType("cc_binary", android.ModuleFactoryAdaptor(cc.BinaryFactory)) ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc.ObjectFactory)) + ctx.RegisterModuleType("cc_test", android.ModuleFactoryAdaptor(cc.TestFactory)) ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(cc.LlndkLibraryFactory)) ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(cc.ToolchainLibraryFactory)) ctx.RegisterModuleType("prebuilt_etc", android.ModuleFactoryAdaptor(android.PrebuiltEtcFactory)) @@ -168,6 +169,7 @@ func testApex(t *testing.T, bp string) *android.TestContext { "system/sepolicy/apex/myapex_keytest-file_contexts": nil, "system/sepolicy/apex/otherapex-file_contexts": nil, "mylib.cpp": nil, + "mytest.cpp": nil, "myprebuilt": nil, "my_include": nil, "vendor/foo/devkeys/test.x509.pem": nil, @@ -1285,6 +1287,40 @@ func TestPrebuiltFilenameOverride(t *testing.T) { } } +func TestApexWithTests(t *testing.T) { + ctx := testApex(t, ` + apex_test { + name: "myapex", + key: "myapex.key", + tests: [ + "mytest", + ], + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_test { + name: "mytest", + gtest: false, + srcs: ["mytest.cpp"], + relative_install_path: "test", + system_shared_libs: [], + static_executable: true, + stl: "none", + } + `) + + apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") + copyCmds := apexRule.Args["copy_commands"] + + // Ensure that test dep is copied into apex. + ensureContains(t, copyCmds, "image.apex/bin/test/mytest") +} + func TestMain(m *testing.M) { run := func() int { setUp() diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go index 41c7d46ce..30381e088 100644 --- a/cmd/soong_build/main.go +++ b/cmd/soong_build/main.go @@ -18,7 +18,12 @@ import ( "flag" "fmt" "os" + "os/exec" "path/filepath" + "strconv" + "strings" + "syscall" + "time" "github.com/google/blueprint/bootstrap" @@ -50,6 +55,42 @@ func newNameResolver(config android.Config) *android.NameResolver { } func main() { + if android.SoongDelveListen != "" { + if android.SoongDelvePath == "" { + fmt.Fprintln(os.Stderr, "SOONG_DELVE is set but failed to find dlv") + os.Exit(1) + } + pid := strconv.Itoa(os.Getpid()) + cmd := []string{android.SoongDelvePath, + "attach", pid, + "--headless", + "-l", android.SoongDelveListen, + "--api-version=2", + "--accept-multiclient", + "--log", + } + + fmt.Println("Starting", strings.Join(cmd, " ")) + dlv := exec.Command(cmd[0], cmd[1:]...) + dlv.Stdout = os.Stdout + dlv.Stderr = os.Stderr + dlv.Stdin = nil + + // Put dlv into its own process group so we can kill it and the child process it starts. + dlv.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + + err := dlv.Start() + if err != nil { + // Print the error starting dlv and continue. + fmt.Println(err) + } else { + // Kill the process group for dlv when soong_build exits. + defer syscall.Kill(-dlv.Process.Pid, syscall.SIGKILL) + // Wait to give dlv a chance to connect and pause the process. + time.Sleep(time.Second) + } + } + flag.Parse() // The top-level Blueprints file is passed as the first argument. @@ -72,7 +113,17 @@ func main() { ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies()) - bootstrap.Main(ctx.Context, configuration, configuration.ConfigFileName, configuration.ProductVariablesFileName) + extraNinjaDeps := []string{configuration.ConfigFileName, configuration.ProductVariablesFileName} + + // Read the SOONG_DELVE again through configuration so that there is a dependency on the environment variable + // and soong_build will rerun when it is set for the first time. + if listen := configuration.Getenv("SOONG_DELVE"); listen != "" { + // Add a non-existent file to the dependencies so that soong_build will rerun when the debugger is + // enabled even if it completed successfully. + extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.BuildDir(), "always_rerun_for_delve")) + } + + bootstrap.Main(ctx.Context, configuration, extraNinjaDeps...) if docFile != "" { if err := writeDocs(ctx, docFile); err != nil { diff --git a/java/androidmk.go b/java/androidmk.go index 39c2d1342..90fdd0f77 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -327,6 +327,18 @@ func (app *AndroidApp) AndroidMk() android.AndroidMkData { install := "$(LOCAL_MODULE_PATH)/" + strings.TrimSuffix(app.installApkName, ".apk") + split.suffix + ".apk" fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED +=", split.path.String()+":"+install) } + if app.noticeOutputs.Merged.Valid() { + fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n", + app.installApkName, app.noticeOutputs.Merged.String(), app.installApkName+"_NOTICE") + } + if app.noticeOutputs.TxtOutput.Valid() { + fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n", + app.installApkName, app.noticeOutputs.TxtOutput.String(), app.installApkName+"_NOTICE.txt") + } + if app.noticeOutputs.HtmlOutput.Valid() { + fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n", + app.installApkName, app.noticeOutputs.HtmlOutput.String(), app.installApkName+"_NOTICE.html") + } }, }, } diff --git a/java/app.go b/java/app.go index f58b0f836..e12b56c58 100644 --- a/java/app.go +++ b/java/app.go @@ -141,6 +141,8 @@ type AndroidApp struct { installApkName string additionalAaptFlags []string + + noticeOutputs android.NoticeOutputs } func (a *AndroidApp) ExportedProguardFlagFiles() android.Paths { @@ -357,11 +359,7 @@ func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext return jniJarFile } -func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext, installDir android.OutputPath) android.OptionalPath { - if !Bool(a.appProperties.Embed_notices) && !ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") { - return android.OptionalPath{} - } - +func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext, installDir android.OutputPath) { // Collect NOTICE files from all dependencies. seenModules := make(map[android.Module]bool) noticePathSet := make(map[android.Path]bool) @@ -391,7 +389,7 @@ func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext, installDir an } if len(noticePathSet) == 0 { - return android.OptionalPath{} + return } var noticePaths []android.Path for path := range noticePathSet { @@ -400,9 +398,8 @@ func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext, installDir an sort.Slice(noticePaths, func(i, j int) bool { return noticePaths[i].String() < noticePaths[j].String() }) - noticeFile := android.BuildNoticeOutput(ctx, installDir, a.installApkName+".apk", noticePaths) - return android.OptionalPathForPath(noticeFile) + a.noticeOutputs = android.BuildNoticeOutput(ctx, installDir, a.installApkName+".apk", noticePaths) } // Reads and prepends a main cert from the default cert dir if it hasn't been set already, i.e. it @@ -455,7 +452,10 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { installDir = android.PathForModuleInstall(ctx, "app", a.installApkName) } - a.aapt.noticeFile = a.noticeBuildActions(ctx, installDir) + a.noticeBuildActions(ctx, installDir) + if Bool(a.appProperties.Embed_notices) || ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") { + a.aapt.noticeFile = a.noticeOutputs.HtmlGzOutput + } // Process all building blocks, from AAPT to certificates. a.aaptBuildActions(ctx) diff --git a/java/app_test.go b/java/app_test.go index c7ea338b1..721dd4dc2 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -1479,16 +1479,20 @@ func TestEmbedNotice(t *testing.T) { // bar has NOTICE files to process, but embed_notices is not set. bar := ctx.ModuleForTests("bar", "android_common") - mergeNotices = bar.MaybeRule("mergeNoticesRule") - if mergeNotices.Rule != nil { - t.Errorf("mergeNotices shouldn't have run for bar") + res = bar.Output("package-res.apk") + aapt2Flags = res.Args["flags"] + e = "-A " + buildDir + "/.intermediates/bar/android_common/NOTICE" + if strings.Contains(aapt2Flags, e) { + t.Errorf("bar shouldn't have the asset dir flag for NOTICE: %q", e) } // baz's embed_notice is true, but it doesn't have any NOTICE files. baz := ctx.ModuleForTests("baz", "android_common") - mergeNotices = baz.MaybeRule("mergeNoticesRule") - if mergeNotices.Rule != nil { - t.Errorf("mergeNotices shouldn't have run for baz") + res = baz.Output("package-res.apk") + aapt2Flags = res.Args["flags"] + e = "-A " + buildDir + "/.intermediates/baz/android_common/NOTICE" + if strings.Contains(aapt2Flags, e) { + t.Errorf("baz shouldn't have the asset dir flag for NOTICE: %q", e) } } diff --git a/ui/build/config.go b/ui/build/config.go index 4a70f063f..4e19e9e91 100644 --- a/ui/build/config.go +++ b/ui/build/config.go @@ -266,6 +266,10 @@ func getConfigArgs(action BuildAction, dir string, buildDependencies bool, ctx C if err != nil { ctx.Fatalf("Error retrieving top directory: %v", err) } + dir, err = filepath.EvalSymlinks(dir) + if err != nil { + ctx.Fatalf("Unable to evaluate symlink of %s: %v", dir, err) + } dir, err = filepath.Abs(dir) if err != nil { ctx.Fatalf("Unable to find absolute path %s: %v", dir, err) diff --git a/ui/build/config_test.go b/ui/build/config_test.go index 856af117f..7a1ee1703 100644 --- a/ui/build/config_test.go +++ b/ui/build/config_test.go @@ -690,6 +690,9 @@ type buildActionTestCase struct { // Build files that exists in the source tree. buildFiles []string + // Create root symlink that points to topDir. + rootSymlink bool + // ********* Action ********* // Arguments passed in to soong_ui. args []string @@ -738,6 +741,22 @@ func testGetConfigArgs(t *testing.T, tt buildActionTestCase, action BuildAction, createDirectories(t, topDir, tt.dirsInTrees) createBuildFiles(t, topDir, tt.buildFiles) + if tt.rootSymlink { + // Create a secondary root source tree which points to the true root source tree. + symlinkTopDir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatalf("failed to create symlink temp dir: %v", err) + } + defer os.RemoveAll(symlinkTopDir) + + symlinkTopDir = filepath.Join(symlinkTopDir, "root") + err = os.Symlink(topDir, symlinkTopDir) + if err != nil { + t.Fatalf("failed to create symlink: %v", err) + } + topDir = symlinkTopDir + } + r := setTop(t, topDir) defer r() @@ -796,7 +815,17 @@ func TestGetConfigArgsBuildModules(t *testing.T) { dirsInTrees: []string{"0/1/2", "0/2", "0/3"}, buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp"}, args: []string{}, - curDir: "1/2/3/4/5/6/7/8/9", + curDir: "0/2", + tidyOnly: "", + expectedArgs: []string{}, + expectedEnvVars: []envVar{}, + }, { + description: "normal execution in symlink root source tree, no args", + dirsInTrees: []string{"0/1/2", "0/2", "0/3"}, + buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp"}, + rootSymlink: true, + args: []string{}, + curDir: "0/2", tidyOnly: "", expectedArgs: []string{}, expectedEnvVars: []envVar{}, @@ -932,6 +961,17 @@ func TestGetConfigArgsBuildModulesInDirectory(t *testing.T) { description: "build action executed at root directory", dirsInTrees: []string{}, buildFiles: []string{}, + rootSymlink: false, + args: []string{}, + curDir: ".", + tidyOnly: "", + expectedArgs: []string{}, + expectedEnvVars: []envVar{}, + }, { + description: "build action executed at root directory in symlink", + dirsInTrees: []string{}, + buildFiles: []string{}, + rootSymlink: true, args: []string{}, curDir: ".", tidyOnly: "", @@ -1080,6 +1120,20 @@ func TestGetConfigArgsBuildModulesInDirectories(t *testing.T) { description: "normal execution from top dir directory", dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"}, buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/3/Android.bp", "0/2/Android.bp"}, + rootSymlink: false, + args: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"}, + curDir: ".", + tidyOnly: "", + expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-3", "MODULES-IN-0-2"}, + expectedEnvVars: []envVar{ + envVar{ + name: "ONE_SHOT_MAKEFILE", + value: ""}}, + }, { + description: "normal execution from top dir directory in symlink", + dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"}, + buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/3/Android.bp", "0/2/Android.bp"}, + rootSymlink: true, args: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"}, curDir: ".", tidyOnly: "", diff --git a/ui/build/exec.go b/ui/build/exec.go index 5c312bcd9..e435c53be 100644 --- a/ui/build/exec.go +++ b/ui/build/exec.go @@ -15,7 +15,10 @@ package build import ( + "bufio" + "io" "os/exec" + "strings" ) // Cmd is a wrapper of os/exec.Cmd that integrates with the build context for @@ -139,3 +142,34 @@ func (c *Cmd) RunAndPrintOrFatal() { st.Finish() c.reportError(err) } + +// RunAndStreamOrFatal will run the command, while running print +// any output, then handle any errors with a call to ctx.Fatal +func (c *Cmd) RunAndStreamOrFatal() { + out, err := c.StdoutPipe() + if err != nil { + c.ctx.Fatal(err) + } + c.Stderr = c.Stdout + + st := c.ctx.Status.StartTool() + + c.StartOrFatal() + + buf := bufio.NewReaderSize(out, 2*1024*1024) + for { + // Attempt to read whole lines, but write partial lines that are too long to fit in the buffer or hit EOF + line, err := buf.ReadString('\n') + if line != "" { + st.Print(strings.TrimSuffix(line, "\n")) + } else if err == io.EOF { + break + } else if err != nil { + c.ctx.Fatal(err) + } + } + + err = c.Wait() + st.Finish() + c.reportError(err) +} diff --git a/ui/build/ninja.go b/ui/build/ninja.go index 7994f3a2d..b41ac208a 100644 --- a/ui/build/ninja.go +++ b/ui/build/ninja.go @@ -103,7 +103,7 @@ func runNinja(ctx Context, config Config) { }() ctx.Status.Status("Starting ninja...") - cmd.RunAndPrintOrFatal() + cmd.RunAndStreamOrFatal() } type statusChecker struct { diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go index e2c504338..a4be2ac47 100644 --- a/ui/build/paths/config.go +++ b/ui/build/paths/config.go @@ -81,6 +81,7 @@ var Configuration = map[string]PathConfig{ "bzip2": Allowed, "dd": Allowed, "diff": Allowed, + "dlv": Allowed, "egrep": Allowed, "expr": Allowed, "find": Allowed, diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go index b94db7448..11ff6677c 100644 --- a/ui/build/sandbox_linux.go +++ b/ui/build/sandbox_linux.go @@ -162,6 +162,10 @@ func (c *Cmd) wrapSandbox() { c.ctx.Printf("AllowBuildBrokenUsesNetwork: %v", c.Sandbox.AllowBuildBrokenUsesNetwork) c.ctx.Printf("BuildBrokenUsesNetwork: %v", c.config.BuildBrokenUsesNetwork()) sandboxArgs = append(sandboxArgs, "-N") + } else if dlv, _ := c.config.Environment().Get("SOONG_DELVE"); dlv != "" { + // The debugger is enabled and soong_build will pause until a remote delve process connects, allow + // network connections. + sandboxArgs = append(sandboxArgs, "-N") } // Stop nsjail from parsing arguments diff --git a/ui/build/soong.go b/ui/build/soong.go index 2ce1ac935..338841702 100644 --- a/ui/build/soong.go +++ b/ui/build/soong.go @@ -120,7 +120,7 @@ func runSoong(ctx Context, config Config) { "--frontend_file", fifo, "-f", filepath.Join(config.SoongOutDir(), file)) cmd.Sandbox = soongSandbox - cmd.RunAndPrintOrFatal() + cmd.RunAndStreamOrFatal() } ninja("minibootstrap", ".minibootstrap/build.ninja") diff --git a/ui/terminal/util.go b/ui/terminal/util.go index f383ef19e..7a603d7f7 100644 --- a/ui/terminal/util.go +++ b/ui/terminal/util.go @@ -23,10 +23,10 @@ import ( ) func isSmartTerminal(w io.Writer) bool { - if term, ok := os.LookupEnv("TERM"); ok && term == "dumb" { - return false - } if f, ok := w.(*os.File); ok { + if term, ok := os.LookupEnv("TERM"); ok && term == "dumb" { + return false + } var termios syscall.Termios _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(), ioctlGetTermios, uintptr(unsafe.Pointer(&termios)), |