I’ve managed to limp through the dependency management ordeal1 in Go with only minor damage so far. Though my experience isn’t extensive, I feel that Go has so far done a good job on being backwards compatible. Upgrading from, say 1.3 to 1.4, typically just works, enough though I’ve had mismatched versions between my local environment and what Godeps installs on Heroku, which went unnoticed until just recently.
Until I got bit.
More like a scratch, but still…
It came time where I needed to practice what I preach and achieve (better) dev/prod parity. I briefly looked into tools like gvm but wanted to ask around before I installed yet another thing to magically solve my problems. After discussing with colleagues, I’ve adopted the strategy outlined below with minimal pain/adjusting. If you’re willing to live dangerously and occasionally blast away your
folder2, supporting multiple versions in Go can be pretty easy.1
$GOPATH/pkg
If you haven’t read Getting Started and How to Write Go Code, then why are you here? Go do that. In fact, I wouldn’t necessarily come right back here either, maybe bookmark for months later when you feel comfortable enough to muck up your environment with reckless abandon3. Assuming you followed those guides, you should have Go currently installed in
; We’re going to delete that.1
/usr/local/go
1
$ rm -rf /usr/local/go
Exciting, right?
In order to get started, we need to review important environment variables that you should already be familiar with.
: specifies where your workspace is located, e.g. 1
$GOPATH
, 1
$GOPATH/bin
and 1
$GOPATH/pkg
)1
$GOPATH/src
: where Go is actually located. Unless specified, Go assumes it lives in 1
$GOROOT
(the thing we just deleted). Fortunately you can install Go anywhere you like so long as set the 1
/usr/local/go
environment variable and add 1
$GOROOT
to your 1
$GOROOT/bin
1
$PATH
You need to setup these environment variables yourself, typically in a
or a 1
~/.bashrc
file (or whatever shell you’re using),
and then make sure the relative 1
~/.zshrc
folders are added to your 1
bin
:1
$PATH
1
2
3
4
# in ~/.zshrc
export GOPATH=$HOME/Go
export GOROOT=/usr/local/go
export PATH=$PATH:$GOPATH/bin:$GOROOT/bin
Your environment should be complete now. Nothing should work though.
The official binary distributions can be found on golang’s site here (this example uses the latest for OS X 10.8+). We’ll create a folder in
to store our Go versions, pull some down and extract them to their own folders, and then create a symlink to the one we want to use. For this example, we’ll use the latest go 1.3.3 and go 1.4.1:1
/usr/local
1
2
3
4
5
$ mkdir -p /usr/local/go-versions/go1.3
$ curl https://storage.googleapis.com/golang/go1.3.3.darwin-386-osx10.8.tar.gz | tar -xz -C /usr/local/go-versions/go1.3 --strip-components=1
$ mkdir -p /usr/local/go-versions/go1.4
$ curl https://storage.googleapis.com/golang/go1.4.1.darwin-386-osx10.8.tar.gz | tar -xz -C /usr/local/go-versions/go1.4 --strip-components=1
Now
should have your separate versions in it and look like this:1
/usr/local/go-versions
1
2
$ ls /usr/local/go-versions
go1.3 go1.4
To support multiple versions we’ll create a symbolic link where your
points to. In order to toggle a version, you simply update the symlink at 1
$GOROOT
(your 1
/usr/local/go
) to point to the desired version. The initial creation of the link and toggling versions can be done with the same command:1
$GOROOT
1
$ ln -sfn /usr/local/go-versions/go1.4 /usr/local/go
This command will create a symbolic link at
, pointing to the 1
/usr/local/go
directory, replacing the symlink if it already exists. Now you can toggle back and forth:1
/usr/local/go-versions/go1.4
1
2
3
4
5
6
7
$ ln -sfn /usr/local/go-versions/go1.4 /usr/local/go
$ go version
go version go1.4.1 darwin/386
$ ln -sfn /usr/local/go-versions/go1.3 /usr/local/go
$ go version
go version go1.3.3 darwin/386
That’s it! Mostly.
Go installs the packaged objects into your
folder, and compiled binaries in 1
$GOPATH/pkg
. Not everything you install will place a binary in 1
$GOPATH/bin
, but it will have something in 1
bin
that mirrors its source directory. As you probably guessed, these things are compiled with the current version of Go. As a result, switching versions could result in undefined behavior.1
pkg
The binaries in
however are statically compiled, meaning they have everything they need to run included in them, and do not depend on anything external at runtime. They should be safe to leave alone.1
$GOPATH/bin
When you invoke the
tool, it will search the 1
go
folder to find existing compiled package objects to avoid recompiling them unnecessarily. Because the objects in 1
$GOPATH/pkg
are compiled against the version of Go at that the time of compilation, they may not work if you switch versions.1
$GOPATH/pkg
To get around any potential issues (or resolve them), you can safely destroy your
folder (not your 1
$GOPATH/pkg
folder):1
$GOROOT/pkg
1
$ rm -r $GOPATH/pkg
Future invocations of
will reinstall/compile packages as necessary. You will incur the additional overhead of recompiling these things (downloading from the net if needed), but at least you can be assured that they are compiled against the current version of Go that you’re using.1
go
Thanks to Ed Muller (@freeformz), Mark Turner (@amerine), Andrew Gwozdziewycz (@apgwoz), and Dane Harrigan (@daneharrigan) for educating me here.