Now that Go 1.5 is out, lots of gophers are excited to try the much improved cross compilation support. For some background on the changes to the cross compilation story you can read my previous post, or Rakyll’s excellent follow up piece.
I’ll assume that you are using the binary version of Go 1.5, as distributed from the Go website. If you are using Go 1.5 from your operating system’s distribution, or homebrew, the process will be the same, however paths may differ slightly.
How to cross compile
To cross compile a Go program using Go 1.5 the process is as follows:
- set
GOOS
andGOARCH
to be the values for the target operating system and architecture. - run
go build -v YOURPACKAGE
If the compile is successful you’ll have a binary called YOURPACKAGE
(possibly with a .exe
extension if you’re targeting Windows) in your current working directory.
-o
may be used to alter the name and destination of your binary, but remember that go build takes a value that is relative to your $GOPATH/src
, not your working directory, so changing directories then executing the go build
command is also an option.
Example
I prefer to combine the two steps outlined above into one, like this:
% env GOOS=linux GOARCH=arm go build -v github.com/constabulary/gb/cmd/gb runtime sync/atomic ... github.com/constabulary/gb github.com/constabulary/gb/cmd github.com/constabulary/gb/cmd/gb % file ./gb ./gb: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, not stripped
and that’s all there is to it.
Using go build vs go install
When cross compiling, you should use go build
, not go install
. This is the one of the few cases where go build
is preferable to go install
.
The reason for this is go install
always caches compiled packages, .a
files, into the pkg/
directory that matches the root of the source code.
For example, if you are building $GOPATH/src/github.com/lib/pq
, then the compiled package will be installed into $GOPATH/pkg/$GOOS_$GOARCH/github.com/lib/pq.a
.
This logic also holds true for the standard library, which lives in /usr/local/go/src
, so will be compiled to /usr/local/go/pkg/$GOOS_$GOARCH
. This is a problem, because when cross compiling the go
tool needs to rebuild the standard library for your target, but the binary distribution expects that /usr/local/go
is not writeable.
Using go build
rather that go install
is the solution here, because go build
builds, then throws away most of the result (rather than caching it for later), leaving you with the final binary in the current directory, which is most likely writeable by you.
Ugh, this is really slow!
In the procedure described above, cross compilation always rebuilds that standard library for the target every time. Depending on your workflow this is either not an issue, or a really big issue. If it’s the latter then I recommend you remove the binary distribution and build from source into a path that is writeable by you, then you’ll have the full gamut of go
commands available to you.
Cross compilation support in gb is being actively developed and will not have this restriction.
What about GOARM?
The go
tool chooses a reasonable value for GOARM
by default. You should not change this unless you have a good reason.
But but, what about GOARM=7?
Sure, knock yourself out, but that means your program won’t run on all models of the Raspberry Pi. The difference between GOARM=6
(the default) and GOARM=7
is enabling a few more floating point registers, and a few more operations that allow floating point double values to be passed to and from the ARMv7 (VPFv3) floating point co processor more efficiently. IMO, with the current Go 1.5 arm compiler, it’s not worth the bother.