1
0

Switched to techknowlogick/xgo

This commit is contained in:
kolaente
2019-04-22 14:02:18 +02:00
parent c8858f6cfb
commit 10f80c190b
12 changed files with 443 additions and 76 deletions

53
vendor/src.techknowlogick.com/xgo/.drone.jsonnet generated vendored Normal file
View File

@ -0,0 +1,53 @@
local BuildSwitchDryRun(version='go-latest', tags='latest', dry=false) = {
name: if dry then 'dry-run-' + version else 'build-' + version,
pull: 'always',
image: 'plugins/docker',
settings: {
dockerfile: 'docker/' + version + '/Dockerfile',
context: 'docker/' + version,
password: {
from_secret: 'docker_password'
},
username: {
from_secret: 'docker_username'
},
repo: 'techknowlogick/xgo',
tags: tags,
dry_run: dry
},
[if !dry then 'when']: {
branch: ['master'],
event: {exclude: ['pull_request']}
},
[if dry then 'when']: {
event: {include: ['pull_request']}
},
};
local BuildWithDiffTags(version='go-latest', tags='latest') = BuildSwitchDryRun(version, tags, false);
local BuildWithDiffTagsDry(version='go-latest', tags='latest') = BuildSwitchDryRun(version, tags, true);
local BuildStep(version='go-latest') = BuildWithDiffTags(version, version);
local BuildStepDry(version='go-latest') = BuildSwitchDryRun(version, version, true);
{
kind: 'pipeline',
name: 'default',
steps: [
BuildStepDry('base'),
BuildStepDry('go-1.12.4'),
BuildStepDry('go-1.11.9'),
BuildStep('base'),
BuildStep('go-1.12.4'),
BuildStep('go-1.12.x'),
BuildWithDiffTags(),
BuildStep('go-1.11.9'),
BuildStep('go-1.11.x'),
BuildStep('go-1.12.3'),
BuildStep('go-1.12.2'),
BuildStep('go-1.12.1'),
BuildStep('go-1.12.0'),
BuildStep('go-1.11.8'),
BuildStep('go-1.11.7'),
]
}

289
vendor/src.techknowlogick.com/xgo/.drone.yml generated vendored Normal file
View File

