forked from mitchellh/gox
-
Notifications
You must be signed in to change notification settings - Fork 1
/
toolchain.go
133 lines (115 loc) · 3.4 KB
/
toolchain.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
package main
import (
"bytes"
"fmt"
"github.com/mitchellh/iochan"
"io"
"os"
"os/exec"
"path/filepath"
"runtime"
"sync"
)
// The "main" method for when the toolchain build is requested.
func mainBuildToolchain(parallel int, platformFlag PlatformFlag, verbose bool) int {
if _, err := exec.LookPath("go"); err != nil {
fmt.Fprintf(os.Stderr, "You must have Go already built for your native platform\n")
fmt.Fprintf(os.Stderr, "and the `go` binary on the PATH to build toolchains.\n")
return 1
}
version, err := GoVersion()
if err != nil {
fmt.Fprintf(os.Stderr, "error reading Go version: %s", err)
return 1
}
root, err := GoRoot()
if err != nil {
fmt.Fprintf(os.Stderr, "error finding GOROOT: %s\n", err)
return 1
}
if verbose {
fmt.Println("Verbose mode enabled. Output from building each toolchain will be")
fmt.Println("outputted to stdout as they are built.\n")
}
// Determine the platforms we're building the toolchain for.
platforms := platformFlag.Platforms(SupportedPlatforms(version))
// The toolchain build can't be parallelized.
if parallel > 1 {
fmt.Println("The toolchain build can't be parallelized because compiling a single")
fmt.Println("Go source directory can only be done for one platform at a time. Therefore,")
fmt.Println("the toolchain for each platform will be built one at a time.\n")
}
parallel = 1
var errorLock sync.Mutex
var wg sync.WaitGroup
errs := make([]error, 0)
semaphore := make(chan int, parallel)
for _, platform := range platforms {
wg.Add(1)
go func(platform Platform) {
err := buildToolchain(&wg, semaphore, root, platform, verbose)
if err != nil {
errorLock.Lock()
defer errorLock.Unlock()
errs = append(errs, fmt.Errorf("%s: %s", platform.String(), err))
}
}(platform)
}
wg.Wait()
if len(errs) > 0 {
fmt.Fprintf(os.Stderr, "\n%d errors occurred:\n", len(errs))
for _, err := range errs {
fmt.Fprintf(os.Stderr, "%s\n", err)
}
return 1
}
return 0
}
func buildToolchain(wg *sync.WaitGroup, semaphore chan int, root string, platform Platform, verbose bool) error {
defer wg.Done()
semaphore <- 1
defer func() { <-semaphore }()
fmt.Printf("--> Toolchain: %s\n", platform.String())
scriptName := "make.bash"
if runtime.GOOS == "windows" {
scriptName = "make.bat"
}
var stderr bytes.Buffer
var stdout bytes.Buffer
scriptDir := filepath.Join(root, "src")
scriptPath := filepath.Join(scriptDir, scriptName)
cmd := exec.Command(scriptPath, "--no-clean")
cmd.Dir = scriptDir
cmd.Env = append(os.Environ(),
"GOARCH="+platform.Arch,
"GOOS="+platform.OS)
cmd.Stderr = &stderr
cmd.Stdout = &stdout
if verbose {
// In verbose mode, we output all stdout to the console.
r, w := io.Pipe()
cmd.Stdout = w
cmd.Stderr = io.MultiWriter(cmd.Stderr, w)
// Send all the output to stdout, and also make a done channel
// so that this compilation isn't done until we receive all output
doneCh := make(chan struct{})
go func() {
defer close(doneCh)
for line := range iochan.DelimReader(r, '\n') {
fmt.Printf("%s: %s", platform.String(), line)
}
}()
defer func() {
w.Close()
<-doneCh
}()
}
if err := cmd.Start(); err != nil {
return fmt.Errorf("Error building '%s': %s", platform.String(), err)
}
if err := cmd.Wait(); err != nil {
return fmt.Errorf("Error building '%s'.\n\nStdout: %s\n\nStderr: %s\n",
platform.String(), stdout.String(), stderr.String())
}
return nil
}