Make it possible to debug soong_ui.
This works by setting the SOONG_UI_DELVE= environment variable to the
port on which soong_ui should accept a Delve connection on.
This is achieved by reusing the Delve execution logic between soong_ui
and soong_build.
Test: Manual.
Change-Id: Id2c1d4b6faac1a4a3918c91030ce2239f7daf54f
diff --git a/android/env.go b/android/env.go
index 58ad0b6..289d803 100644
--- a/android/env.go
+++ b/android/env.go
@@ -15,11 +15,6 @@
package android
import (
- "fmt"
- "os"
- "strings"
- "syscall"
-
"android/soong/shared"
)
@@ -31,9 +26,6 @@
// a manifest regeneration.
var originalEnv map[string]string
-var soongDelveListen string
-var soongDelvePath string
-var isDebugging bool
func InitEnvironment(envFile string) {
var err error
@@ -41,51 +33,6 @@
if err != nil {
panic(err)
}
-
- soongDelveListen = originalEnv["SOONG_DELVE"]
- soongDelvePath = originalEnv["SOONG_DELVE_PATH"]
-}
-
-// Returns whether the current process is running under Delve due to
-// ReexecWithDelveMaybe().
-func IsDebugging() bool {
- return isDebugging
-}
-func ReexecWithDelveMaybe() {
- isDebugging = os.Getenv("SOONG_DELVE_REEXECUTED") == "true"
- if isDebugging || soongDelveListen == "" {
- return
- }
-
- if soongDelvePath == "" {
- fmt.Fprintln(os.Stderr, "SOONG_DELVE is set but failed to find dlv")
- os.Exit(1)
- }
-
- soongDelveEnv := []string{}
- for _, env := range os.Environ() {
- idx := strings.IndexRune(env, '=')
- if idx != -1 {
- soongDelveEnv = append(soongDelveEnv, env)
- }
- }
-
- soongDelveEnv = append(soongDelveEnv, "SOONG_DELVE_REEXECUTED=true")
-
- dlvArgv := []string{
- soongDelvePath,
- "--listen=:" + soongDelveListen,
- "--headless=true",
- "--api-version=2",
- "exec",
- os.Args[0],
- "--",
- }
- dlvArgv = append(dlvArgv, os.Args[1:]...)
- os.Chdir(absSrcDir)
- syscall.Exec(soongDelvePath, dlvArgv, soongDelveEnv)
- fmt.Fprintln(os.Stderr, "exec() failed while trying to reexec with Delve")
- os.Exit(1)
}
func EnvSingleton() Singleton {
diff --git a/android/writedocs.go b/android/writedocs.go
index 6417690..6cb2f10 100644
--- a/android/writedocs.go
+++ b/android/writedocs.go
@@ -66,7 +66,9 @@
soongDocs := ctx.Rule(pctx, "soongDocs",
blueprint.RuleParams{
Command: fmt.Sprintf("rm -f ${outDir}/* && %s --soong_docs %s %s",
- primaryBuilder.String(), docsFile.String(), strings.Join(os.Args[1:], " ")),
+ primaryBuilder.String(),
+ docsFile.String(),
+ "\""+strings.Join(os.Args[1:], "\" \"")+"\""),
CommandDeps: []string{primaryBuilder.String()},
Description: fmt.Sprintf("%s docs $out", primaryBuilder.Base()),
},
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index e7f995f..d022f49 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -33,11 +33,15 @@
outDir string
docFile string
bazelQueryViewDir string
+ delveListen string
+ delvePath string
)
func init() {
flag.StringVar(&topDir, "top", "", "Top directory of the Android source tree")
flag.StringVar(&outDir, "out", "", "Soong output directory (usually $TOP/out/soong)")
+ flag.StringVar(&delveListen, "delve_listen", "", "Delve port to listen on for debugging")
+ flag.StringVar(&delvePath, "delve_path", "", "Path to Delve. Only used if --delve_listen is set")
flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output")
flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory")
}
@@ -87,9 +91,9 @@
func main() {
flag.Parse()
+ shared.ReexecWithDelveMaybe(delveListen, delvePath)
android.InitSandbox(topDir)
android.InitEnvironment(shared.JoinPath(topDir, outDir, "soong.environment.available"))
- android.ReexecWithDelveMaybe()
// The top-level Blueprints file is passed as the first argument.
srcDir := filepath.Dir(flag.Arg(0))
@@ -101,9 +105,7 @@
// user sets SOONG_DELVE the first time.
configuration.Getenv("SOONG_DELVE")
configuration.Getenv("SOONG_DELVE_PATH")
- // 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 != "" {
+ if shared.IsDebugging() {
// 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"))
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 74ede68..1c5e78a 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -25,6 +25,7 @@
"strings"
"time"
+ "android/soong/shared"
"android/soong/ui/build"
"android/soong/ui/logger"
"android/soong/ui/metrics"
@@ -118,6 +119,8 @@
// Command is the type of soong_ui execution. Only one type of
// execution is specified. The args are specific to the command.
func main() {
+ shared.ReexecWithDelveMaybe(os.Getenv("SOONG_UI_DELVE"), shared.ResolveDelveBinary())
+
buildStarted := time.Now()
c, args, err := getCommand(os.Args)
diff --git a/shared/debug.go b/shared/debug.go
index 0c9ba4f..5392f2b 100644
--- a/shared/debug.go
+++ b/shared/debug.go
@@ -1,8 +1,15 @@
package shared
import (
+ "fmt"
"os"
"os/exec"
+ "strings"
+ "syscall"
+)
+
+var (
+ isDebugging bool
)
// Finds the Delve binary to use. Either uses the SOONG_DELVE_PATH environment
@@ -15,3 +22,48 @@
return result
}
+
+// Returns whether the current process is running under Delve due to
+// ReexecWithDelveMaybe().
+func IsDebugging() bool {
+ return isDebugging
+}
+
+// Re-executes the binary in question under the control of Delve when
+// delveListen is not the empty string. delvePath gives the path to the Delve.
+func ReexecWithDelveMaybe(delveListen, delvePath string) {
+ isDebugging = os.Getenv("SOONG_DELVE_REEXECUTED") == "true"
+ if isDebugging || delveListen == "" {
+ return
+ }
+
+ if delvePath == "" {
+ fmt.Fprintln(os.Stderr, "Delve debugging requested but failed to find dlv")
+ os.Exit(1)
+ }
+
+ soongDelveEnv := []string{}
+ for _, env := range os.Environ() {
+ idx := strings.IndexRune(env, '=')
+ if idx != -1 {
+ soongDelveEnv = append(soongDelveEnv, env)
+ }
+ }
+
+ soongDelveEnv = append(soongDelveEnv, "SOONG_DELVE_REEXECUTED=true")
+
+ dlvArgv := []string{
+ delvePath,
+ "--listen=:" + delveListen,
+ "--headless=true",
+ "--api-version=2",
+ "exec",
+ os.Args[0],
+ "--",
+ }
+
+ dlvArgv = append(dlvArgv, os.Args[1:]...)
+ syscall.Exec(delvePath, dlvArgv, soongDelveEnv)
+ fmt.Fprintln(os.Stderr, "exec() failed while trying to reexec with Delve")
+ os.Exit(1)
+}
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 242282b..c2fa427 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -105,11 +105,6 @@
soongBuildEnv.Set("BAZEL_WORKSPACE", absPath(ctx, "."))
soongBuildEnv.Set("BAZEL_METRICS_DIR", config.BazelMetricsDir())
- if os.Getenv("SOONG_DELVE") != "" {
- // SOONG_DELVE is already in cmd.Environment
- soongBuildEnv.Set("SOONG_DELVE_PATH", shared.ResolveDelveBinary())
- }
-
err := writeEnvironmentFile(ctx, envFile, soongBuildEnv.AsMap())
if err != nil {
ctx.Fatalf("failed to write environment file %s: %s", envFile, err)
@@ -176,6 +171,11 @@
"-f", filepath.Join(config.SoongOutDir(), file))
cmd.Environment.Set("SOONG_OUTDIR", config.SoongOutDir())
+ if os.Getenv("SOONG_DELVE") != "" {
+ // SOONG_DELVE is already in cmd.Environment
+ cmd.Environment.Set("SOONG_DELVE_PATH", shared.ResolveDelveBinary())
+ }
+
cmd.Sandbox = soongSandbox
cmd.RunAndStreamOrFatal()
}