File: toolchain.go

package info (click to toggle)
gox 0.3.0-3
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, buster, sid
  • size: 140 kB
  • sloc: makefile: 6
file content (131 lines) | stat: -rw-r--r-- 3,388 bytes parent folder | download | duplicates (2)
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
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
	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

	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': %s",
			platform.String(), stderr.String())
	}

	return nil
}