Golang Before||After Modules
Although I am familiar (and a fan of) the old ${GOPATH}
way, to remain current and because of the many benefits, I’ve begun to use Go Modules.
Like others, I found the switch to be confusing. So…
Before Modules
WORKDIR=[[PATH-TO-YOUR-WORKING-DIRECTORY]]mkdir -p ${WORKDIR}/go
export GOPATH=${WORKDIR}/go
export PATH=${GOPATH}/bin:${PATH}mkdir -p {${WORKDIR}/go/src/foo, ${WORKDIR}/go/src/foo/bar}
Then create ${WORKDIR}/go/src/foo/bar/library.go
:
package barfunc Something() (string) {
return "Hello Freddie"
}
Then create ${WORKDIR}/go/src/foo/main.go
:
package mainimport (
"fmt"
"foo/bar"
)func main() {
fmt.Printf("%s", bar.Something())
}
You’ll have a structure like this:
.
└── go
└── src
└── foo
├── bar
│ └── library.go
└── main.go
Then you may run this in any of the following ways:
GO111MODULE=off go run ${WORKDIR}/go/src/foo/main.go
Hello Freddie!cd ${WORKDIR}/go/src/foo
GO111MODULE=off go run main.go
Hello Freddie!GO111MODULE=off go run foo
Hello Freddie!
NB With the above, you may omit
GO111MODULE=off
because this is implicit when sources are within${GOPATH}
. I’ve included it to be explicit with intent.
After Modules
Assuming you’ve done the above!
You don’t need to do this step but this is the new best practice. We’re moving our sources outside of ${GOPATH}
. ${GOPATH}
is still used to store our versioned packages.
NB In this trivial example, we're not using external packages so
${GOPATH}
remains empty. See the end for an example.
mv ${WORKDIR}/go/src/foo ${WORKDIR}
rm ${WORKDIR}/go/src
You should have a structure like this:
.
├── foo
│ ├── bar
│ │ └── library.go
│ └── main.go
└── go
NB Our sources rooted on
foo
are now outside of${GOPATH}
cd ${WORKDIR}/fooGO111MODULE=on go mod init foo
more go.mod
module foogo 1.12GO111MODULE=on go run foo
Hello Freddie!GO111MODULE=on go run main.go
Hello Freddie!
For Completeness
To show the difference with external packages. Not using Modules, go get
pulls the latest version of the package into ${GOPATH}/src
:
GO111MODULE=off go get github.com/golang/glog.
├── foo
│ ├── bar
│ │ └── library.go
│ ├── go.mod
│ ├── go.sum
│ └── main.go
└── go
└── src
└── github.com
└── golang
└── glog
├── glog_file.go
├── glog.go
├── glog_test.go
├── LICENSE
└── README
Versus using Modules, pulls a specific version (or versions) of the package into ${GOPATH}/pkg
:
GO111MODULE=on go get github.com/golang/glog.
├── foo
│ ├── bar
│ │ └── library.go
│ ├── go.mod
│ ├── go.sum
│ └── main.go
└── go
└── pkg
├── linux_amd64
│ └── github.com
│ └── golang
│ └── glog.a
└── mod
├── cache
│ ├── download
│ │ └── github.com
│ │ └── golang
│ │ └── glog
└── github.com
└── golang
└── glog@v0.0.0-20160126235308-23def4e6c14b
├── glog_file.go
├── glog.go
├── glog_test.go
├── LICENSE
└── README
Go Modules Mirror
You include PROXY=https://proxy.golang.org
in all the above commands to take advantage of the Golang team’s (Google Trillian based) Mirror:
GO111MODULE=on \
GOPROXY=https://proxy.golang.org \
go get github.com/golang/glog
Trick
Thanks to this link for this useful trick finding packages that will be installed somewhere in go/pkg/mod
when using Modules:
GO111MODULE=on \
go list -f '{{ .Dir }}' github.com/golang/gloggo: finding github.com/golang/glog latest
/path/to/go/pkg/mod/github.com/golang/glog@v0.0.0-20160126235308-23def4e6c14b
Visual Studio Code
A few folks asked for my configuration settings for Visual Studio Code w/ Go Modules. Here’s what I have:
{
"go.autocompleteUnimportedPackages": true,
"go.useLanguageServer": true,
"[go]": {
"editor.snippetSuggestions": "none",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
},
"gopls": {
"usePlaceholders": true // add parameter placeholders when completing a function
},
"files.eol": "\n", // formatting only supports LF line endings
"go.toolsEnvVars": {
"GO111MODULE": "on",
"GOPROXY": "https://proxy.golang.org"
},
}
A combination of help from this page and this reply from a Google Engineer. I try to remain current but the Golang team’s gopls had passed me by entirely :(
https://github.com/golang/go/wiki/gopls
Conclusion
Modules are the future of package management in Golang. Having now begun the plunge, I mostly see benefits. Even with my simple Golang repos, there’s utility in being able to define specific versions of packages that are imported.
Initially I didn’t understand (and maybe still don’t understand) the uility in not including my sources under ${GOPATH}
but, one advantage is that it becomes more evident which sources are my sources and which are others packages. When everything’s smooshed under ${GOPATH}
this was confusing.
In part, the use of semantic versioning (aka SemVer) also means that, once you’ve go get useful-package@v1.0.0
you should (!) never need to re-get
it. I’ve previously always kept my ${GOPATH}
directories distinct by project to keep code isolated. But, by pulling my sources out of ${GOPATH}
and because I only need one copy of useful-package@v1.0.0
, and (!) because I know this package is immutable, I will now begin using a single, central ${GOPATH}
for all my projects.
That’s all!