A safety note about using (or having) go.mod
inside $GOPATH
in Go 1.13
One of the things in the Go 1.13 release notes is a little note
about improved support for go.mod
. This is worth quoting in
more or less full:
The
GO111MODULE
environment variable continues to default toauto
, but theauto
setting now activates the module-aware mode of the go command whenever the current working directory contains, or is below a directory containing, ago.mod
file — even if the current directory is withinGOPATH/src
.
The important safety note is that this potentially creates a confusing situation, and also it may be easy for other people to misunderstand what this actually says in the same way that I did.
Suppose that there is a Go program that is part of a module,
example.org/fred/cmd/bar (with the module being example.org/fred).
If you do 'go get example.org/fred/cmd/bar
', you're fetching and
building things in non-module mode, and you will wind up with a
$GOPATH/src/example.org/fred
VCS clone, which will have a go.mod
file at its root, ie $GOPATH/src/example.org/fred/go.mod
. Despite
the fact that there is a go.mod
file right there on disk, re-running
'go get example.org/fred/cmd/bar
' while you're in (say) your home
directory will not do a module-aware build. This is because, as the
note says, module-aware builds only happen if your current directory
or its parents contain a go.mod
file, not just if there happens
to be a go.mod
file in the package (and module) tree being built.
So the only way to do a proper module aware build is to actually
be in the command's subdirectory:
cd $GOPATH/src/example.org/fred/cmd/bar go get
(You can get very odd results if you cd to $GOPATH/src/example.org
and then attempt to 'go get example.org/fred/cmd/bar
'. The result
is sort of module-aware but weird.)
This makes it rather more awkward to build or rebuild Go programs
through scripts, especially if they involve various programs that introspect your existing
Go binaries. It's also easy to slip up and de-modularize a Go binary;
one absent-minded 'go get example.org/...
' will do it.
In a way, Go modules don't exist on disk unless you're in their
directory tree. If that tree is inside $GOPATH
and you're not in
it, you have a plain Go package, not a module.
(If the directory tree is outside $GOPATH
, well, you're not doing
much with it without cd
'ing into it, at which point you have a
module.)
The easiest way to see whether a binary was built module-aware or
not is 'goversion -m PROGRAM
'. If
the program was built module-aware, you will get a list of all of
the modules involved. If it wasn't, you'll just get a report of
what Go version it was built with. Also, it turns out that you can
build a program with modules without it having a go.mod
:
GO111MODULE=on go get rsc.io/goversion@latest
The repository has tags but no go.mod
. This also works on
repositories with no tags at all. If the program uses outside
packages, they too can be non-modular, and 'goversion -m PROGRAM
'
will (still) produce a report of what tags, dates, and hashes they
were at.
Update: in Go 1.13, 'go version -m PROGRAM
' also reports the
module build information, with module hashes included as well.
This does mean that in theory you could switch over to building all
third party Go programs you use this way. If the program hasn't
converted to modules you get more or less the same results as today,
and if the program has converted, you get their hopefully stable
go.mod
settings. You'd lose having a local copy of everything in
your $GOPATH
, though, which opens up some issues.
|
|