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
GO111MODULEenvironment variable continues to default toauto, but theautosetting now activates the module-aware mode of the go command whenever the current working directory contains, or is below a directory containing, ago.modfile — 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 .