Add support for local import paths.

This commit is contained in:
Péter Szilágyi 2015-09-17 16:10:47 +03:00
parent 3544aa27ec
commit 1827c25933
3 changed files with 107 additions and 68 deletions

View File

@ -62,6 +62,10 @@ Simply specify the import path you want to build, and xgo will do the rest:
-rwxr-xr-x 1 root root 7131477 Sep 14 18:05 iris-windows-386.exe
-rwxr-xr-x 1 root root 8963900 Sep 14 18:05 iris-windows-amd64.exe
If the path is not a canonical import path, but rather a local path (starts with
a dot `.` or a dash `/`), xgo will use the local GOPATH contents for the cross
compilation.
### Build flags

View File

@ -17,33 +17,44 @@
# FLAG_RACE - Optional race flag to set on the Go builder
# TARGETS - Comma separated list of build targets to compile for
# GO_VERSION - Bootstrapped version of Go to disable uncupported targets
# EXT_GOPATH - GOPATH elements mounted from the host filesystem
# Download the canonical import path (may fail, don't allow failures beyond)
echo "Fetching main repository $1..."
go get -d $1
set -e
if [ "$EXT_GOPATH" != "" ]; then
# If local builds are requested, inject the sources
echo "Building locally $1..."
export GOPATH=$GOPATH:$EXT_GOPATH
set -e
cd $GOPATH/src/$1
export GOPATH=$GOPATH:`pwd`/Godeps/_workspace
# Find and change into the package folder
cd `go list -e -f {{.Dir}} $1`
export GOPATH=$GOPATH:`pwd`/Godeps/_workspace
else
# Otherwise download the canonical import path (may fail, don't allow failures beyond)
echo "Fetching main repository $1..."
go get -d $1
set -e
# Switch over the code-base to another checkout if requested
if [ "$REPO_REMOTE" != "" ]; then
echo "Switching over to remote $REPO_REMOTE..."
if [ -d ".git" ]; then
git remote set-url origin $REPO_REMOTE
git pull
elif [ -d ".hg" ]; then
echo -e "[paths]\ndefault = $REPO_REMOTE\n" >> .hg/hgrc
hg pull
cd $GOPATH/src/$1
export GOPATH=$GOPATH:`pwd`/Godeps/_workspace
# Switch over the code-base to another checkout if requested
if [ "$REPO_REMOTE" != "" ]; then
echo "Switching over to remote $REPO_REMOTE..."
if [ -d ".git" ]; then
git remote set-url origin $REPO_REMOTE
git pull
elif [ -d ".hg" ]; then
echo -e "[paths]\ndefault = $REPO_REMOTE\n" >> .hg/hgrc
hg pull
fi
fi
fi
if [ "$REPO_BRANCH" != "" ]; then
echo "Switching over to branch $REPO_BRANCH..."
if [ -d ".git" ]; then
git checkout $REPO_BRANCH
elif [ -d ".hg" ]; then
hg checkout $REPO_BRANCH
if [ "$REPO_BRANCH" != "" ]; then
echo "Switching over to branch $REPO_BRANCH..."
if [ -d ".git" ]; then
git checkout $REPO_BRANCH
elif [ -d ".hg" ]; then
hg checkout $REPO_BRANCH
fi
fi
fi
@ -73,7 +84,6 @@ if [ "$TARGETS" == "" ]; then
fi
# Build for each requested platform individually
builds=0
for TARGET in $TARGETS; do
# Split the target into platform and architecture
XGOOS=`echo $TARGET | cut -d '/' -f 1`
@ -103,10 +113,9 @@ for TARGET in $TARGETS; do
CC=arm-linux-androideabi-gcc GOOS=android GOARCH=arm GOARM=7 CGO_ENABLED=1 CGO_CFLAGS="$CGO_CCPIE" CGO_LDFLAGS="$CGO_LDPIE" go install std
echo "Compiling for android-$PLATFORM/arm..."
CC=arm-linux-androideabi-gcc CXX=arm-linux-androideabi-g++ HOST=arm-linux-androideabi PREFIX=/usr/$ANDROID_CHAIN_ARM $BUILD_DEPS /deps
CC=arm-linux-androideabi-gcc CXX=arm-linux-androideabi-g++ HOST=arm-linux-androideabi PREFIX=/usr/$ANDROID_CHAIN_ARM/arm-linux-androideabi $BUILD_DEPS /deps
CC=arm-linux-androideabi-gcc CXX=arm-linux-androideabi-g++ GOOS=android GOARCH=arm GOARM=7 CGO_ENABLED=1 CGO_CFLAGS="$CGO_CCPIE" CGO_CXXFLAGS="$CGO_CCPIE" CGO_LDFLAGS="$CGO_LDPIE" go get $V $X -d ./$PACK
CC=arm-linux-androideabi-gcc CXX=arm-linux-androideabi-g++ GOOS=android GOARCH=arm GOARM=7 CGO_ENABLED=1 CGO_CFLAGS="$CGO_CCPIE" CGO_CXXFLAGS="$CGO_CCPIE" CGO_LDFLAGS="$CGO_LDPIE" go build --ldflags="$EXT_LDPIE" $V $X $R -o $NAME-android-$PLATFORM-arm$R ./$PACK
builds=$((builds+1))
CC=arm-linux-androideabi-gcc CXX=arm-linux-androideabi-g++ GOOS=android GOARCH=arm GOARM=7 CGO_ENABLED=1 CGO_CFLAGS="$CGO_CCPIE" CGO_CXXFLAGS="$CGO_CCPIE" CGO_LDFLAGS="$CGO_LDPIE" go build --ldflags="$EXT_LDPIE" $V $X $R -o /build/$NAME-android-$PLATFORM-arm$R ./$PACK
fi
fi
fi
@ -115,58 +124,44 @@ for TARGET in $TARGETS; do
echo "Compiling for linux/amd64..."
HOST=x86_64-linux PREFIX=/usr/local $BUILD_DEPS /deps
GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go get $V $X -d ./$PACK
GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build $V $X $R -o $NAME-linux-amd64$R ./$PACK
builds=$((builds+1))
GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build $V $X $R -o /build/$NAME-linux-amd64$R ./$PACK
fi
if ([ $XGOOS == "." ] || [ $XGOOS == "linux" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "386" ]); then
echo "Compiling for linux/386..."
HOST=i686-linux PREFIX=/usr/local $BUILD_DEPS /deps
GOOS=linux GOARCH=386 CGO_ENABLED=1 go get $V $X -d ./$PACK
GOOS=linux GOARCH=386 CGO_ENABLED=1 go build $V $X -o $NAME-linux-386 ./$PACK
builds=$((builds+1))
GOOS=linux GOARCH=386 CGO_ENABLED=1 go build $V $X -o /build/$NAME-linux-386 ./$PACK
fi
if ([ $XGOOS == "." ] || [ $XGOOS == "linux" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "arm" ]); then
echo "Compiling for linux/arm..."
CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabi-g++ HOST=arm-linux PREFIX=/usr/local/arm $BUILD_DEPS /deps
CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabi-g++ GOOS=linux GOARCH=arm CGO_ENABLED=1 GOARM=5 go get $V $X -d ./$PACK
CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabi-g++ GOOS=linux GOARCH=arm CGO_ENABLED=1 GOARM=5 go build $V $X -o $NAME-linux-arm ./$PACK
builds=$((builds+1))
CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabi-g++ GOOS=linux GOARCH=arm CGO_ENABLED=1 GOARM=5 go build $V $X -o /build/$NAME-linux-arm ./$PACK
fi
# Check and build for Windows targets
if ([ $XGOOS == "." ] || [ $XGOOS == "windows" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "amd64" ]); then
echo "Compiling for windows/amd64..."
CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ HOST=x86_64-w64-mingw32 PREFIX=/usr/x86_64-w64-mingw32 $BUILD_DEPS /deps
CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ GOOS=windows GOARCH=amd64 CGO_ENABLED=1 go get $V $X -d ./$PACK
CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ GOOS=windows GOARCH=amd64 CGO_ENABLED=1 go build $V $X $R -o $NAME-windows-amd64$R.exe ./$PACK
builds=$((builds+1))
CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ GOOS=windows GOARCH=amd64 CGO_ENABLED=1 go build $V $X $R -o /build/$NAME-windows-amd64$R.exe ./$PACK
fi
if ([ $XGOOS == "." ] || [ $XGOOS == "windows" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "386" ]); then
echo "Compiling for windows/386..."
CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ HOST=i686-w64-mingw32 PREFIX=/usr/i686-w64-mingw32 $BUILD_DEPS /deps
CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ GOOS=windows GOARCH=386 CGO_ENABLED=1 go get $V $X -d ./$PACK
CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ GOOS=windows GOARCH=386 CGO_ENABLED=1 go build $V $X -o $NAME-windows-386.exe ./$PACK
builds=$((builds+1))
CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ GOOS=windows GOARCH=386 CGO_ENABLED=1 go build $V $X -o /build/$NAME-windows-386.exe ./$PACK
fi
# Check and build for OSX targets
if ([ $XGOOS == "." ] || [ $XGOOS == "darwin" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "amd64" ]); then
echo "Compiling for darwin/amd64..."
CC=o64-clang CXX=o64-clang++ HOST=x86_64-apple-darwin10 PREFIX=/usr/local $BUILD_DEPS /deps
CC=o64-clang CXX=o64-clang++ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go get $V $X -d ./$PACK
CC=o64-clang CXX=o64-clang++ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go build -ldflags=-s $V $X $R -o $NAME-darwin-amd64$R ./$PACK
builds=$((builds+1))
CC=o64-clang CXX=o64-clang++ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go build -ldflags=-s $V $X $R -o /build/$NAME-darwin-amd64$R ./$PACK
fi
if ([ $XGOOS == "." ] || [ $XGOOS == "darwin" ]) && ([ $XGOARCH == "." ] || [ $XGOARCH == "386" ]); then
echo "Compiling for darwin/386..."
CC=o32-clang CXX=o32-clang++ HOST=i386-apple-darwin10 PREFIX=/usr/local $BUILD_DEPS /deps
CC=o32-clang CXX=o32-clang++ GOOS=darwin GOARCH=386 CGO_ENABLED=1 go get $V $X -d ./$PACK
CC=o32-clang CXX=o32-clang++ GOOS=darwin GOARCH=386 CGO_ENABLED=1 go build -ldflags=-s $V $X -o $NAME-darwin-386 ./$PACK
builds=$((builds+1))
CC=o32-clang CXX=o32-clang++ GOOS=darwin GOARCH=386 CGO_ENABLED=1 go build -ldflags=-s $V $X -o /build/$NAME-darwin-386 ./$PACK
fi
done
if [ "$builds" -eq 0 ]; then
echo "No build targets matched!"
else
echo "Moving $builds binaries to host..."
cp `ls -t | head -n $builds` /build
fi

