Update xgo to versioned containers.

This commit is contained in:
Péter Szilágyi 2014-08-13 15:09:53 +03:00
parent 98aea0a339
commit d2febd9eba
3 changed files with 95 additions and 160 deletions

View File

@ -1,135 +0,0 @@
# Go CGO cross compiler
# Copyright (c) 2014 Péter Szilágyi. All rights reserved.
#
# Released under the MIT license.
FROM ubuntu:14.04
MAINTAINER Péter Szilágyi <peterke@gmail.com>
# Create a small script to download binaries and validate their checksum
ENV FETCH ./fetch.sh
RUN \
echo '#!/bin/bash' > $FETCH && \
echo 'set -e' >> $FETCH && \
echo 'file=`basename $1`' >> $FETCH && \
echo 'echo "Downloading $1..."' >> $FETCH && \
echo 'wget -q $1' >> $FETCH && \
echo 'echo "$2 $file" > $file.sum' >> $FETCH && \
echo 'sha1sum -c $file.sum' >> $FETCH && \
echo 'rm $file.sum' >> $FETCH && \
chmod +x $FETCH
# Make sure apt-get is up to date and dependent packages are installed
RUN \
apt-get update && \
apt-get install -y automake autogen build-essential ca-certificates \
gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-multilib gcc-mingw-w64 \
clang llvm-dev libtool libxml2-dev uuid-dev libssl-dev patch make \
xz-utils cpio wget unzip git mercurial --no-install-recommends
# Configure the container for OSX cross compilation
ENV OSX_SDK_PATH https://github.com/trevd/android_platform_build2/raw/master/osxsdks10.6.tar.gz
ENV OSX_SDK MacOSX10.6.sdk
RUN \
git clone https://github.com/tpoechtrager/osxcross.git && \
sed -i.bak s/read/#read/g /osxcross/build.sh && \
\
$FETCH $OSX_SDK_PATH f526b4ae9806e8d31e3b094e3f004f8f160a3fad && \
tar -xzf `basename $OSX_SDK_PATH` --strip-components 1 SDKs/$OSX_SDK && \
tar -cjf /osxcross/tarballs/$OSX_SDK.tar.bz2 $OSX_SDK && \
rm -rf `basename $OSX_SDK_PATH` $OSX_SDK && \
\
/osxcross/build.sh
# Define the Go packages for each platform
ENV DIST_LINUX_64 http://golang.org/dl/go1.3.linux-amd64.tar.gz
ENV DIST_LINUX_32 http://golang.org/dl/go1.3.linux-386.tar.gz
ENV DIST_LINUX_ARM http://dave.cheney.net/paste/go.1.3.linux-arm~armv5-1.tar.gz
ENV DIST_OSX_64 http://golang.org/dl/go1.3.darwin-amd64-osx10.6.tar.gz
ENV DIST_OSX_32 http://golang.org/dl/go1.3.darwin-386-osx10.6.tar.gz
ENV DIST_WIN_64 http://golang.org/dl/go1.3.windows-amd64.zip
ENV DIST_WIN_32 http://golang.org/dl/go1.3.windows-386.zip
# Download all the Go packages, install the core Linux package, inject and
# bootstrap the others
RUN \
$FETCH $DIST_LINUX_64 b6b154933039987056ac307e20c25fa508a06ba6 && \
$FETCH $DIST_LINUX_32 22db33b0c4e242ed18a77b03a60582f8014fd8a6 && \
$FETCH $DIST_LINUX_ARM fc059c970a059757778b157b1140a3c56eb1a069 && \
$FETCH $DIST_OSX_64 82ffcfb7962ca7114a1ee0a96cac51c53061ea05 && \
$FETCH $DIST_OSX_32 159d2797bee603a80b829c4404c1fb2ee089cc00 && \
$FETCH $DIST_WIN_64 1e4888e1494aed7f6934acb5c4a1ffb0e9a022b1 && \
$FETCH $DIST_WIN_32 e4e5279ce7d8cafdf210a522a70677d5b9c7589d && \
\
tar -C /usr/local -xzf `basename $DIST_LINUX_64` && \
rm -rf /usr/local/go/pkg/linux_amd64_race && \
\
tar -C /usr/local --wildcards -xzf `basename $DIST_LINUX_32` go/pkg/linux_386 && \
GOOS=linux GOARCH=386 /usr/local/go/pkg/tool/linux_amd64/dist bootstrap && \
tar -C /usr/local --wildcards -xzf `basename $DIST_LINUX_ARM` go/pkg/linux_arm && \
GOOS=linux GOARCH=arm /usr/local/go/pkg/tool/linux_amd64/dist bootstrap && \
\
tar -C /usr/local --wildcards -xzf `basename $DIST_OSX_64` go/pkg/darwin_amd64 && \
GOOS=darwin GOARCH=amd64 /usr/local/go/pkg/tool/linux_amd64/dist bootstrap && \
tar -C /usr/local --wildcards -xzf `basename $DIST_OSX_32` go/pkg/darwin_386 && \
GOOS=darwin GOARCH=386 /usr/local/go/pkg/tool/linux_amd64/dist bootstrap && \
\
unzip -d /usr/local -q `basename $DIST_WIN_64` go/pkg/windows_amd64/* && \
GOOS=windows GOARCH=amd64 /usr/local/go/pkg/tool/linux_amd64/dist bootstrap && \
unzip -d /usr/local -q `basename $DIST_WIN_32` go/pkg/windows_386/* && \
GOOS=windows GOARCH=386 /usr/local/go/pkg/tool/linux_amd64/dist bootstrap && \
\
rm -f `basename $DIST_LINUX_64` `basename $DIST_LINUX_32` `basename $DIST_LINUX_ARM` \
`basename $DIST_OSX_64` `basename $DIST_OSX_32` `basename $DIST_WIN_64` `basename $DIST_WIN_32`
ENV PATH /usr/local/go/bin:$PATH
ENV GOPATH /go
# Create a small script to go get a package and cross compile it
ENV BUILD ./build.sh
RUN \
echo '#!/bin/bash' > $BUILD && \
echo 'set -e' >> $BUILD && \
echo >> $BUILD && \
echo 'echo Fetching $1...' >> $BUILD && \
echo 'go get $1' >> $BUILD && \
echo 'cd $GOPATH/src/$1' >> $BUILD && \
echo 'pack=`basename $1`' >> $BUILD && \
echo >> $BUILD && \
echo 'echo Compiling for linux/amd64...' >> $BUILD && \
echo 'GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -o $pack-linux-amd64' >> $BUILD && \
echo >> $BUILD && \
echo 'echo Compiling for linux/386...' >> $BUILD && \
echo 'GOOS=linux GOARCH=386 CGO_ENABLED=1 go build -o $pack-linux-386' >> $BUILD && \
echo >> $BUILD && \
echo 'echo Compiling for linux/arm...' >> $BUILD && \
echo 'CC=arm-linux-gnueabi-gcc \\' >> $BUILD && \
echo ' GOOS=linux GOARCH=arm CGO_ENABLED=1 go build -o $pack-linux-arm' >> $BUILD && \
echo >> $BUILD && \
echo 'echo Compiling for windows/amd64...' >> $BUILD && \
echo 'CC=x86_64-w64-mingw32-gcc \\' >> $BUILD && \
echo ' GOOS=windows GOARCH=amd64 CGO_ENABLED=1 go build -o $pack-windows-amd64.exe' >> $BUILD && \
echo >> $BUILD && \
echo 'echo Compiling for windows/386...' >> $BUILD && \
echo 'CC=i686-w64-mingw32-gcc \\' >> $BUILD && \
echo ' GOOS=windows GOARCH=386 CGO_ENABLED=1 go build -o $pack-windows-386.exe' >> $BUILD && \
echo >> $BUILD && \
echo 'echo Compiling for darwin/amd64...' >> $BUILD && \
echo '`/osxcross/target/bin/osxcross-env`' >> $BUILD && \
echo 'CC=o64-clang \\' >> $BUILD && \
echo ' GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go build -o $pack-darwin-amd64' >> $BUILD && \
echo >> $BUILD && \
echo 'echo Compiling for darwin/386...' >> $BUILD && \
echo 'CC=o32-clang \\' >> $BUILD && \
echo ' GOOS=darwin GOARCH=386 CGO_ENABLED=1 go build -o $pack-darwin-386' >> $BUILD && \
echo >> $BUILD && \
echo 'echo Moving binaries to host...' >> $BUILD && \
echo 'cp `ls -t | head -n 7` /build' >> $BUILD && \
chmod +x $BUILD
ENTRYPOINT ["./build.sh"]

View File

@ -1,5 +1,4 @@
xgo - Go CGO cross compiler # xgo - Go CGO cross compiler
===========================
Although Go strives to be a cross platform language, cross compilation from one Although Go strives to be a cross platform language, cross compilation from one
platform to another is not as simple as it could be, as you need the Go sources platform to another is not as simple as it could be, as you need the Go sources
@ -26,8 +25,7 @@ libraries. This becomes very annoying when you need access only to some trivial
OS specific functionality (e.g. query the CPU load), but need to configure and OS specific functionality (e.g. query the CPU load), but need to configure and
maintain separate build environments to do it. maintain separate build environments to do it.
Enter xgo ## Enter xgo
---------
My solution to the challenge of cross compiling Go code with embedded C snippets My solution to the challenge of cross compiling Go code with embedded C snippets
(i.e. CGO_ENABLED=1) is based on the concept of [lightweight Linux containers](http://en.wikipedia.org/wiki/LXC). (i.e. CGO_ENABLED=1) is based on the concept of [lightweight Linux containers](http://en.wikipedia.org/wiki/LXC).
@ -35,21 +33,19 @@ All the necessary Go tool-chains, C cross compilers and platform headers/librari
have been assembled into a single Docker container, which can then be called as if have been assembled into a single Docker container, which can then be called as if
a single command to compile a Go package to various platforms and architectures. a single command to compile a Go package to various platforms and architectures.
Installation ## Installation
------------
Although you could build the container manually, it is available as an automatic Although you could build the container manually, it is available as an automatic
trusted build from Docker's container registry (67MB base + 472MB xgo): trusted build from Docker's container registry (~530MB):
docker pull karalabe/xgo docker pull karalabe/xgo-latest
To prevent having to remember a potentially complex Docker command every time, To prevent having to remember a potentially complex Docker command every time,
a lightweight Go wrapper was written on top of it. a lightweight Go wrapper was written on top of it.
go get github.com/karalabe/xgo go get github.com/karalabe/xgo
Usage ## Usage
-----
Simply specify the import path you want to build, and xgo will do the rest: Simply specify the import path you want to build, and xgo will do the rest:
@ -64,3 +60,25 @@ Simply specify the import path you want to build, and xgo will do the rest:
-rwxr-xr-x 1 root root 4151688 Aug 7 10:01 iris-linux-arm -rwxr-xr-x 1 root root 4151688 Aug 7 10:01 iris-linux-arm
-rwxr-xr-x 1 root root 4228608 Aug 7 10:01 iris-windows-386.exe -rwxr-xr-x 1 root root 4228608 Aug 7 10:01 iris-windows-386.exe
-rwxr-xr-x 1 root root 5243904 Aug 7 10:01 iris-windows-amd64.exe -rwxr-xr-x 1 root root 5243904 Aug 7 10:01 iris-windows-amd64.exe
### Go releases
As newer versions of the language runtime, libraries and tools get released,
these will get incorporated into xgo too as extensions layers to the base cross
compilation image (only Go 1.3 and above will be supported).
You can select which Go release to work with through the `-go` command line flag
to xgo and if the specific release was already integrated, it will automatically
be retrieved and installed.
$ xgo -go 1.3.0 github.com/project-iris/iris
...
Since xgo depends on not only the official releases, but also on Dave Cheney's
ARM packages, there will be a slight delay between official Go updates and the
xgo updates.
Additionally, a few wildcard release strings are also supported:
- `-go latest` will use the latest Go release
- `-go 1.3.x` will use the latest point release of a specific Go version

82
xgo.go
View File

@ -7,6 +7,8 @@
package main package main
import ( import (
"bytes"
"flag"
"fmt" "fmt"
"io" "io"
"log" "log"
@ -14,31 +16,81 @@ import (
"os/exec" "os/exec"
) )
// Docker container configured for cross compilation. // Cross compilation docker containers
var container = "karalabe/xgo" var dockerBase = "karalabe/xgo-base"
var dockerDist = "karalabe/xgo-"
// Command line arguments to fine tune the compilation
var goVersion = flag.String("go", "latest", "Go release to use for cross compilation")
func main() { func main() {
// Make sure docker is actually available on the system flag.Parse()
fmt.Println("Checking docker installation...")
if err := run(exec.Command("docker", "version")); err != nil { // Ensure docker is available
if err := checkDocker(); err != nil {
log.Fatalf("Failed to check docker installation: %v.", err) log.Fatalf("Failed to check docker installation: %v.", err)
} }
fmt.Println() // Validate the command line arguments
if len(flag.Args()) != 1 {
// Fetch and configure the compilation settings log.Fatalf("Usage: %s [options] <go import path>", os.Args[0])
if len(os.Args) != 2 {
log.Fatalf("Usage: %s <go import path>", os.Args[0])
} }
path := os.Args[1] // Check that all required images are available
pwd, err := os.Getwd() found, err := checkDockerImage(dockerDist + *goVersion)
switch {
case err != nil:
log.Fatalf("Failed to check docker image availability: %v.", err)
case !found:
fmt.Println("not found!")
if err := pullDockerImage(dockerDist + *goVersion); err != nil {
log.Fatalf("Failed to pull docker image from the registry: %v.", err)
}
default:
fmt.Println("found.")
}
// Cross compile the requested package into the local folder
if err := compile(flag.Args()[0]); err != nil {
log.Fatalf("Failed to cross compile package: %v.", err)
}
}
// Checks whether a docker installation can be found and is functional.
func checkDocker() error {
fmt.Println("Checking docker installation...")
if err := run(exec.Command("docker", "version")); err != nil {
return err
}
fmt.Println()
return nil
}
// Checks whether a required docker image is available locally.
func checkDockerImage(image string) (bool, error) {
fmt.Printf("Checking for required docker image %s... ", image)
out, err := exec.Command("docker", "images", "--no-trunc").Output()
if err != nil {
return false, err
}
return bytes.Contains(out, []byte(image)), nil
}
// Pulls an image from the docker registry.
func pullDockerImage(image string) error {
fmt.Printf("Pulling %s from docker registry...\n", image)
fmt.Printf("Note, this may take some time, but due to a docker bug, progress cannot be displayed.\n")
return run(exec.Command("docker", "pull", image))
}
// Cross compiles a requested package into the current working directory.
func compile(path string) error {
folder, err := os.Getwd()
if err != nil { if err != nil {
log.Fatalf("Failed to retrieve the working directory: %v.", err) log.Fatalf("Failed to retrieve the working directory: %v.", err)
} }
// Cross compile the requested package into the local folder
fmt.Printf("Cross compiling %s...\n", path) fmt.Printf("Cross compiling %s...\n", path)
if err := run(exec.Command("docker", "run", "-v", pwd+":/build", container, path)); err != nil { if err := run(exec.Command("docker", "run", "-v", folder+":/build", dockerDist+*goVersion, path)); err != nil {
log.Fatalf("Failed to cross compile package: %v.", err) return err
} }
return nil
} }
// Executes a command synchronously, redirecting its output to stdout. // Executes a command synchronously, redirecting its output to stdout.