From 9cc3384cd6405615a750d967bd90a241979292b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Thu, 24 Jul 2025 15:10:36 +0200 Subject: [PATCH] Add flag -o to just build a binary Closes #15. --- main.go | 60 ++++++++++++++++++++++++++++++++++++++-------------- main_test.go | 29 +++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 16 deletions(-) diff --git a/main.go b/main.go index 6ded346..d5901e0 100644 --- a/main.go +++ b/main.go @@ -137,19 +137,24 @@ func runTime(cmd *exec.Cmd) error { } 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) + exePath := buildOutput + if exePath == "" { + + exeDir, err := os.MkdirTemp("", "goeval*") + if err != nil { + return err } - }() - exePath := filepath.Join(exeDir, "goeval-run") - if runtime.GOOS == "windows" { - exePath += ".exe" + defer func() { + if err := os.RemoveAll(exeDir); err != nil { + log.Printf("RemoveAll(%q): %v", exeDir, err) + } + }() + + exePath = filepath.Join(exeDir, "goeval-run") + if runtime.GOOS == "windows" { + exePath += ".exe" + } } cmdBuild := exec.Command(goCmd, "build", @@ -171,6 +176,11 @@ func gorun(srcFilename string, env []string, buildDir string, runDir string, arg return fmt.Errorf("failed to build: %w", err) } + // actionBuild: don't run + if buildOutput != "" { + return nil + } + cmdRun := exec.Command(exePath, args...) cmdRun.Env = env cmdRun.Dir = runDir // In Go module mode we run from the temp module dir @@ -212,6 +222,7 @@ type actionBits uint const ( actionRun actionBits = iota + actionBuild // -o ... actionDump // -E actionDumpPlay // -Eplay actionPlay // -play @@ -220,7 +231,12 @@ const ( actionDefault = actionRun ) -var action actionBits +var ( + action actionBits + buildOutput string // -o + + errActionExclusive = errors.New("flags -o, -E, -Eplay, -play and -share are exclusive") +) func flagAction(name string, a actionBits, target *string, usage string) { flag.BoolFunc(name, usage, func(value string) error { @@ -228,7 +244,7 @@ func flagAction(name string, a actionBits, target *string, usage string) { return errors.New("no value expected") } if action != actionDefault { - return errors.New("flags -E, -Eplay, -play and -share are exclusive") + return errActionExclusive } action = a return nil @@ -256,6 +272,18 @@ func _main() error { // TODO allow to optionally set a different endpoint for the Go Playground + flag.Func("o", "just build a binary, don't execute.", func(value string) (err error) { + if action != actionDefault { + return errActionExclusive + } + if value == "" { + return errors.New("invalid empty output file") + } + action = actionBuild + buildOutput, err = filepath.Abs(value) + return + }) + showCmds := flag.Bool("x", false, "print commands executed.") flag.Usage = func() { @@ -294,7 +322,7 @@ func _main() error { args := flag.Args()[1:] if len(args) > 0 { switch action { - case actionDump: + case actionBuild, actionDump: return errors.New("arguments not expected") } } @@ -445,7 +473,7 @@ func _main() error { tail func() error ) switch action { - case actionRun: + case actionRun, actionBuild: f, err := os.CreateTemp(dir, "*.go") if err != nil { log.Fatal(err) @@ -521,7 +549,7 @@ func _main() error { */ // dump go.mod, go.sum - if moduleMode && action != actionRun { + if moduleMode && action >= actionDump { gomod, err := os.Open(dir + "/go.mod") if err != nil { log.Fatal(err) diff --git a/main_test.go b/main_test.go index 93a775a..fe179c9 100644 --- a/main_test.go +++ b/main_test.go @@ -20,6 +20,8 @@ import ( "bytes" "os" "os/exec" + "path/filepath" + "runtime" "testing" ) @@ -126,3 +128,30 @@ func TestPrintStack(t *testing.T) { // PrintStack sends output to stderr goevalPrint(t.Log, t.Log, `-i=runtime/debug`, `-goimports=`, `debug.PrintStack()`) } + +// Test "goeval -o ..." +func TestBuild(t *testing.T) { + tempDir := t.TempDir() + + exe := filepath.Join(tempDir, "x") + if runtime.GOOS == "windows" { + exe += ".exe" + } + t.Logf("Building %q...", exe) + + goevalT(t, `-o`, exe, `fmt.Print(os.Args[1])`) + + _, err := os.Stat(exe) + if err != nil { + t.Fatalf(`%q: %v`, exe, err) + } + + cmd := exec.Command(exe, `toto`) + out, err := cmd.Output() + if err != nil { + t.Fatalf(`exec(%q): %v`, exe, err) + } + if string(out) != "toto" { + t.Errorf(`output: got %q, expected "toto"`, out) + } +}