diff options
-rw-r--r-- | GNUmakefile | 11 | ||||
-rw-r--r-- | main.go | 54 | ||||
-rw-r--r-- | tests/testing/chdir/chdir.go | 27 |
3 files changed, 91 insertions, 1 deletions
diff --git a/GNUmakefile b/GNUmakefile index 7333a6ebc..70a7c5f58 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -483,8 +483,17 @@ tinygo-baremetal: # regression test for #2666: e.g. encoding/hex must pass on baremetal $(TINYGO) test -target cortex-m-qemu encoding/hex +.PHONY: testchdir +testchdir: + # test 'build' command with{,out} -C argument + $(TINYGO) build -C tests/testing/chdir chdir.go && rm tests/testing/chdir/chdir + $(TINYGO) build ./tests/testing/chdir/chdir.go && rm chdir + # test 'run' command with{,out} -C argument + EXPECT_DIR=$(PWD)/tests/testing/chdir $(TINYGO) run -C tests/testing/chdir chdir.go + EXPECT_DIR=$(PWD) $(TINYGO) run ./tests/testing/chdir/chdir.go + .PHONY: smoketest -smoketest: +smoketest: testchdir $(TINYGO) version $(TINYGO) targets > /dev/null # regression test for #2892 @@ -1461,6 +1461,7 @@ func main() { // Early command processing, before commands are interpreted by the Go flag // library. + handleChdirFlag() switch command { case "clang", "ld.lld", "wasm-ld": err := builder.RunTool(command, os.Args[2:]...) @@ -1946,3 +1947,56 @@ type outputEntry struct { stderr bool data []byte } + +// handleChdirFlag handles the -C flag before doing anything else. +// The -C flag must be the first flag on the command line, to make it easy to find +// even with commands that have custom flag parsing. +// handleChdirFlag handles the flag by chdir'ing to the directory +// and then removing that flag from the command line entirely. +// +// We have to handle the -C flag this way for two reasons: +// +// 1. Toolchain selection needs to be in the right directory to look for go.mod and go.work. +// +// 2. A toolchain switch later on reinvokes the new go command with the same arguments. +// The parent toolchain has already done the chdir; the child must not try to do it again. + +func handleChdirFlag() { + used := 2 // b.c. command at os.Args[1] + if used >= len(os.Args) { + return + } + + var dir string + switch a := os.Args[used]; { + default: + return + + case a == "-C", a == "--C": + if used+1 >= len(os.Args) { + return + } + dir = os.Args[used+1] + os.Args = slicesDelete(os.Args, used, used+2) + + case strings.HasPrefix(a, "-C="), strings.HasPrefix(a, "--C="): + _, dir, _ = strings.Cut(a, "=") + os.Args = slicesDelete(os.Args, used, used+1) + } + + if err := os.Chdir(dir); err != nil { + fmt.Fprintln(os.Stderr, "cannot chdir:", err) + os.Exit(1) + } +} + +// go1.19 compatibility: lacks slices package +func slicesDelete[S ~[]E, E any](s S, i, j int) S { + _ = s[i:j:len(s)] // bounds check + + if i == j { + return s + } + + return append(s[:i], s[j:]...) +} diff --git a/tests/testing/chdir/chdir.go b/tests/testing/chdir/chdir.go new file mode 100644 index 000000000..75281c21f --- /dev/null +++ b/tests/testing/chdir/chdir.go @@ -0,0 +1,27 @@ +package main + +import ( + "log" + "os" + "path/filepath" + "runtime" +) + +/* +Test that this program is 'run' in expected directory. 'run' with expected +working-directory in 'EXPECT_DIR' environment variable' with{,out} a -C +argument. +*/ +func main() { + expectDir := os.Getenv("EXPECT_DIR") + cwd, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + if runtime.GOOS == "windows" { + cwd = filepath.ToSlash(cwd) + } + if cwd != expectDir { + log.Fatalf("expected:\"%v\" != os.Getwd():\"%v\"", expectDir, cwd) + } +} |