As those who write multi-platform applications will attest, configuring a reliable cross-development environment on Windows can be a chore. If you're developing a native app that leverages other open source libraries, you've chosen a path that will challenge even the most patient of Zen masters.
Compilers/assemblers/linkers. Headers. Libraries. Build tools. Required source tweaks. Library version linking issues. Upstream maintainers with bad attitudes immune to your best cajoling. Absurdly clueless decisions by tool suppliers a la Microsoft's removal of the command line tools from the Windows 8 SDK after finally doing the right thing years ago with the Visual C++ Toolkit 2003. Great, what started as a challenging adventure quickly turned into a grinding slog-fest through a tarpit infested with brambly roadblocks and fun-sucking vampires.
But, Don Quixote de la MultiPlatform, all is not hopeless. Go to the rescue.
This post is targeted to quickly getting you set up and productive on a Windows development system simply because it has been such a fight in the past. However, thanks to the hard work of the Go contributors, setting up a multi-platform development system on Windows, Linux, OS X and other platforms is simple. As such, all of the info shared below is applicable to building multi-platform Go development environments on non-Windows platforms.
Get and Build Go
The first step toward Go development nirvana is to get a local copy of Go's Mercurial repo via
C:\Apps>hg clone https://code.google.com/p/go/ go-hg C:\Apps>cd go-hg && hg sum parent: 16706:37bf155bc780 tip cmd/5g, cmd/6g, cmd/8g: more nil ptr to large struct checks branch: default commit: 3 unknown (clean) update: (current)
One of the many fantastic features of Go is that Go provides its own multi-platform
capable compilers and tools. Even more fantastic is that both the toolchain and
the Go environment are easily built on Windows 32/64-bit systems with just a simple
MinGW gcc
setup.
I've built Go with a number of different MinGW flavors including the 32 and 64-bit gcc 4.8.0 mingw-w64 flavors. In all cases, the build experience has been painless. When I've run into issues, the committers have been fast and easy to work with.
Automating Your Cross Builds
One of the most valuable gifts you can give your hacking self is to automate your build workflows so you can stay in the zone. This task is often a tooth pulling experience, but not with Go. Go's default build helpers work equally well on Linux, Windows, and OS X.
While the Go install documentation shows how to build the Go environment from source, I chose to create the following PowerShell helper to automate building my Go environment to support 32-bit Windows, Linux, and OS X platforms. I use a slightly more complex version on my Windows 8 system to build for 32/64-bit Linux, Windows, and OS X platforms.
# file: build_all.ps1
$toolkit = 'C:\DevKit-mb4.8.0\mingw\bin'
$targets = 'windows:386:1', 'linux:386:0', 'darwin:386:0'
$orig_path = $env:PATH
$env:PATH = "$toolkit;$env:PATH"
Push-Location src
$targets | % {
$env:GOOS, $env:GOARCH, $env:CGO_ENABLED = $_.Split(':')
switch ($env:CGO_ENABLED) {
'0' { $cmd = 'make.bat --no-clean' }
'1' { $cmd = 'all.bat' }
}
Write-Host "`n---> building for $env:GOOS/$env:GOARCH platform`n" `
-foregroundcolor yellow
Invoke-Expression ".\$cmd"
}
Pop-Location
$env:PATH = $orig_path
If you're stuck with using .bat
files, here's a helper to get you started.
:: file: build_all.bat
@echo off
setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
set DEVTOOLSDIR=C:\DevKit-mb4.8.0\mingw\bin
set PATH=%DEVTOOLSDIR%;%PATH%
pushd src
echo.
echo ---^> building Go for windows 386
echo.
set GOOS=windows
set GOARCH=386
set CGO_ENABLED=1
call all.bat
echo.
echo ---^> building Go for linux 386
echo.
set GOOS=linux
set CGO_ENABLED=0
call make.bat --no-clean
echo.
echo ---^> building Go for darwin 386
echo.
set GOOS=darwin
call make.bat --no-clean
popd
Go Build Multi-platform Apps
Now that you've created a reliable, multi-platform Go development environment, it's almost embarassing how easy it is to build Go applications that run on Windows, Linux and OS X systems.
In a future post I'll go into more details of other Go goodies such as Go's #ifdef
killer build contraints. For now, here's a simple
Ruby Rakefile
showing how easy it is to build multi-platform Go apps using the
GOOS
and GOARCH
environment variables, and the go
command line tool.
require 'rake/clean'
require 'rbconfig'
# --- BUILD CONFIGURATION ---
UPX_EXE = 'C:/Apps/upx/bin/upx.exe'
S7ZIP_EXE = 'C:/tools/7za.exe'
# ---------------------------
task :default => :all
ARCH = ENV['GOARCH'] || '386'
BUILD = 'build'
PKG = File.expand_path('pkg')
CLEAN.include(BUILD)
CLOBBER.include(PKG)
def dev_null
if RbConfig::CONFIG['host_os'] =~ /mingw|mswin/
'NUL'
else
'/dev/null'
end
end
desc 'build all OS/arch flavors'
task :all => %W[build:windows_#{ARCH} build:linux_#{ARCH} build:darwin_#{ARCH}]
namespace :all do
desc 'build and shrink all exes'
task :shrink => [:all] do
Dir.chdir BUILD do
Dir.glob('*').each do |d|
Dir.chdir d do
Dir.glob('uru*').each do |f|
puts "---> upx shrinking #{d} #{f}"
system "#{UPX_EXE} -9 #{f} > #{dev_null} 2>&1"
end
end
end
end
end
end
namespace :build do
%W[windows:#{ARCH}:0 linux:#{ARCH}:0 darwin:#{ARCH}:0].each do |tgt|
os, arch, cgo = tgt.split(':')
ext = (os == 'windows' ? '.exe' : '')
desc "build #{os}/#{arch}"
task :"#{os}_#{arch}" do |t|
puts "---> building uru #{os}_#{arch} flavor"
ENV['GOARCH'] = arch
ENV['GOOS'] = os
ENV['CGO_ENABLED'] = cgo
system "go build -o #{BUILD}/#{t.name.split(':')[-1]}/uru_rt#{ext}"
end
end
end
desc 'archive all built exes'
task :package => 'package:all'
directory PKG
namespace :package do
task :all => ['all:shrink',PKG] do
ts = Time.now.strftime('%Y%m%dT%H%M')
Dir.chdir BUILD do
Dir.glob('*').each do |d|
case d
when /\A(darwin|linux)/
puts "---> packaging #{d}"
system "#{S7ZIP_EXE} a -tgzip -mx9 uru-#{$1}-#{ts}-bin-x86.gz ./#{d}/* > #{dev_null} 2>&1"
mv "uru-#{$1}-#{ts}-bin-x86.gz", PKG, :verbose => false
when /\Awindows/
puts "---> packaging #{d}"
system "#{S7ZIP_EXE} a -t7z -mx9 uru-windows-#{ts}-bin-x86.7z ./#{d}/* > #{dev_null} 2>&1"
mv "uru-windows-#{ts}-bin-x86.7z", PKG, :verbose => false
end
end
end
end
end
Conclusion
As the Go team prepares to release 1.1
, it's a great time to dig into the details
of how Go can make your multi-platform hacking a lot nicer. It's obvious that the
team is both experienced and refreshingly pragmatic in their approach to providing
a great environment for quickly building multi-platform apps. Go take advantage of
their hard work and expertise!