Introduction
Cross compilation is one of Go’s headline features. I’ve written about it a few times, and others have taken this work and built better tooling around it.
This morning Russ Cox committed this change which resolved the last issue in making cross compilation simpler and more accessible for all Gophers. When Go 1.5 ships in August any Go programmer will be able to cross compile their program without having to go through a fussy set up phase.
Background
In the current version of Go, (if you’re from the future, read Go 1.4 and earlier) before you could cross compile a Go program, you needed to go through a set up phase to enhance your Go installation with the bits necessary to build for other platforms.
This worked ok if you had built Go from source, but if you were using one of the binary distributions or something from your operating system (brew, apt, etc), then the process would turn into a maze of twisty passages, all alike.
This was necessary because for successful cross compilation you would need
- compilers for the target platform, if they differed from your host platform, ie you’re on darwin/amd64 (6g) and you want to compile for linux/arm (5g).
- a standard library for the target platform, which included some files generated at the point your Go distribution was built.
With the plan to translate the Go compiler into Go coming to fruition in the 1.5 release the first issue is now resolved. That just left the small amount of customisation to the runtime done at installation time, which has been whittled away as part of the compiler transition (most of the customisation was generation of include files for the C parts of the runtime), and as of this morning, removed.
Try it out
If you can’t wait til August, you can try this out today by building the development version of Go.
Disclaimer: the development branch is changing rapidly due to the re-factoring of the compilers. If you’re using Go in production, the release version, Go 1.4.x, is always recommended.
Prerequisites
As mentioned above to build the development version of Go, you also need Go 1.4 installed to bootstrap. Once Go 1.5 comes out this step will be unnecessary.
These steps are a simple procedure to do this which I would recommend following
- Uninstall any version of Go you have on your system, including any
$PATH
variables. - Check out Go 1.4 and build it
% git clone https://go.googlesource.com/go $HOME/go1.4 % cd $HOME/go1.4/src % git checkout release-branch.go1.4 % ./make.bash
- Check out the development branch and build it
% git clone https://go.googlesource.com/go $HOME/go % cd $HOME/go/src % env GOROOT_BOOTSTRAP=$HOME/go1.4 ./all.bash
- Add
$HOME/go/bin
to your$PATH
Build something
Notice: if you are reading this from a future where Go 1.5 has been released, you can skip the previous step.
Now you have the development version of Go 1.5 installed, cross compiling this simple program is trivial
package main import "fmt" import "runtime" func main() { fmt.Printf("Hello %s/%s\n", runtime.GOOS, runtime.GOARCH) }
Now build for darwin/386
% env GOOS=darwin GOARCH=386 go build hello.go # scp to darwin host $ ./hello Hello darwin/386
Or build for linux/arm
% env GOOS=linux GOARCH=arm GOARM=7 go build hello.go # scp to linux host $ ./hello Hello linux/arm
That’s it!
Cross compilation can’t get any simpler than that.
Technical mumbo-jumbo
So what is happening under the hood when we compile a program for a different platform ? The -v
flag gives us a clue.
% go build -v hello.go command-line-arguments % env GOOS=linux GOARCH=arm go build -v hello.go runtime errors sync/atomic math unicode/utf8 sync io syscall time strconv reflect os fmt command-line-arguments
Comparing the two examples above, the first build is using the standard library that was built as part of the Go installation. That is to say, all the packages that fmt
and runtime
depend on are already built into $GOROOT/pkg/linux_amd64
.
In the second example, the go
tool detects that all the dependencies of this program need to be built for the target plaform before hello.go
can be compiled and builds them, all the way back to the runtime
package.
I have not included the output here because it is very verbose, but you can look at all the steps that are being performed if you pass the -x
flag to go build
.
Out of scope
Cross compilation while linking to libraries via cgo is the holy grail for some. Sadly the changes mentioned above do not change the situation with respect to cgo. In fact you may still need to rebuild your Go installation in the traditional way to pass environment variables like CC_FOR_TARGET
. Please try it out.
Conclusion
In case you can’t tell, I’m over the moon about this improvement. Cross compilation is Go’s ace in the hole and Go 1.5 will make it even better.
Please try it out and if you find issues please let us know on the GitHub issue tracker.