Currently, go run
requires Go files as arguments. It's useful for quickly running Go programs, but has been discouraged by many people because dependent packages are not installed. With the changes to build caching in Go 1.10, I propose we simplify usage of go run
to encourage more use of it.
A rundown of use cases and how this change would improve the user experience:
Program with one file (e.g., one-off scripts, go generate scripts)
go run script.go
Program with more than one file, with no tests
go run *.go
go run
Program with more than one file, where test files are also in the same directory.
go run !(*_test).go
or go run main.go a.go b.go
go run
Program that accepts arguments
go run main.go arg1 arg2
go run -- arg1 arg2
--
separator when filenames are omitted.Program that accepts Go filenames as arguments
go run main.go other.go -- foo.go
go run -- foo.go
Program with build tags
go build -tags -o a && ./a && rm ./a
(go run ignores build tags on the files passed in)go run -tags foo
Also, perhaps a package path should be allowed as an argument, which would better mirror go build:
go run golang.org/x/tools/cmd/godoc -http=:6060
FWIW, I'm one of those people that discourages the use of go run
and my reasoning has little to do with caching dependent packages. From what I've seen, when go run
is used for multiple .go files or for any of the more complex use cases like the ones you've listed here, it's generally by new Gophers that haven't really internalized how packages work yet. I don't see how an easier-to-use go run
is better than go build
/go install
for these sorts of things.
- Before:
go run script.go -- foo.go
- After:
go run -- foo.go
How does go run
know to run script.go
in this case, if there are other .go files?
I don't see how an easier-to-use go run is better than go build/go install for these sorts of things.
go run is already in many ways better than go build/go install for one-off/ad-hoc invocations.
go build is decent, but requires you to clean up the binary after you're done. go install is even worse: the user must understand that the binary ends up in $GOPATH/bin.
How does go run know to run script.go in this case, if there are other .go files?
Use case 5 is when there are no other Go files. If there are other Go files, that's use case 1. Updated script.go to main.go to clarify.
Here's a concrete example that might help you understand my viewpoint:
https://github.com/GoogleCloudPlatform/golang-samples/tree/master/vision/detect
This program is split across two files, and there's also a test file. There's two choices to help people run this program (note that they may not be experienced Go developers, who fully understand how packages, go build, go install all work).
There are a few choices to tell people how to run the program:
As you can see, I chose the third option. It'd be much nicer for this to simply be go run -- [args]
, I think.
@broady
go install && $GOPATH/bin/detect [args] && rm $GOPATH/bin/detect
The problem here is when GOPATH
is a path list; hence you can't rely on $GOPATH/bin/xyz
.
I started a thread on golang-nuts with what I think is similar motivation to this issue:
https://groups.google.com/d/msg/golang-nuts/4C_Rd_JJZVU/UdqRePeMBwAJ
@cznic raised some concerns about overloading the run
subcommand which I can understand, hence I sort of morphed the suggestion into a new subcommand exec
. But then there wasn't much other interest.
As you point out, with the build caching changes in Go 1.10, I would guess that go exec
would be even easier to implement/support?
Related:
I'm pretty sure there's old discussion about whether #! should be ignored in code that played into these too but I can't find the ones that related to go run. (I don't think we should ignore #! lines.)
Potential design based on discussion with proposal review:
go run [go flags] [single-package-or-*.go-list] [subprocess flags]
If you specify a package (even "."), then it's clear how to split the two possible flag lists.
If you don't specify a package, we should probably not repeat 'go test's mistake and try to split the flags magically. So then we have to decide whether
go run -flag
means -flag goes to the go command or to the subprocess (the decision should not depend on exactly which flag it is). For consistency with go build etc we should probably make the default be go flags. That seems less surprising. To specify subprocess flags, fill in [single-package-or-*.go-list] with "." meaning the package in the current directory.
Today:
go run x.go -x # go build -o a.out x.go && ./a.out -x
go run x.go hello # go build -o a.out x.go && ./a.out hello
go run x.go -- -x # go build -o a.out x.go && ./a.out -- -x
Maybe tomorrow:
go run # go build -o a.out . && ./a.out
go run -v # go build -v -o a.out . && ./a.out
go run . -v # go build -o a.out . && ./a.out -v
go run -v . -x # go build -v -o a.out . && ./a.out -x
go run -v -- -x # go build -v -o a.out . && ./a.out -- -x
go run cmd/gofmt -help # go build -o a.out cmd/gofmt && ./a.out -help
This would change @broady's 4 and 5 above to use "." instead of "--", but that's much easier to explain ("." is a valid argument to build, test, etc).
Actually the default really _must_ be that without a package list the flags are all for the go command. If they were all for the subprocess then there would be ambiguities due to not knowing which of the subprocess flags took arguments. For example if the subprocess took a -x=string flag, then
go run -x=string
would look like not having a package, but you'd think an equivalent command would be:
go run -x string
which would incorrectly look like a go flag followed by a package name.
Defining that the first flags are always go flags avoids this ambiguity.
Based on discussion with proposal-review again this week, accepting proposal as detailed in https://github.com/golang/go/issues/22726#issuecomment-345841019. I will take a look at this for Go 1.11.
Back from vacation. I had done a little more thinking before you posted the comments above, and came to a similar (but more strict) conclusion: that "go run" should require a package name (or list of files) to remove all ambiguity.
So:
go run # error, must provide package name or list of files
go run . # go build -o a.out . && ./a.out
go run -v . # go build -v -o a.out . && ./a.out
go run . -v # go build -o a.out . && ./a.out -v
go run -v . -x # go build -v -o a.out . && ./a.out -x
go run -v . -- -x # go build -v -o a.out . && ./a.out -- -x
go run cmd/gofmt -help # go build -o a.out cmd/gofmt && ./a.out -help
I'm not certain that the gain in precision is worth requiring the argument, and think that the rules you described balance ease of use with precision well enough.
Imho if we want to simplify this for the newbies then go run
should really be removed. The new way of running things go run -- -x
vs go run -x
will be even more confusing for people, especially for those whom never had to deal with --
or coming from other languages. I understand that this is aimed at the CLI users, but imho it doesn't simplify the problem for new users, it just moves it around.
@dlsniper please read the most recent comments. The proposal for the --
delimiter was rejected.
I like the idea for allowing go run of a directory, broady's proposal for how to handle flags makes sense to me, except the -- is awkward.
It's unclear to me that go run cmd/gofmt
should work. Maybe the argument should be limited to be a list of .go files or a directory (. being the common one, but any absolute or relative path is fine), but at least for the start exclude import paths as arguments.
Hi @broady - do you hope to get this in for Go 1.11?
I'm 馃挴 馃憤the current proposal.
With the changes to build caching in Go 1.10, I propose we simplify usage of
go run
to encourage more use of it.
Just to check one point: Go 1.10's caching changes do not appear to extend to go run
; so presumably the work relating to this issue would also include extending the caching to the run command?
A very nice side effect of doing so would be that we have fast, reproducible "builds" via go run
(we already have the latter to be fair, but with this proposal and caching changes we get the "fast" bit and the ability to specify a package name to go run
). My intention is to use go run pkg_name
in lieu of go install
etc in //go:generate
directives. One of the critical bits of this pipeline is that I know when code generator itself has changed (much like the build cache changes it forms the input to a hash that is checked vs a code generation cache). With reproducible "builds" I can take the hash of os.Args[0]
in the generator itself as my "version".
In light of @rsc's recent points, to make the perhaps obvious point: having vgo run
implement this proposal would seem to be a great fit.
@myitcv I'm keen to implement this, but not comfortable committing to a timeline. (that is, if someone else wants to do it, please do!)
@rsc is there any conflict here with vgo? The main issue I see is that the implementation should probably be pushed upstream to x/vgo after implementing in master.
@broady 馃憤
The main issue I see is that the implementation should probably be pushed upstream to x/vgo after implementing in master.
Sounds good.
I suspect @rsc will agree with your suggestion given comments in https://go-review.googlesource.com/c/vgo/+/105855
Change https://golang.org/cl/109341 mentions this issue: cmd/go: add support for 'go run pkg' or 'go run .'
What is the problem to do some auto/run .go file by Go in 2xClick on .go file and set in windows registry the related icon to .go files?
Most helpful comment
Related:
I'm pretty sure there's old discussion about whether #! should be ignored in code that played into these too but I can't find the ones that related to go run. (I don't think we should ignore #! lines.)
Potential design based on discussion with proposal review:
If you specify a package (even "."), then it's clear how to split the two possible flag lists.
If you don't specify a package, we should probably not repeat 'go test's mistake and try to split the flags magically. So then we have to decide whether
means -flag goes to the go command or to the subprocess (the decision should not depend on exactly which flag it is). For consistency with go build etc we should probably make the default be go flags. That seems less surprising. To specify subprocess flags, fill in [single-package-or-*.go-list] with "." meaning the package in the current directory.
Today:
Maybe tomorrow:
This would change @broady's 4 and 5 above to use "." instead of "--", but that's much easier to explain ("." is a valid argument to build, test, etc).