I always use Debian stable on my work laptop. I also tend to use the provided version for most programming languages. Sometimes I need multiple versions for work or research related reasons. This isn't a problem with languages such as Haskell or Rust, but it tends to be a pain with others, Golang being the cause of these notes.

Making the default version more «default».

At the time of this writing, Debian 10 («buster») provides Golang 1.11. I need it to build Debian packages for a few applications, so it can be installed via APT

# apt install golang-1.11-go golang-1.11-doc golang-1.11-src

The first thing that will surprise you is that there's no «default» version of /usr/bin/go to use right away. Debian Golang packages install their stuff under /usr/lib/go-<version>/ and you're left to your own devices to adjust GOROOT correctly for your environment, as the Golang folks want you to. Later Golang revisions allow you to have multiple binaries that hold the version as part of their name -- I cannot sanction such buffoonery.

I want to have /usr/bin/go pointing to my «default» version, thus keeping things civil. Moreover, I want /usr/bin/gofmt pointing to the matching one, to avoid confusions and have my vim setup work Just Right®. Since it's possible to have multiple versions installed, I want to be able to change from one version to the other.

Here's where Debian's update-alternatives(1) shines.

# update-alternatives --install /usr/bin/go go /usr/lib/go-1.11/bin/go 50 \
    --slave /usr/bin/gofmt gofmt /usr/lib/go-1.11/bin/gofmt
# ls -l /usr/bin/go
lrwxrwxrwx 1 root root 20 Sep 10 17:04 /usr/bin/go -> /etc/alternatives/go
# ls -l /etc/alternatives/go
lrwxrwxrwx 1 root root 23 Sep 10 17:05 /etc/alternatives/go -> /usr/lib/go-1.11/bin/go
# go version
go version go1.11.6 linux/amd64

The above command creates a new alternative group named go with master location /usr/bin/go and priority 50. The master location is set as a symlink to /usr/lib/go-1.11/bin/go. Finally, it sets a dependent alternative /usr/bin/gofmt to the matching /usr/lib/go-1.11/bin/gofmt. Alternatives are handled by having symlinks into /etc/alternatives, holding symlinks to the actual selection.

Now there's a default /usr/bin/go I can use, with matching /usr/bin/gofmt.

Installing additional Golang versions

Golang has one of the worst dependency management systems there are, and its type system's shortcomings have forced third-party libraries to jump through hoops to accommodate. One particularly nasty issue is handling the SQL NULL representation over a type system lacking polymorphism. Long story short, to make the latest SQL library work, one needs to have Golang 1.14.

Fortunately, Debian Backports provides Golang 1.14. After adding the the relevant APT source

# apt install golang-1.14-go golang-1.14-doc golang-1.14-src

Now, this is the version I want to have as default, therefore

# update-alternatives --install /usr/bin/go go /usr/lib/go-1.14/bin/go 100 \
    --slave /usr/bin/gofmt gofmt /usr/lib/go-1.14/bin/gofmt
# ls -l /usr/bin/go
lrwxrwxrwx 1 root root 20 Sep 10 17:08 /usr/bin/go -> /etc/alternatives/go
# ls -l /etc/alternatives/go
lrwxrwxrwx 1 root root 23 Sep 10 17:08 /etc/alternatives/go -> /usr/lib/go-1.14/bin/go
# go version
go version go1.14 linux/amd64

The above command extends the existing go alternative group, with the new master location at /usr/lib/go-1.14/bin/go with priority 100. It sets the matching dependent alternative /usr/bin/gofmt to /usr/lib/go-1.14/bin/gofmt.

Now, we can check that there are indeed two alternatives

# update-alternatives --display go
go - auto mode
  link best version is /usr/lib/go-1.14/bin/go
  link currently points to /usr/lib/go-1.14/bin/go
  link go is /usr/bin/go
  slave gofmt is /usr/bin/gofmt
/usr/lib/go-1.11/bin/go - priority 50
  slave gofmt: /usr/lib/go-1.11/bin/gofmt
/usr/lib/go-1.14/bin/go - priority 100
  slave gofmt: /usr/lib/go-1.14/bin/gofmt

The highest priority set becomes the default for the system, in this case Golang 1.14 as I need.

Switching versions

To change the system-wide default, working as root

# update-alternatives --config go
There are 2 choices for the alternative go (providing /usr/bin/go).

  Selection    Path                     Priority   Status
* 0            /usr/lib/go-1.14/bin/go   100       auto mode
  1            /usr/lib/go-1.11/bin/go   50        manual mode
  2            /usr/lib/go-1.14/bin/go   100       manual mode

Press <enter> to keep the current choice[*], or type selection number: 1
update-alternatives: using /usr/lib/go-1.11/bin/go to provide /usr/bin/go (go) in manual mode
# go version
go version go1.11.6 linux/amd64

You can also use the --set command line option to achieve the same results.

What's missing...

At the time of this writing, Debian's Golang packages do not provide automatic alternative management on installation or removal. If you need additional versions, or remove ones you don't need, remember to run update-alternatives to add or remove them.

Note that update-alternatives sets the global default alternative only. If a user needs to work with a different version, they will need to set up GOROOT in their environment accordingly. At least they can get the right value from update-alternatives --display go...