A nice feature of the Go language is the ability to build binaries for multiple platforms directly from a single source system. As an example, even from a development Windows 7 32-bit machine, you can build binaries for both 64 bit Linux and Windows 2012 Servers.
Before Go 1.5, you needed a compiler for the target architecture, but now that the entire tool chain is written in Go, building for multiple architectures is easy.
And unlike other languages where additional external libraries need to be copied or downloaded on the target system, Go dependencies are generally statically linked [1,2,3,4] into a single binary which makes portability that much easier.
Building for default architecture
Let’s use a simple go file as an example. Assuming you have installed Go and have the proper environment variable setup, run the following commands on Ubuntu (or the equivalent on Windows):
$ cd $GOPATH $ mkdir -p src/myarch $ cd src/myarch
And then either copy the simple go file below, or download it directly from my github project.
// put at $GOPATH/src/myarch/myarch.go package main import "fmt" import "runtime" func main() { fmt.Printf("Hello from: %s %s\n",runtime.GOOS,runtime.GOARCH) }
Being on a 64 bit Ubuntu 14.04 host, doing either a default build or specifying a 64 bit binary explicitly results in:
$ go build $ ./myarch Hello from: linux amd64 $ env GOARCH=amd64 go build $ ./myarch Hello from linux amd64
And you can also verify the binary target architecture by having the ‘file’ command look at the header:
$ file ./myarch ./myarch: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
For Linux, to check if the binary is statically or dynamically linked, use the ‘ldd’ and ‘readelf’ utilities. The below output is for a statically linked binary.
$ ldd ./myarch not a dynamic executable # readelf should return empty if statically linked $ readelf -l myarch | grep interpret
Alternatively, a dynamically linked binary would have return back results that look similar to:
$ldd ./mybinary linux-vdso.so.1 => (0x00007ffea676e000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fd8acfee000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd8acc26000) /lib64/ld-linux-x86-64.so.2 (0x00007fd8ad20c000) $ readelf -l mybinary | grep interpret [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
To force a Go binary to be statically linked set the CGO_ENABLED environment variable to 0 before running the build:
$ export CGO_ENABLED=0 $ go build -a
Building for Linux 32 bit
If I needed to build a 32 bit Linux binary (even though my host server is a 64 bit Linux), I could specify a different target architecture.
$ env GOARCH=386 go build $ ./myarch Hello from: linux 386
And you can also verify the binary by having the ‘file’ command look at the header:
$ file ./myarch ./myarch: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped
Building for Windows 32 bit
If I needed to build a 32 bit Windows binary (even though my host server is a 64 bit Linux), I could specify a different target architecture and OS.
$ env GOOS=windows GOARCH=386 go build
And you can also verify the binary by having the ‘file’ command look at the header:
$ file ./myarch myarch.exe: PE32 executable (console) Intel 80386 (stripped to external PDB), for MS Windows
If you copy this file over to a Windows 32-bit host and run it:
> myarch.exe hello from: windows 386
And if you try to run a 64 bit binary on a 32 bit Windows machine, you get an error saying that the exe is “is not compatible with the version of Windows you’re running”, as expected.
REFERENCES
https://dave.cheney.net/2015/08/22/cross-compilation-with-go-1-5
https://github.com/golang/go/wiki/WindowsCrossCompiling
https://www.infoq.com/news/2015/08/go-1-5
http://stackoverflow.com/questions/16747021/how-do-you-statically-link-a-c-library-in-go-using-cgo
https://www.osso.nl/blog/golang-statically-linked/
https://blog.codeship.com/building-minimal-docker-containers-for-go-applications/
https://stackoverflow.com/questions/16747021/how-do-you-statically-link-a-c-library-in-go-using-cgo
https://www.osso.nl/blog/golang-statically-linked/
https://github.com/golang/go/issues/12808
https://github.com/golang/go/issues/9530
https://news.ycombinator.com/item?id=7677699
dh1tw.de Tobias Wellnitz, cross-compling golang with static c libs using Docker
NOTES
go build -a -tags netgo (for further hint on static linking, but CGO_ENABLED=0 has been enough for me)