@ -0,0 +1,289 @@
---
kind: pipeline
name: default
platform:
os: linux
arch: amd64
steps:
- name: dry-run-base
pull: always
image: plugins/docker
settings:
context: docker/base
dockerfile: docker/base/Dockerfile
dry_run: true
password:
from_secret: docker_password
repo: techknowlogick/xgo
tags: base
username:
from_secret: docker_username
when:
event:
- pull_request
- name: dry-run-go-1.12.4
pull: always
image: plugins/docker
settings:
context: docker/go-1.12.4
dockerfile: docker/go-1.12.4/Dockerfile
dry_run: true
password:
from_secret: docker_password
repo: techknowlogick/xgo
tags: go-1.12.4
username:
from_secret: docker_username
when:
event:
- pull_request
- name: dry-run-go-1.11.9
pull: always
image: plugins/docker
settings:
context: docker/go-1.11.9
dockerfile: docker/go-1.11.9/Dockerfile
dry_run: true
password:
from_secret: docker_password
repo: techknowlogick/xgo
tags: go-1.11.9
username:
from_secret: docker_username
when:
event:
- pull_request
- name: build-base
pull: always
image: plugins/docker
settings:
context: docker/base
dockerfile: docker/base/Dockerfile
password:
from_secret: docker_password
repo: techknowlogick/xgo
tags: base
username:
from_secret: docker_username
when:
branch:
- master
event:
exclude:
- pull_request
- name: build-go-1.12.4
pull: always
image: plugins/docker
settings:
context: docker/go-1.12.4
dockerfile: docker/go-1.12.4/Dockerfile
password:
from_secret: docker_password
repo: techknowlogick/xgo
tags: go-1.12.4
username:
from_secret: docker_username
when:
branch:
- master
event:
exclude:
- pull_request
- name: build-go-1.12.x
pull: always
image: plugins/docker
settings:
context: docker/go-1.12.x
dockerfile: docker/go-1.12.x/Dockerfile
password:
from_secret: docker_password
repo: techknowlogick/xgo
tags: go-1.12.x
username:
from_secret: docker_username
when:
branch:
- master
event:
exclude:
- pull_request
- name: build-go-latest
pull: always
image: plugins/docker
settings:
context: docker/go-latest
dockerfile: docker/go-latest/Dockerfile
password:
from_secret: docker_password
repo: techknowlogick/xgo
tags: latest
username:
from_secret: docker_username
when:
branch:
- master
event:
exclude:
- pull_request
- name: build-go-1.11.9
pull: always
image: plugins/docker
settings:
context: docker/go-1.11.9
dockerfile: docker/go-1.11.9/Dockerfile
password:
from_secret: docker_password
repo: techknowlogick/xgo
tags: go-1.11.9
username:
from_secret: docker_username
when:
branch:
- master
event:
exclude:
- pull_request
- name: build-go-1.11.x
pull: always
image: plugins/docker
settings:
context: docker/go-1.11.x
dockerfile: docker/go-1.11.x/Dockerfile
password:
from_secret: docker_password
repo: techknowlogick/xgo
tags: go-1.11.x
username:
from_secret: docker_username
when:
branch:
- master
event:
exclude:
- pull_request
- name: build-go-1.12.3
pull: always
image: plugins/docker
settings:
context: docker/go-1.12.3
dockerfile: docker/go-1.12.3/Dockerfile
password:
from_secret: docker_password
repo: techknowlogick/xgo
tags: go-1.12.3
username:
from_secret: docker_username
when:
branch:
- master
event:
exclude:
- pull_request
- name: build-go-1.12.2
pull: always
image: plugins/docker
settings:
context: docker/go-1.12.2
dockerfile: docker/go-1.12.2/Dockerfile
password:
from_secret: docker_password
repo: techknowlogick/xgo
tags: go-1.12.2
username:
from_secret: docker_username
when:
branch:
- master
event:
exclude:
- pull_request
- name: build-go-1.12.1
pull: always
image: plugins/docker
settings:
context: docker/go-1.12.1
dockerfile: docker/go-1.12.1/Dockerfile
password:
from_secret: docker_password
repo: techknowlogick/xgo
tags: go-1.12.1
username:
from_secret: docker_username
when:
branch:
- master
event:
exclude:
- pull_request
- name: build-go-1.12.0
pull: always
image: plugins/docker
settings:
context: docker/go-1.12.0
dockerfile: docker/go-1.12.0/Dockerfile
password:
from_secret: docker_password
repo: techknowlogick/xgo
tags: go-1.12.0
username:
from_secret: docker_username
when:
branch:
- master
event:
exclude:
- pull_request
- name: build-go-1.11.8
pull: always
image: plugins/docker
settings:
context: docker/go-1.11.8
dockerfile: docker/go-1.11.8/Dockerfile
password:
from_secret: docker_password
repo: techknowlogick/xgo
tags: go-1.11.8
username:
from_secret: docker_username
when:
branch:
- master
event:
exclude:
- pull_request
- name: build-go-1.11.7
pull: always
image: plugins/docker
settings:
context: docker/go-1.11.7
dockerfile: docker/go-1.11.7/Dockerfile
password:
from_secret: docker_password
repo: techknowlogick/xgo
tags: go-1.11.7
username:
from_secret: docker_username
when:
branch:
- master
event:
exclude:
- pull_request
...

21
vendor/src.techknowlogick.com/xgo/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2014 Péter Szilágyi <peterke@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

253
vendor/src.techknowlogick.com/xgo/README.md generated vendored Normal file
View File

