From 9ccfde6ee7df654be64a79c79ec4178c10d8fd18 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 25 Apr 2018 13:03:03 -0400 Subject: [PATCH] cmd/go: add support for 'go run pkg' or 'go run .' To date, go run has required a list of .go files. This CL allows in place of that list a single import path or a directory name or a pattern matching a single patckage. This allows 'go run pkg' or 'go run dir', most importantly 'go run .'. The discussion in #22726 gives more motivation. The basic idea is that you can already run 'go test .' but if you're developing a command it's pretty awkward to iterate at the same speed. This lets you do that, by using 'go run . [args]'. Fixes #22726. Change-Id: Ibfc8172a4f752588ad96df0a6b0928e9b61fa27f Reviewed-on: https://go-review.googlesource.com/109341 Run-TryBot: Russ Cox TryBot-Result: Gobot Gobot Reviewed-by: Bryan C. Mills --- src/cmd/go/alldocs.go | 9 ++++-- src/cmd/go/go_test.go | 12 +++++++ src/cmd/go/internal/run/run.go | 44 ++++++++++++++++++-------- src/cmd/go/testdata/src/hello/hello.go | 5 +++ 4 files changed, 54 insertions(+), 16 deletions(-) create mode 100644 src/cmd/go/testdata/src/hello/hello.go diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index fa0d57f16b..8cccbf4de0 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -718,10 +718,12 @@ // // Usage: // -// go run [build flags] [-exec xprog] gofiles... [arguments...] +// go run [build flags] [-exec xprog] package [arguments...] // -// Run compiles and runs the main package comprising the named Go source files. -// A Go source file is defined to be a file ending in a literal ".go" suffix. +// Run compiles and runs the named main Go package. +// Typically the package is specified as a list of .go source files, +// but it may also be an import path, file system path, or pattern +// matching a single known package, as in 'go run .' or 'go run my/cmd'. // // By default, 'go run' runs the compiled binary directly: 'a.out arguments...'. // If the -exec flag is given, 'go run' invokes the binary using xprog: @@ -736,6 +738,7 @@ // The exit status of Run is not the exit status of the compiled binary. // // For more about build flags, see 'go help build'. +// For more about specifying packages, see 'go help packages'. // // See also: go build. // diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index 06948525ad..33fbc2cc48 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -1273,6 +1273,18 @@ func TestRunInternal(t *testing.T) { tg.grepStderr(`testdata(\/|\\)src(\/|\\)run(\/|\\)bad\.go\:3\:8\: use of internal package not allowed`, "unexpected error for run/bad.go") } +func TestRunPkg(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + dir := filepath.Join(tg.pwd(), "testdata") + tg.setenv("GOPATH", dir) + tg.run("run", "hello") + tg.grepStderr("hello, world", "did not find hello, world") + tg.cd(filepath.Join(dir, "src/hello")) + tg.run("run", ".") + tg.grepStderr("hello, world", "did not find hello, world") +} + func testMove(t *testing.T, vcs, url, base, config string) { testenv.MustHaveExternalNetwork(t) diff --git a/src/cmd/go/internal/run/run.go b/src/cmd/go/internal/run/run.go index 6ff2090095..8460d1fac6 100644 --- a/src/cmd/go/internal/run/run.go +++ b/src/cmd/go/internal/run/run.go @@ -18,11 +18,13 @@ import ( ) var CmdRun = &base.Command{ - UsageLine: "run [build flags] [-exec xprog] gofiles... [arguments...]", + UsageLine: "run [build flags] [-exec xprog] package [arguments...]", Short: "compile and run Go program", Long: ` -Run compiles and runs the main package comprising the named Go source files. -A Go source file is defined to be a file ending in a literal ".go" suffix. +Run compiles and runs the named main Go package. +Typically the package is specified as a list of .go source files, +but it may also be an import path, file system path, or pattern +matching a single known package, as in 'go run .' or 'go run my/cmd'. By default, 'go run' runs the compiled binary directly: 'a.out arguments...'. If the -exec flag is given, 'go run' invokes the binary using xprog: @@ -37,6 +39,7 @@ available. The exit status of Run is not the exit status of the compiled binary. For more about build flags, see 'go help build'. +For more about specifying packages, see 'go help packages'. See also: go build. `, @@ -62,18 +65,33 @@ func runRun(cmd *base.Command, args []string) { for i < len(args) && strings.HasSuffix(args[i], ".go") { i++ } - files, cmdArgs := args[:i], args[i:] - if len(files) == 0 { + var p *load.Package + if i > 0 { + files := args[:i] + for _, file := range files { + if strings.HasSuffix(file, "_test.go") { + // GoFilesPackage is going to assign this to TestGoFiles. + // Reject since it won't be part of the build. + base.Fatalf("go run: cannot run *_test.go files (%s)", file) + } + } + p = load.GoFilesPackage(files) + } else if len(args) > 0 && !strings.HasPrefix(args[0], "-") { + pkgs := load.PackagesAndErrors(args[:1]) + if len(pkgs) > 1 { + var names []string + for _, p := range pkgs { + names = append(names, p.ImportPath) + } + base.Fatalf("go run: pattern %s matches multiple packages:\n\t%s", args[0], strings.Join(names, "\n\t")) + } + p = pkgs[0] + i++ + } else { base.Fatalf("go run: no go files listed") } - for _, file := range files { - if strings.HasSuffix(file, "_test.go") { - // GoFilesPackage is going to assign this to TestGoFiles. - // Reject since it won't be part of the build. - base.Fatalf("go run: cannot run *_test.go files (%s)", file) - } - } - p := load.GoFilesPackage(files) + cmdArgs := args[i:] + if p.Error != nil { base.Fatalf("%s", p.Error) } diff --git a/src/cmd/go/testdata/src/hello/hello.go b/src/cmd/go/testdata/src/hello/hello.go new file mode 100644 index 0000000000..73d83e646f --- /dev/null +++ b/src/cmd/go/testdata/src/hello/hello.go @@ -0,0 +1,5 @@ +package main + +func main() { + println("hello, world") +}