Cross Compile Your Go Programs

Oct 2, 2013


Introduction
In my post about building and running programs in Iron.Io, I needed to switched over to my Ubuntu VM to build linux versions of my test programs locally. I love the ability to have Ubuntu available to me for building and testing my code. However, if I can stay on the Mac side it is better.

I have wanted to learn how to cross compile my Go programs for the two platforms I use, darwin/amd64 and linux/amd64. This way I could create final builds of my programs and publish everything from my Mac OS. After a couple of hours I am finally able to do this.

If you don’t have the need to cross compile your code then I recommend you stick with the traditional distribution packages and installs for Go. If this is something you need, then it all starts with downloading the current release of the Go source code.

Installing Git
The Go source code is stored in a DVCS called Github and is located on github.com/golang/go. The first thing you need to do is install Git if you don’t already have it.

Go to the download page on the Git website:  http://git-scm.com/downloads

Cloning Go Source Code
http://golang.org/doc/install/source

Open up a Terminal session and go to your $HOME directory and clone the current release of the Go source code. This will create a folder named Go, make sure this does not already exist:

cd $HOME
git clone https://go.googlesource.com/go

If everything works correctly you should see the following output or something similar:

Cloning into ‘go’…
remote: Sending approximately 57.30 MiB …
remote: Counting objects: 6825, done
remote: Finding sources: 100% (13451345)
remote: Total 194730 (delta 160700), reused 194473 (delta 160700)
Receiving objects: 100% (194730194730), 55.07 MiB | 2.92 MiB/s, done.
Resolving deltas: 100% (161032161032), done.
Checking connectivity… done.

Once that is done you now have a folder called go inside of the $HOME directory with the source code under src. Now change your directory to the go folder and checkout the latest version:

cd go
git checkout go1.4.1

If everything works correctly you should see the following output or something similar:

Note: checking out ‘go1.4.1’.

You are in ‘detached HEAD’ state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b new_branch_name

HEAD is now at 886b02d… [release-branch.go1.4] go1.4.1

Building Go For Each Target
Now you need to build the Go code for the targets you need. For now just build darwin/amd64 and linux/amd64.

First build the Go code for the darwin/amd64 target which is for the host machine:

cd src
GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 ./make.bash –no-clean

If everything builds correctly you should see the build end like this:


Installed Go for darwin/amd64 in /Users/name/go
Installed commands in /Users/name/go/bin

If you get the following error on your Mac there is a fix:

crypto/x509 root_darwin.go:9:43: error: CoreFoundation/CoreFoundation.h: No such file or directory root_darwin.go:10:31: error: Security/Security.h: No such file or directory mime/multipart net/mail


This means you don’t have the Command Line Tools for Xcode installed on your machine. To install the command line tools on 10.9 follow these instructions:

http://www.computersnyou.com/2025/2013/06/install-command-line-tools-in-osx-10-9-mavericks-how-to/

Click the Install button for the Command Line Tools. Once that is done try building the Go code again.

Once the build is successful, open the Go folder. You should see everything you need for building Go programs on the Mac 64 bit environment:


You have the go, godoc and gofmt tools and all the package related libraries and tools. Next you need to fix your PATH to point to the bin folder so you can start using the tools.

export PATH=$HOME/go/bin:$PATH

You may want to set that in .bashrc or .bash_profile file from inside the $HOME directory.

With the path set, check that Go is working. Check the version and the environment:

go version
go version go1.4.1 darwin/amd64

go env
GOARCH="amd64"
GOBIN=""
GOCHAR="6"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="linux"
GOPATH=""
GORACE=""
GOROOT="/Users/name/go"
GOTOOLDIR="/Users/name/go/pkg/tool/darwin_amd64"
CC="gcc"
GOGCCFLAGS="-g -O2 -fPIC -m64"
CGO_ENABLED="1"

If everything looks good, now build a version of Go that will let you build linux/amd64 binaries:

GOOS=linux GOARCH=amd64 CGO_ENABLED=0 ./make.bash –no-clean

You can’t use CGO when cross compiling. Make sure CGO_ENABLED is set to 0

If everything builds correctly you should see the build end like this:


