diff --git a/README.md b/README.md index 804b786..5061c1b 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/docker/base/build.sh b/docker/base/build.sh index 15c32a9..62d58e3 100644 --- a/docker/base/build.sh +++ b/docker/base/build.sh @@ -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 diff --git a/xgo.go b/xgo.go index c8af2c3..5d8b0b2 100644 --- a/xgo.go +++ b/xgo.go @@ -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] ", 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.