From c150541ed0a72fdbef546fa8b5aeff9428ca0395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Tue, 1 Jul 2025 15:00:52 +0200 Subject: [PATCH 1/2] Refactor to extract a func 'gorun' --- main.go | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/main.go b/main.go index dd9557a..c531ac7 100644 --- a/main.go +++ b/main.go @@ -135,6 +135,21 @@ func runTime(cmd *exec.Cmd) error { return cmd.Run() } +func gorun(srcFilename string, env []string, dir string, args ...string) error { + runArgs := make([]string, 0, 3+len(args)) + runArgs = append(runArgs, "run", srcFilename, "--") + runArgs = append(runArgs, args...) + // log.Println(goCmd, runArgs) + + cmd := exec.Command(goCmd, runArgs...) + cmd.Env = env + cmd.Dir = dir // In Go module mode we run from the temp module dir + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return run(cmd) +} + var goCmd = "go" func getGOMODCACHE(env []string) (string, error) { @@ -391,22 +406,11 @@ func _main() error { srcFinal = f srcFilename = f.Name() - runArgs := make([]string, 0, 3+len(args)) - runArgs = append(runArgs, "run", srcFilename, "--") - runArgs = append(runArgs, args...) - // log.Println(goCmd, runArgs) - - cmd := exec.Command(goCmd, runArgs...) - cmd.Env = env - cmd.Dir = dir // In Go module mode we run from the temp module dir - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr tail = func() error { if err = f.Close(); err != nil { return err } - return run(cmd) + return gorun(srcFilename, env, dir, args...) } case actionPlay: var cleanup func() From c3a0246cc78d3af87e03c6675b702736dd8db301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Tue, 1 Jul 2025 15:54:58 +0200 Subject: [PATCH 2/2] Internal implementation of "go run" Stop using "go run" to compile and execute the user's code as well as our capsules (sub/). Instead, call "go build" and then launch the binary. This allows: - proper exit status code - no "exit status #" display - no conflict with 'go' command arguments parsing - no artificial import of "os" in the program Closes #9. --- main.go | 52 ++++++++++++++++++++++++++++++++++------------------ main_test.go | 7 +------ 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/main.go b/main.go index c531ac7..d896844 100644 --- a/main.go +++ b/main.go @@ -26,6 +26,7 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "strings" "time" @@ -135,19 +136,38 @@ func runTime(cmd *exec.Cmd) error { return cmd.Run() } -func gorun(srcFilename string, env []string, dir string, args ...string) error { - runArgs := make([]string, 0, 3+len(args)) - runArgs = append(runArgs, "run", srcFilename, "--") - runArgs = append(runArgs, args...) - // log.Println(goCmd, runArgs) +func gorun(srcFilename string, env []string, buildDir string, runDir string, args ...string) error { + exeDir, err := os.MkdirTemp("", "goeval*") + if err != nil { + return err + } + defer func() { + if err := os.RemoveAll(exeDir); err != nil { + log.Printf("RemoveAll(%q): %v", exeDir, err) + } + }() - cmd := exec.Command(goCmd, runArgs...) - cmd.Env = env - cmd.Dir = dir // In Go module mode we run from the temp module dir - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - return run(cmd) + exePath := filepath.Join(exeDir, "goeval-run") + if runtime.GOOS == "windows" { + exePath += ".exe" + } + + cmdBuild := exec.Command(goCmd, "build", "-o", exePath, srcFilename) + cmdBuild.Env = env + cmdBuild.Dir = buildDir + cmdBuild.Stdout = os.Stdout + cmdBuild.Stderr = os.Stderr + if err := run(cmdBuild); err != nil { + return fmt.Errorf("failed to build: %w", err) + } + + cmdRun := exec.Command(exePath, args...) + cmdRun.Env = env + cmdRun.Dir = runDir // In Go module mode we run from the temp module dir + cmdRun.Stdin = os.Stdin + cmdRun.Stdout = os.Stdout + cmdRun.Stderr = os.Stderr + return run(cmdRun) } var goCmd = "go" @@ -202,7 +222,7 @@ func flagAction(name string, a actionBits, usage string) { func _main() error { imports := imports{ - packages: map[string]string{" ": "os"}, + packages: map[string]string{}, onlySemVer: true, } flag.Var(&imports, "i", "* import package: [alias=]import-path\n* switch to Go module mode and import package: [alias=]import-path@version") @@ -381,10 +401,6 @@ func _main() error { } src.WriteString("func main() {\n") if action <= actionDump { - src.WriteString("os.Args[1] = os.Args[0]\nos.Args = os.Args[1:]\n") - if moduleMode { - fmt.Fprintf(&src, "_ = os.Chdir(%q)\n", origDir) - } src.WriteString("//line :1\n") } src.WriteString(code) @@ -410,7 +426,7 @@ func _main() error { if err = f.Close(); err != nil { return err } - return gorun(srcFilename, env, dir, args...) + return gorun(srcFilename, env, dir, origDir, args...) } case actionPlay: var cleanup func() diff --git a/main_test.go b/main_test.go index 2780e49..880ce5a 100644 --- a/main_test.go +++ b/main_test.go @@ -42,14 +42,9 @@ func Example_dump() { // Output: // package main // - // import ( - // "fmt" - // "os" - // ) + // import "fmt" // // func main() { - // os.Args[1] = os.Args[0] - // os.Args = os.Args[1:] // //line :1 // fmt.Println("OK") // }