Installed Go for linux/amd64 in /Users/name/go
Installed commands in /Users/name/go/bin

If you look at the Go folder again you should see some new folders for linux/amd64:


Now it is time to test if you can build Go programs for both the Mac and Linux operating systems. Set up a quick GOPATH and Go program. In Terminal run the following commands:

cd $HOME
mkdir example
mkdir src
mkdir simple
export GOPATH=$HOME/example
cd example/src/simple

Create a file called main.go inside of the simple folder with this code:

package main

import (
    "fmt"
)

func main() {
    fmt.Printf("Hello Gophers\n")
}

First build the Mac version and make sure it is a Mac executable using the file command:

go build
file simple

simple: Mach-O 64-bit executable x86_64

The file command tells us what type of file our program is. It is certainly a Mac executable file.

Now build the code for linux/amd64:

export GOARCH="amd64"
export GOOS="linux"

go build
file simple

Simple: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

You need to change either one or both of the OS/ARCH environment variables to point to the target platform and architecture. Then you can build the code. After the build you can see the file command is reporting the program is a linux executable.

As a reference, here are the different platforms and architectures you can build for cross compilation:

$GOOS     $GOARCH
darwin    386      – 32 bit MacOSX
darwin    amd64    – 64 bit MacOSX
freebsd   386
freebsd   amd64
linux     386      – 32 bit Linux
linux     amd64    – 64 bit Linux
linux     arm      – RISC Linux
netbsd    386
netbsd    amd64
openbsd   386
openbsd   amd64
plan9     386
windows   386      – 32 bit Windows
windows   amd64    – 64 bit Windows

Installing Go Tooling
Once you finish building Go for the targets you want, you will want to install these Go tools for the default target.

go get golang.org/x/tools/cmd/godoc
go get golang.org/x/tools/cmd/vet
go get golang.org/x/tools/cmd/goimports
go get golang.org/x/tools/cmd/gorename
go get golang.org/x/tools/cmd/oracle
go get golang.org/x/tools/cmd/gotype
go get github.com/golang/lint/golint

Release Updates
If you are performing an update be sure to remove the pkg folder from your GOPATH. These archive files will no longer be valid.

Then you will want to update the source code to the current release:

**NOTE: Read This First: http://golang.org/s/go14nopkg

cd $HOME/go/src
git fetch
git checkout go<tag>

Then you will want to build your targets again:

GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 ./make.bash –no-clean
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 ./make.bash –no-clean

Finally re-build the tools. Before doing so, remove it is a good idea to remove the pkg folder from your GOPATH.

go get -u golang.org/x/tools/cmd/godoc
go get -u golang.org/x/tools/cmd/vet
go get -u golang.org/x/tools/cmd/goimports
go get -u golang.org/x/tools/cmd/gorename
go get -u golang.org/x/tools/cmd/oracle
go get -u golang.org/x/tools/cmd/gotype
go get -u github.com/golang/lint/golint

You may want to remove everything from the bin folder as well and go get them to re-build and install.

Conclusion
This is the documentation for building Go from the source:

http://golang.org/doc/install/source

This is a document written by Dave Cheney about cross compilation. He has build a script that you can download. It makes all of this real simple to perform:

http://dave.cheney.net/2013/07/09/an-introduction-to-cross-compilation-with-go-1-1

Mitchell Hashimoto built this great tool called gox. This tool makes it real easy to build your program for all the different targets without the need to manually change the GOARCH and GOOS environment variables.


Go Training

We have taught Go to thousands of developers all around the world since 2014. There is no other company that has been doing it longer and our material has proven to help jump start developers 6 to 12 months ahead of their knowledge of Go. We know what knowledge developers need in order to be productive and efficient when writing software in Go.

Our Go, Web and Data Science classes are perfect for both experienced and beginning engineers. We start every class from the beginning and get very detailed about the internals, mechanics, specification, guidelines, best practices and design philosophies. We cover a lot about "if performance matters" with a focus on mechanical sympathy, data oriented design, decoupling and writing production software.

Learn More

To learn about Corporate training events, options and special pricing please contact:

William Kennedy
ArdanLabs (www.ardanlabs.com)
bill@ardanlabs.com