80
xgo.go
View File

@ -10,9 +10,12 @@ import (
"bytes"
"flag"
"fmt"
"go/build"
"log"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
)
@ -21,14 +24,16 @@ 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")
var inPackage = flag.String("pkg", "", "Sub-package to build if not root import")
var outPrefix = flag.String("out", "", "Prefix to use for output naming (empty = package name)")
var srcRemote = flag.String("remote", "", "Version control remote repository to build")
var srcBranch = flag.String("branch", "", "Version control branch to build")
var crossDeps = flag.String("deps", "", "CGO dependencies (configure/make based archives)")
var targets = flag.String("targets", "*/*", "Comma separated targets to build for")
var dockerImage = flag.String("image", "", "Use a custom docker image instead of official distribution")
var (
goVersion = flag.String("go", "latest", "Go release to use for cross compilation")
inPackage = flag.String("pkg", "", "Sub-package to build if not root import")
outPrefix = flag.String("out", "", "Prefix to use for output naming (empty = package name)")
srcRemote = flag.String("remote", "", "Version control remote repository to build")
srcBranch = flag.String("branch", "", "Version control branch to build")
crossDeps = flag.String("deps", "", "CGO dependencies (configure/make based archives)")
targets = flag.String("targets", "*/*", "Comma separated targets to build for")
dockerImage = flag.String("image", "", "Use custom docker image instead of official distribution")
)
// Command line arguments to pass to go build
var buildVerbose = flag.Bool("v", false, "Print the names of packages as they are compiled")
@ -46,12 +51,11 @@ func main() {
if len(flag.Args()) != 1 {
log.Fatalf("Usage: %s [options] <go import path>", os.Args[0])
}
// Select the image to use, either official or custom
image := dockerDist + *goVersion
if dockerImage != nil {
if *dockerImage != "" {
image = *dockerImage
}
// Check that all required images are available
found, err := checkDockerImage(image)
switch {
@ -99,23 +103,59 @@ func pullDockerImage(image string) error {
// Cross compiles a requested package into the current working directory.
func compile(repo string, image string, remote string, branch string, pack string, deps string, prefix string, verbose bool, steps bool, race bool, targets []string) error {
// Retrieve the current folder to store the binaries in
folder, err := os.Getwd()
if err != nil {
log.Fatalf("Failed to retrieve the working directory: %v.", err)
}
// If a local build was requested, find the import path and mount all GOPATH sources
locals, mounts, paths := []string{}, []string{}, []string{}
if strings.HasPrefix(repo, string(filepath.Separator)) || strings.HasPrefix(repo, ".") {
// Resolve the repository import path from the file path
path, err := filepath.Abs(repo)
if err != nil {
log.Fatalf("Failed to locate requested package: %v.", err)
}
stat, err := os.Stat(path)
if err != nil || !stat.IsDir() {
log.Fatalf("Requested path invalid.")
}
pack, err := build.ImportDir(path, build.FindOnly)
if err != nil {
log.Fatalf("Failed to resolve import path: %v.", err)
}
repo = pack.ImportPath
// Iterate over all the local libs and export the mount points
for i, gopath := range strings.Split(os.Getenv("GOPATH"), string(os.PathListSeparator)) {
locals = append(locals, filepath.Join(gopath, "src"))
mounts = append(mounts, filepath.Join("/ext-go", strconv.Itoa(i), "src"))
paths = append(paths, filepath.Join("/ext-go", strconv.Itoa(i)))
}
}
// Assemble and run the cross compilation command
fmt.Printf("Cross compiling %s...\n", repo)
return run(exec.Command("docker", "run",
"-v", folder+":/build",
"-e", "REPO_REMOTE="+remote,
"-e", "REPO_BRANCH="+branch,
"-e", "PACK="+pack,
"-e", "DEPS="+deps,
"-e", "OUT="+prefix,
args := []string{
"run",
"-v", folder + ":/build",
"-e", "REPO_REMOTE=" + remote,
"-e", "REPO_BRANCH=" + branch,
"-e", "PACK=" + pack,
"-e", "DEPS=" + deps,
"-e", "OUT=" + prefix,
"-e", fmt.Sprintf("FLAG_V=%v", verbose),
"-e", fmt.Sprintf("FLAG_X=%v", steps),
"-e", fmt.Sprintf("FLAG_RACE=%v", race),
"-e", "TARGETS="+strings.Replace(strings.Join(targets, " "), "*", ".", -1),
image, repo))
"-e", "TARGETS=" + strings.Replace(strings.Join(targets, " "), "*", ".", -1),
}
for i := 0; i < len(locals); i++ {
args = append(args, []string{"-v", fmt.Sprintf("%s:%s:ro", locals[i], mounts[i])}...)
}
args = append(args, []string{"-e", "EXT_GOPATH=" + strings.Join(paths, ":")}...)
args = append(args, []string{image, repo}...)
return run(exec.Command("docker", args...))
}
// Executes a command synchronously, redirecting its output to stdout.