149 lines
3.8 KiB
Go
149 lines
3.8 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"os"
|
||
|
"os/exec"
|
||
|
"path/filepath"
|
||
|
"runtime"
|
||
|
"sync"
|
||
|
|
||
|
"github.com/mitchellh/iochan"
|
||
|
)
|
||
|
|
||
|
// 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
|
||
|
}
|
||
|
|
||
|
// If we're version 1.5 or greater, then we don't need to do this anymore!
|
||
|
versionParts, err := GoVersionParts()
|
||
|
if err != nil {
|
||
|
fmt.Fprintf(os.Stderr, "error reading Go version: %s", err)
|
||
|
return 1
|
||
|
}
|
||
|
if versionParts[0] >= 1 && versionParts[1] >= 5 {
|
||
|
fmt.Fprintf(
|
||
|
os.Stderr,
|
||
|
"-build-toolchain is no longer required for Go 1.5 or later.\n"+
|
||
|
"You can start using Gox immediately!\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
|
||
|
}
|