forked from tsg/xgo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
xgo.go
161 lines (144 loc) · 5.08 KB
/
xgo.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// 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 (
"bytes"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
)
// Cross compilation docker containers
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 dockerImage = flag.String("image", "", "Use a custom docker image")
var beforeBuildScript = flag.String("before-build", "", "Script to run before the build step")
var source = flag.String("source", "", "Copy from the source directory instead of cloning")
// Command line arguments to pass to go build
var buildVerbose = flag.Bool("v", false, "Print the names of packages as they are compiled")
var buildRace = flag.Bool("race", false, "Enable data race detection (supported only on amd64)")
func main() {
flag.Parse()
// 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])
}
image := dockerDist + *goVersion
if dockerImage != nil {
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.")
}
// Cross compile the requested package into the local folder
if err := compile(flag.Args()[0], image, *srcRemote, *srcBranch, *inPackage, *crossDeps, *outPrefix, *buildVerbose, *buildRace, *beforeBuildScript, *source); 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))
}
// 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, race bool, beforeBuild string, source string) error {
folder, err := os.Getwd()
if err != nil {
log.Fatalf("Failed to retrieve the working directory: %v.", err)
}
tmpDir, err := ioutil.TempDir(folder, "")
if err != nil {
log.Fatalf("Failed to create temporary directory: %v.", err)
}
defer func() {
err := os.RemoveAll(tmpDir)
if err != nil {
log.Fatalf("Error removing temporary directory: %v.", err)
}
}()
if len(beforeBuild) > 0 {
err = os.Link(beforeBuild, filepath.Join(tmpDir, filepath.Base(beforeBuild)))
if err != nil {
log.Fatalf("Error linking script to temporary dir: %v.", err)
}
}
fmt.Printf("Cross compiling %s...\n", repo)
args := []string{
"run",
"-v", folder + ":/build",
"-v", tmpDir + ":/scripts",
"-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_RACE=%v", race),
"-e", fmt.Sprintf("BEFORE_BUILD=%v", filepath.Base(beforeBuild)),
}
if len(source) > 0 {
sourceDir, err := filepath.Abs(source)
if err != nil {
log.Fatalf("Error getting absolute path for: %s", source)
}
args = append(args, "-v")
args = append(args, sourceDir+":/source")
args = append(args, "-e")
args = append(args, "SOURCE=/source")
}
args = append(args, image)
args = append(args, repo)
fmt.Printf("Running docker %v\n", args)
return run(exec.Command("docker", args...))
}
// 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()
}