@ -0,0 +1,253 @@
# xgo - Go CGO cross compiler
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
bootstrapped to each platform and architecture.
The first step towards cross compiling was Dave Cheney's [golang-crosscompile](https://github.com/davecheney/golang-crosscompile)
package, which automatically bootstrapped the necessary sources based on your
existing Go installation. Although this was enough for a lot of cases, certain
drawbacks became apparent where the official libraries used CGO internally: any
dependency to third party platform code is unavailable, hence those parts don't
cross compile nicely (native DNS resolution, system certificate access, etc).
A step forward in enabling cross compilation was Alan Shreve's [gonative](https://github.com/inconshreveable/gonative)
package, which instead of bootstrapping the different platforms based on the
existing Go installation, downloaded the official pre-compiled binaries from the
golang website and injected those into the local toolchain. Since the pre-built
binaries already contained the necessary platform specific code, the few missing
dependencies were resolved, and true cross compilation could commence... of pure
Go code.
However, there was still one feature missing: cross compiling Go code that used
CGO itself, which isn't trivial since you need access to OS specific headers and
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
maintain separate build environments to do it.
## Enter xgo
My solution to the challenge of cross compiling Go code with embedded C/C++ snippets
(i.e. CGO_ENABLED=1) is based on the concept of [lightweight Linux containers](http://en.wikipedia.org/wiki/LXC).
All the necessary Go tool-chains, C cross compilers and platform headers/libraries
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.
## Installation
Although you could build the container manually, it is available as an automatic
trusted build from Docker's container registry (not insignificant in size):
docker pull techknowlogick/xgo:latest
To prevent having to remember a potentially complex Docker command every time,
a lightweight Go wrapper was written on top of it.
go get src.techknowlogick.com/xgo
## Usage
Simply specify the import path you want to build, and xgo will do the rest:
$ xgo github.com/project-iris/iris
...
$ ls -al
-rwxr-xr-x 1 root root 9995000 Nov 24 16:44 iris-android-16-arm
-rwxr-xr-x 1 root root 6776500 Nov 24 16:44 iris-darwin-10.6-386
-rwxr-xr-x 1 root root 8755532 Nov 24 16:44 iris-darwin-10.6-amd64
-rwxr-xr-x 1 root root 7114176 Nov 24 16:45 iris-ios-5.0-arm
-rwxr-xr-x 1 root root 10135248 Nov 24 16:44 iris-linux-386
-rwxr-xr-x 1 root root 12598472 Nov 24 16:44 iris-linux-amd64
-rwxr-xr-x 1 root root 10040464 Nov 24 16:44 iris-linux-arm
-rwxr-xr-x 1 root root 7516368 Nov 24 16:44 iris-windows-4.0-386.exe
-rwxr-xr-x 1 root root 9549416 Nov 24 16:44 iris-windows-4.0-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
A handful of flags can be passed to `go build`. The currently supported ones are
- `-v`: prints the names of packages as they are compiled
- `-x`: prints the build commands as compilation progresses
- `-race`: enables data race detection (supported only on amd64, rest built without)
- `-tags='tag list'`: list of build tags to consider satisfied during the build
- `-ldflags='flag list'`: arguments to pass on each go tool link invocation
- `-buildmode=mode`: binary type to produce by the compiler
### 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.6.1 github.com/project-iris/iris
Additionally, a few wildcard release strings are also supported:
- `latest` will use the latest Go release (this is the default)
- `1.6.x` will use the latest point release of a specific Go version
- `1.6-develop` will use the develop branch of a specific Go version
- `develop` will use the develop branch of the entire Go repository
### Output prefixing
xgo by default uses the name of the package being cross compiled as the output
file prefix. This can be overridden with the `-out` flag.
$ xgo -out iris-v0.3.2 github.com/project-iris/iris
...
$ ls -al
-rwxr-xr-x 1 root root 9995000 Nov 24 16:44 iris-v0.3.2-android-16-arm
-rwxr-xr-x 1 root root 6776500 Nov 24 16:44 iris-v0.3.2-darwin-10.6-386
-rwxr-xr-x 1 root root 8755532 Nov 24 16:44 iris-v0.3.2-darwin-10.6-amd64
-rwxr-xr-x 1 root root 7114176 Nov 24 16:45 iris-v0.3.2-ios-5.0-arm
-rwxr-xr-x 1 root root 10135248 Nov 24 16:44 iris-v0.3.2-linux-386
-rwxr-xr-x 1 root root 12598472 Nov 24 16:44 iris-v0.3.2-linux-amd64
-rwxr-xr-x 1 root root 10040464 Nov 24 16:44 iris-v0.3.2-linux-arm
-rwxr-xr-x 1 root root 7516368 Nov 24 16:44 iris-v0.3.2-windows-4.0-386.exe
-rwxr-xr-x 1 root root 9549416 Nov 24 16:44 iris-v0.3.2-windows-4.0-amd64.exe
### Branch selection
Similarly to `go get`, xgo also uses the `master` branch of a repository during
source code retrieval. To switch to a different branch before compilation pass
the desired branch name through the `--branch` argument.
$ xgo --branch release-branch.go1.4 golang.org/x/tools/cmd/goimports
...
$ ls -al
-rwxr-xr-x 1 root root 4171248 Nov 24 16:40 goimports-android-16-arm
-rwxr-xr-x 1 root root 4139868 Nov 24 16:40 goimports-darwin-10.6-386
-rwxr-xr-x 1 root root 5186720 Nov 24 16:40 goimports-darwin-10.6-amd64
-rwxr-xr-x 1 root root 3202364 Nov 24 16:40 goimports-ios-5.0-arm
-rwxr-xr-x 1 root root 4189456 Nov 24 16:40 goimports-linux-386
-rwxr-xr-x 1 root root 5264136 Nov 24 16:40 goimports-linux-amd64
-rwxr-xr-x 1 root root 4209416 Nov 24 16:40 goimports-linux-arm
-rwxr-xr-x 1 root root 4348416 Nov 24 16:40 goimports-windows-4.0-386.exe
-rwxr-xr-x 1 root root 5415424 Nov 24 16:40 goimports-windows-4.0-amd64.exe
### Remote selection
Yet again similarly to `go get`, xgo uses the repository remote corresponding to
the import path being built. To switch to a different remote while preserving the
original import path, use the `--remote` argument.
$ xgo --remote github.com/golang/tools golang.org/x/tools/cmd/goimports
...
### Package selection
If you used the above *branch* or *remote* selection machanisms, it may happen
that the path you are trying to build is only present in the specific branch and
not the default repository, causing Go to fail at locating it. To circumvent this,
you may specify only the repository root for xgo, and use an additional `--pkg`
parameter to select the exact package within, honoring any prior *branch* and
*remote* selections.
$ xgo --pkg cmd/goimports golang.org/x/tools
...
$ ls -al
-rwxr-xr-x 1 root root 4194956 Nov 24 16:38 goimports-android-16-arm
-rwxr-xr-x 1 root root 4164448 Nov 24 16:38 goimports-darwin-10.6-386
-rwxr-xr-x 1 root root 5223584 Nov 24 16:38 goimports-darwin-10.6-amd64
-rwxr-xr-x 1 root root 3222848 Nov 24 16:39 goimports-ios-5.0-arm
-rwxr-xr-x 1 root root 4217184 Nov 24 16:38 goimports-linux-386
-rwxr-xr-x 1 root root 5295768 Nov 24 16:38 goimports-linux-amd64
-rwxr-xr-x 1 root root 4233120 Nov 24 16:38 goimports-linux-arm
-rwxr-xr-x 1 root root 4373504 Nov 24 16:38 goimports-windows-4.0-386.exe
-rwxr-xr-x 1 root root 5450240 Nov 24 16:38 goimports-windows-4.0-amd64.exe
This argument may at some point be integrated into the import path itself, but for
now it exists as an independent build parameter. Also, there is not possibility
for now to build mulitple commands in one go.
### Limit build targets
By default `xgo` will try and build the specified package to all platforms and
architectures supported by the underlying Go runtime. If you wish to restrict
the build to only a few target systems, use the comma separated `--targets` CLI
argument:
* `--targets=linux/arm`: builds only the ARMv5 Linux binaries (`arm-6`/`arm-7` allowed)
* `--targets=windows/*,darwin/*`: builds all Windows and OSX binaries
* `--targets=*/arm`: builds ARM binaries for all platforms
* `--targets=*/*`: builds all suppoted targets (default)
The supported targets are:
* Platforms: `android`, `darwin`, `ios`, `linux`, `windows`
* Achitectures: `386`, `amd64`, `arm-5`, `arm-6`, `arm-7`, `arm64`, `mips`, `mipsle`, `mips64`, `mips64le`
### Platform versions
By default `xgo` tries to cross compile to the lowest possible versions of every
supported platform, in order to produce binaries that are portable among various
versions of the same operating system. This however can lead to issues if a used
dependency is only supported by more recent systems. As such, `xgo` supports the
selection of specific platform versions by appending them to the OS target string.
* `--targets=darwin-10.9/*`: cross compile to Mac OS X Mavericks
* `--targets=windows-6.0/*`: cross compile to Windows Vista
The supported platforms are:
* All Android APIs up to Android Lollipop 5.0 ([API level ids](https://source.android.com/source/build-numbers.html))
* All Windows APIs up to Windows 8.1 limited by `mingw-w64` ([API level ids](https://en.wikipedia.org/wiki/Windows_NT#Releases))
* OSX APIs in the range of 10.6 - 10.14
### CGO dependencies
The main differentiator of xgo versus other cross compilers is support for basic
embedded C/C++ code and target-platform specific OS SDK availability. The current
xgo release introduces an experimental CGO *dependency* cross compilation, enabling
building Go programs that require external C/C++ libraries.
It is assumed that the dependent C/C++ library is `configure/make` based, was
properly prepared for cross compilation and is available as a tarball download
(`.tar`, `.tar.gz` or `.tar.bz2`). Further plans include extending this to cmake
based projects, if need arises (please open an issue if it's important to you).
Such dependencies can be added via the `--deps` argument. They will be retrieved
prior to starting the cross compilation and the packages cached to save bandwidth
on subsequent calls.
A complex sample for such a scenario is building the Ethereum CLI node, which has
the GNU Multiple Precision Arithmetic Library as it's dependency.
$ xgo --deps=https://gmplib.org/download/gmp/gmp-6.1.0.tar.bz2 \
--targets=windows/* github.com/ethereum/go-ethereum/cmd/geth
...
$ ls -al
-rwxr-xr-x 1 root root 16315679 Nov 24 16:39 geth-windows-4.0-386.exe
-rwxr-xr-x 1 root root 19452036 Nov 24 16:38 geth-windows-4.0-amd64.exe
Some trivial arguments may be passed to the dependencies' configure script via
`--depsargs`.
$ xgo --deps=https://gmplib.org/download/gmp/gmp-6.1.0.tar.bz2 \
--targets=ios/* --depsargs=--disable-assembly \
github.com/ethereum/go-ethereum/cmd/geth
...
$ ls -al
-rwxr-xr-x 1 root root 14804160 Nov 24 16:32 geth-ios-5.0-arm
Note, that since xgo needs to cross compile the dependencies for each platform
and architecture separately, build time can increase significantly.

84
vendor/src.techknowlogick.com/xgo/testsuite.go generated vendored Normal file
View File

@ -0,0 +1,84 @@
// Go CGO cross compiler
// Copyright (c) 2016 Péter Szilágyi. All rights reserved.
//
// Released under the MIT license.
// This is a manual test suite to run the cross compiler against various known
// projects, codebases and repositories to ensure at least a baseline guarantee
// that things work as they supposed to.
//
// Run as: go run testsuite.go
// +build ignore
package main
import (
"log"
"os"
"os/exec"
"path/filepath"
)
// layers defines all the docker layers needed for the final xgo image. The last
// one will be used to run the test suite against.
var layers = []struct {
tag string
dir string
}{
{"techknowlogick/xgo:base", "base"},
{"techknowlogick/xgo:1.12.4", "go-1.12.4"},
{"techknowlogick/xgo:1.12.x", "go-1.12.x"},
{"techknowlogick/xgo:latest", "go-latest"},
}
// tests defaines all the input test cases and associated arguments the cross
// compiler should be ran for and with which arguments.
var tests = []struct {
path string
args []string
}{
// Tiny test cases to smoke test cross compilations
{"github.com/karalabe/xgo/tests/embedded_c", nil},
{"github.com/karalabe/xgo/tests/embedded_cpp", nil},
// Baseline projects to ensure minimal requirements
{"github.com/ethereum/go-ethereum/cmd/geth", []string{"--branch", "develop"}},
// Third party projects using xgo, smoke test that they don't break
{"github.com/rwcarlsen/cyan/cmd/cyan", nil},
{"github.com/cockroachdb/cockroach", []string{"--targets", "darwin-10.6/amd64"}},
}
func main() {
// Retrieve the current working directory to locate the dockerfiles
pwd, err := os.Getwd()
if err != nil {
log.Fatalf("Failed to retrieve local working directory: %v", err)
}
if _, err := os.Stat(filepath.Join(pwd, "docker", "base")); err != nil {
log.Fatalf("Failed to locate docker image: %v", err)
}
// Assemble the multi-layered xgo docker image
for _, layer := range layers {
cmd := exec.Command("docker", "build", "--tag", layer.tag, filepath.Join(pwd, "docker", layer.dir))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Fatalf("Failed to build xgo layer: %v", err)
}
}
// Iterate over each of the test cases and run them
for i, test := range tests {
cmd := exec.Command("docker", append([]string{"run", "--entrypoint", "xgo", layers[len(layers)-1].tag, "-v"}, append(test.args, test.path)...)...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Fatalf("Test #%d: cross compilation failed: %v", i, err)
}
}
}

404
vendor/src.techknowlogick.com/xgo/xgo.go generated vendored Normal file
View File

@ -0,0 +1,404 @@
// Go CGO cross compiler
// Copyright (c) 2014 Péter Szilágyi. All rights reserved.
//
// Released under the MIT license.
// Wrapper around the GCO cross compiler docker container.
package main // import "src.techknowlogick.com/xgo"
import (
"bytes"
"flag"
"fmt"
"go/build"
"io"
"log"
"net/http"
"os"
"os/exec"
"os/user"
"path/filepath"
"strconv"
"strings"
)
// Path where to cache external dependencies
var depsCache string
func init() {
// Initialize the external dependency cache path to a few possible locations
if home := os.Getenv("HOME"); home != "" {
depsCache = filepath.Join(home, ".xgo-cache")
return
}
if user, err := user.Current(); user != nil && err == nil && user.HomeDir != "" {
depsCache = filepath.Join(user.HomeDir, ".xgo-cache")
return
}
depsCache = filepath.Join(os.TempDir(), "xgo-cache")
}
// Cross compilation docker containers
var dockerBase = "techknowlogick/xgo:base"
var dockerDist = "techknowlogick/xgo:"
// Command line arguments to fine tune the compilation
var (
goVersion = flag.String("go", "latest", "Go release to use for cross compilation")
srcPackage = flag.String("pkg", "", "Sub-package to build if not root import")
srcRemote = flag.String("remote", "", "Version control remote repository to build")
srcBranch = flag.String("branch", "", "Version control branch to build")
outPrefix = flag.String("out", "", "Prefix to use for output naming (empty = package name)")
outFolder = flag.String("dest", "", "Destination folder to put binaries in (empty = current)")
crossDeps = flag.String("deps", "", "CGO dependencies (configure/make based archives)")
crossArgs = flag.String("depsargs", "", "CGO dependency configure arguments")
targets = flag.String("targets", "*/*", "Comma separated targets to build for")
dockerImage = flag.String("image", "", "Use custom docker image instead of official distribution")
)
// ConfigFlags is a simple set of flags to define the environment and dependencies.
type ConfigFlags struct {
Repository string // Root import path to build
Package string // Sub-package to build if not root import
Prefix string // Prefix to use for output naming
Remote string // Version control remote repository to build
Branch string // Version control branch to build
Dependencies string // CGO dependencies (configure/make based archives)
Arguments string // CGO dependency configure arguments
Targets []string // Targets to build for
}
// Command line arguments to pass to go build
var (
buildVerbose = flag.Bool("v", false, "Print the names of packages as they are compiled")
buildSteps = flag.Bool("x", false, "Print the command as executing the builds")
buildRace = flag.Bool("race", false, "Enable data race detection (supported only on amd64)")
buildTags = flag.String("tags", "", "List of build tags to consider satisfied during the build")
buildLdFlags = flag.String("ldflags", "", "Arguments to pass on each go tool link invocation")
buildMode = flag.String("buildmode", "default", "Indicates which kind of object file to build")
)
// BuildFlags is a simple collection of flags to fine tune a build.
type BuildFlags struct {
Verbose bool // Print the names of packages as they are compiled
Steps bool // Print the command as executing the builds
Race bool // Enable data race detection (supported only on amd64)
Tags string // List of build tags to consider satisfied during the build
LdFlags string // Arguments to pass on each go tool link invocation
Mode string // Indicates which kind of object file to build
}
func main() {
// Retrieve the CLI flags and the execution environment
flag.Parse()
xgoInXgo := os.Getenv("XGO_IN_XGO") == "1"
if xgoInXgo {
depsCache = "/deps-cache"
}
// Only use docker images if we're not already inside out own image
image := ""
if !xgoInXgo {
// Ensure docker is available
if err := checkDocker(); err != nil {
log.Fatalf("Failed to check docker installation: %v.", err)
}
// Validate the command line arguments
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 != "" {
image = *dockerImage
}
// Check that all required images are available
found, err := checkDockerImage(image)
switch {
case err != nil:
log.Fatalf("Failed to check docker image availability: %v.", err)
case !found:
fmt.Println("not found!")
if err := pullDockerImage(image); err != nil {
log.Fatalf("Failed to pull docker image from the registry: %v.", err)
}
default:
fmt.Println("found.")
}
}
// Cache all external dependencies to prevent always hitting the internet
if *crossDeps != "" {
if err := os.MkdirAll(depsCache, 0751); err != nil {
log.Fatalf("Failed to create dependency cache: %v.", err)
}
// Download all missing dependencies
for _, dep := range strings.Split(*crossDeps, " ") {
if url := strings.TrimSpace(dep); len(url) > 0 {
path := filepath.Join(depsCache, filepath.Base(url))
if _, err := os.Stat(path); err != nil {
fmt.Printf("Downloading new dependency: %s...\n", url)
out, err := os.Create(path)
if err != nil {
log.Fatalf("Failed to create dependency file: %v.", err)
}
res, err := http.Get(url)
if err != nil {
log.Fatalf("Failed to retrieve dependency: %v.", err)
}
defer res.Body.Close()
if _, err := io.Copy(out, res.Body); err != nil {
log.Fatalf("Failed to download dependency: %v", err)
}
out.Close()
fmt.Printf("New dependency cached: %s.\n", path)
} else {
fmt.Printf("Dependency already cached: %s.\n", path)
}
}
}
}
// Assemble the cross compilation environment and build options
config := &ConfigFlags{
Repository: flag.Args()[0],
Package: *srcPackage,
Remote: *srcRemote,
Branch: *srcBranch,
Prefix: *outPrefix,
Dependencies: *crossDeps,
Arguments: *crossArgs,
Targets: strings.Split(*targets, ","),
}
flags := &BuildFlags{
Verbose: *buildVerbose,
Steps: *buildSteps,
Race: *buildRace,
Tags: *buildTags,
LdFlags: *buildLdFlags,
Mode: *buildMode,
}
folder, err := os.Getwd()
if err != nil {
log.Fatalf("Failed to retrieve the working directory: %v.", err)
}
if *outFolder != "" {
folder, err = filepath.Abs(*outFolder)
if err != nil {
log.Fatalf("Failed to resolve destination path (%s): %v.", *outFolder, err)
}
}
// Execute the cross compilation, either in a container or the current system
if !xgoInXgo {
err = compile(image, config, flags, folder)
} else {
err = compileContained(config, flags, folder)
}
if 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)
return run(exec.Command("docker", "pull", image))
}
// compile cross builds a requested package according to the given build specs
// using a specific docker cross compilation image.
func compile(image string, config *ConfigFlags, flags *BuildFlags, folder string) error {
// If a local build was requested, find the import path and mount all GOPATH sources
locals, mounts, paths := []string{}, []string{}, []string{}
var usesModules bool
if strings.HasPrefix(config.Repository, string(filepath.Separator)) || strings.HasPrefix(config.Repository, ".") {
// Resolve the repository import path from the file path
config.Repository = resolveImportPath(config.Repository)
// Determine if this is a module-based repository
var modFile = config.Repository + "/go.mod"
_, err := os.Stat(modFile)
usesModules = !os.IsNotExist(err)
// Iterate over all the local libs and export the mount points
if os.Getenv("GOPATH") == "" && !usesModules {
log.Fatalf("No $GOPATH is set or forwarded to xgo")
}
if !usesModules {
for _, gopath := range strings.Split(os.Getenv("GOPATH"), string(os.PathListSeparator)) {
// Since docker sandboxes volumes, resolve any symlinks manually
sources := filepath.Join(gopath, "src")
filepath.Walk(sources, func(path string, info os.FileInfo, err error) error {
// Skip any folders that errored out
if err != nil {
log.Printf("Failed to access GOPATH element %s: %v", path, err)
return nil
}
// Skip anything that's not a symlink
if info.Mode()&os.ModeSymlink == 0 {
return nil
}
// Resolve the symlink and skip if it's not a folder
target, err := filepath.EvalSymlinks(path)
if err != nil {
return nil
}
if info, err = os.Stat(target); err != nil || !info.IsDir() {
return nil
}
// Skip if the symlink points within GOPATH
if filepath.HasPrefix(target, sources) {
return nil
}
// Folder needs explicit mounting due to docker symlink security
locals = append(locals, target)
mounts = append(mounts, filepath.Join("/ext-go", strconv.Itoa(len(locals)), "src", strings.TrimPrefix(path, sources)))
paths = append(paths, filepath.Join("/ext-go", strconv.Itoa(len(locals))))
return nil
})
// Export the main mount point for this GOPATH entry
locals = append(locals, sources)
mounts = append(mounts, filepath.Join("/ext-go", strconv.Itoa(len(locals)), "src"))
paths = append(paths, filepath.Join("/ext-go", strconv.Itoa(len(locals))))
}
}
}
// Assemble and run the cross compilation command
fmt.Printf("Cross compiling %s...\n", config.Repository)
args := []string{
"run", "--rm",
"-v", folder + ":/build",
"-v", depsCache + ":/deps-cache:ro",
"-e", "REPO_REMOTE=" + config.Remote,
"-e", "REPO_BRANCH=" + config.Branch,
"-e", "PACK=" + config.Package,
"-e", "DEPS=" + config.Dependencies,
"-e", "ARGS=" + config.Arguments,
"-e", "OUT=" + config.Prefix,
"-e", fmt.Sprintf("FLAG_V=%v", flags.Verbose),
"-e", fmt.Sprintf("FLAG_X=%v", flags.Steps),
"-e", fmt.Sprintf("FLAG_RACE=%v", flags.Race),
"-e", fmt.Sprintf("FLAG_TAGS=%s", flags.Tags),
"-e", fmt.Sprintf("FLAG_LDFLAGS=%s", flags.LdFlags),
"-e", fmt.Sprintf("FLAG_BUILDMODE=%s", flags.Mode),
"-e", "TARGETS=" + strings.Replace(strings.Join(config.Targets, " "), "*", ".", -1),
}
if usesModules {
args = append(args, []string{"-e", "GO111MODULE=on"}...)
args = append(args, []string{"-v", os.Getenv("GOPATH") + ":/go"}...)
// Map this repository to the /source folder
absRepository, err := filepath.Abs(config.Repository)
if err != nil {
log.Fatalf("Failed to locate requested module repository: %v.", err)
}
args = append(args, []string{"-v", absRepository + ":/source"}...)
fmt.Printf("Enabled Go module support\n")
// Check whether it has a vendor folder, and if so, use it
vendorPath := absRepository + "/vendor"
vendorfolder, err := os.Stat(vendorPath)
if !os.IsNotExist(err) && vendorfolder.Mode().IsDir() {
args = append(args, []string{"-e", "FLAG_MOD=vendor"}...)
fmt.Printf("Using vendored Go module dependencies\n")
}
} else {
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, config.Repository}...)
return run(exec.Command("docker", args...))
}
// compileContained cross builds a requested package according to the given build
// specs using the current system opposed to running in a container. This is meant
// to be used for cross compilation already from within an xgo image, allowing the
// inheritance and bundling of the root xgo images.
func compileContained(config *ConfigFlags, flags *BuildFlags, folder string) error {
// If a local build was requested, resolve the import path
local := strings.HasPrefix(config.Repository, string(filepath.Separator)) || strings.HasPrefix(config.Repository, ".")
if local {
config.Repository = resolveImportPath(config.Repository)
}
// Fine tune the original environment variables with those required by the build script
env := []string{
"REPO_REMOTE=" + config.Remote,
"REPO_BRANCH=" + config.Branch,
"PACK=" + config.Package,
"DEPS=" + config.Dependencies,
"ARGS=" + config.Arguments,
"OUT=" + config.Prefix,
fmt.Sprintf("FLAG_V=%v", flags.Verbose),
fmt.Sprintf("FLAG_X=%v", flags.Steps),
fmt.Sprintf("FLAG_RACE=%v", flags.Race),
fmt.Sprintf("FLAG_TAGS=%s", flags.Tags),
fmt.Sprintf("FLAG_LDFLAGS=%s", flags.LdFlags),
fmt.Sprintf("FLAG_BUILDMODE=%s", flags.Mode),
"TARGETS=" + strings.Replace(strings.Join(config.Targets, " "), "*", ".", -1),
}
if local {
env = append(env, "EXT_GOPATH=/non-existent-path-to-signal-local-build")
}
// Assemble and run the local cross compilation command
fmt.Printf("Cross compiling %s...\n", config.Repository)
cmd := exec.Command("/build.sh", config.Repository)
cmd.Env = append(os.Environ(), env...)
return run(cmd)
}
// resolveImportPath converts a package given by a relative path to a Go import
// path using the local GOPATH environment.
func resolveImportPath(path string) string {
abs, err := filepath.Abs(path)
if err != nil {
log.Fatalf("Failed to locate requested package: %v.", err)
}
stat, err := os.Stat(abs)
if err != nil || !stat.IsDir() {
log.Fatalf("Requested path invalid.")
}
pack, err := build.ImportDir(abs, build.FindOnly)
if err != nil {
log.Fatalf("Failed to resolve import path: %v.", err)
}
return pack.ImportPath
}
// Executes a command synchronously, redirecting its output to stdout.
func run(cmd *exec.Cmd) error {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}