cmd/gomobile: clean up work directory after environment errors buildEnvInit creates its work directory before calling envInit. If an error occurs afterward, buildEnvInit returns without giving the caller a cleanup function, leaving the directory behind. Defer cleanupFn and invoke it whenever buildEnvInit returns an error. This covers future error paths and preserves -work behavior because cleanupFn already retains and prints the work directory when requested. Add a regression test using an NDK whose compiler is missing. Fixes golang/go#80901 Change-Id: I8557b11364769e95ac37353018b2123f9fc85e74 Reviewed-on: https://go-review.googlesource.com/c/mobile/+/816100 Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Hajime Hoshi <hajimehoshi@gmail.com> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Auto-Submit: Hajime Hoshi <hajimehoshi@gmail.com>
diff --git a/cmd/gomobile/env.go b/cmd/gomobile/env.go index b05ad04..3562ad5 100644 --- a/cmd/gomobile/env.go +++ b/cmd/gomobile/env.go
@@ -140,6 +140,11 @@ } removeAll(tmpdir) } + defer func() { + if err != nil { + cleanupFn() + } + }() if buildN { tmpdir = "$WORK" cleanupFn = func() {}
diff --git a/cmd/gomobile/env_test.go b/cmd/gomobile/env_test.go index ad4c040..e23b9f3 100644 --- a/cmd/gomobile/env_test.go +++ b/cmd/gomobile/env_test.go
@@ -5,12 +5,60 @@ package main import ( + "errors" "fmt" "os" "path/filepath" "testing" ) +func TestBuildEnvInitCleanup(t *testing.T) { + // Prepare an incomplete NDK without a compiler so that environment + // initialization fails after creating the work directory. + ndkRoot := t.TempDir() + metaDir := filepath.Join(ndkRoot, "meta") + if err := os.Mkdir(metaDir, 0755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(metaDir, "platforms.json"), []byte(`{"min":16,"max":32}`), 0644); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(metaDir, "abis.json"), []byte(`{}`), 0644); err != nil { + t.Fatal(err) + } + t.Setenv("ANDROID_NDK_HOME", ndkRoot) + + oldBuildAndroidAPI := buildAndroidAPI + oldBuildN := buildN + oldBuildWork := buildWork + oldTmpdir := tmpdir + t.Cleanup(func() { + buildAndroidAPI = oldBuildAndroidAPI + buildN = oldBuildN + buildWork = oldBuildWork + tmpdir = oldTmpdir + }) + buildAndroidAPI = minAndroidAPI + buildN = false + buildWork = false + + cleanup, err := buildEnvInit() + workDir := tmpdir + t.Cleanup(func() { os.RemoveAll(workDir) }) + if err == nil { + if cleanup != nil { + cleanup() + } + t.Fatal("buildEnvInit succeeded with an NDK missing its compiler") + } + if cleanup != nil { + t.Fatal("buildEnvInit returned a cleanup function with an error") + } + if _, err := os.Stat(tmpdir); !errors.Is(err, os.ErrNotExist) { + t.Errorf("work directory still exists after buildEnvInit failed: %v", err) + } +} + func TestNdkRoot(t *testing.T) { home, err := os.MkdirTemp("", "gomobile-test-") if err != nil {