Description
What version of Go are you using (go version
)?
go version go1.11 linux/amd64
What did you do?
Tied to install and run a tool dependency in a number of my modules (more detail below)
What did you expect to see?
A nice easy way to (install and) run a tool dependency.
What did you see instead?
I needed to set GOBIN
and update PATH
for each module.
Further detail (on What did you do?)
This issue builds on top of #25922 so if that changes shape in any significant way it may void what follows.
Per #27643 (comment), I think we need to make the workflow around using a module's tool dependencies easier. Let me try to explain by covering my "workflow".
The tools I use on a day-to-day basis fall into two categories:
- Tools I want to be controlled by a project's
go.mod
- Tools that I need globally
Dealing with these in reverse order.
Category 2: I think it's clear that with Go 1.11, there is a "gap" here and that this is covered by #24250. Per the detail in that discussion, there are open questions on how to handle multiple versions, where the installed binaries should be put etc, but it all falls under that issue.
Category 1: by far the largest category of tools for me, made up largely of code generators that I use with go generate
and the like. I absolutely want these to be version controlled. And I don't want to be using (via my PATH
) a "global" install of such a tool, even if the version just happens to match at that point in time. But both go get
and go install
currently (i.e. Go 1.11) have a target of $GOPATH/bin
(ignoring multi-element `GOPATH values for now).
Hence the workflow I have effectively adopted, building on #25922, is to create a module-local install target:
# create a new module
cd $(mktemp -d)
mkdir hello
cd hello
go mod init example.com/hello
# set GOBIN as a module-local install target
export GOBIN=$PWD/.bin
# update my PATH accordingly (I actually use https://github.com/cxreg/smartcd for this)
export PATH=$GOBIN:$PATH
# add a tool dependency (by definition, category 1 tool) following "best practice" laid out in
# https://github.com/golang/go/issues/25922#issuecomment-412992431
cat <<EOD > tools.go
// +build tools
package tools
import (
_ "golang.org/x/tools/cmd/stringer"
)
EOD
# install the tool
go install golang.org/x/tools/cmd/stringer
# verify we are using the module-local binary
which stringer
# which gives something like:
# /tmp/tmp.Hh0BNOF6k2/hello/.bin/stringer
As covered in #27643, one of the disconnects in Go 1.11 is that a go get
or go install
in a module context modifies the "local" go.mod
but installs "globally". This is, as @bcmills put it, "weird". But is to my mind a gap in Go 1.11, just as there not being a "global" tool install is a gap (i.e. #24250).
Potential solutions
Just listing these here as a starting point:
go run
is a potential alternative to the "local" install here (and a very attractive one to my mind), but we need to find a way to address cmd/go: go run pkg is significantly slower than running built binary #25416.- There could be some convention that a
.bin/
directory, alongside ago.mod
, is the target forgo get
andgo install
(ofmain
packages) for "local" installs? But this wouldn't obviate the need for everyone to update theirPATH
and indeed.gitignore
the.bin
directory for every module they work on. - ...