1// Copyright 2012 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package main
6
7import (
8	"bytes"
9	"flag"
10	"fmt"
11	"io"
12	"os"
13	"os/exec"
14	"path/filepath"
15	"sort"
16	"strconv"
17	"strings"
18	"sync"
19	"time"
20)
21
22// pathf is fmt.Sprintf for generating paths
23// (on windows it turns / into \ after the printf).
24func pathf(format string, args ...interface{}) string {
25	return filepath.Clean(fmt.Sprintf(format, args...))
26}
27
28// filter returns a slice containing the elements x from list for which f(x) == true.
29func filter(list []string, f func(string) bool) []string {
30	var out []string
31	for _, x := range list {
32		if f(x) {
33			out = append(out, x)
34		}
35	}
36	return out
37}
38
39// uniq returns a sorted slice containing the unique elements of list.
40func uniq(list []string) []string {
41	out := make([]string, len(list))
42	copy(out, list)
43	sort.Strings(out)
44	keep := out[:0]
45	for _, x := range out {
46		if len(keep) == 0 || keep[len(keep)-1] != x {
47			keep = append(keep, x)
48		}
49	}
50	return keep
51}
52
53const (
54	CheckExit = 1 << iota
55	ShowOutput
56	Background
57)
58
59var outputLock sync.Mutex
60
61// run is like runEnv with no additional environment.
62func run(dir string, mode int, cmd ...string) string {
63	return runEnv(dir, mode, nil, cmd...)
64}
65
66// runEnv runs the command line cmd in dir with additional environment env.
67// If mode has ShowOutput set and Background unset, run passes cmd's output to
68// stdout/stderr directly. Otherwise, run returns cmd's output as a string.
69// If mode has CheckExit set and the command fails, run calls fatalf.
70// If mode has Background set, this command is being run as a
71// Background job. Only bgrun should use the Background mode,
72// not other callers.
73func runEnv(dir string, mode int, env []string, cmd ...string) string {
74	if vflag > 1 {
75		errprintf("run: %s\n", strings.Join(cmd, " "))
76	}
77
78	xcmd := exec.Command(cmd[0], cmd[1:]...)
79	if env != nil {
80		xcmd.Env = append(os.Environ(), env...)
81	}
82	setDir(xcmd, dir)
83	var data []byte
84	var err error
85
86	// If we want to show command output and this is not
87	// a background command, assume it's the only thing
88	// running, so we can just let it write directly stdout/stderr
89	// as it runs without fear of mixing the output with some
90	// other command's output. Not buffering lets the output
91	// appear as it is printed instead of once the command exits.
92	// This is most important for the invocation of 'go build -v bootstrap/...'.
93	if mode&(Background|ShowOutput) == ShowOutput {
94		xcmd.Stdout = os.Stdout
95		xcmd.Stderr = os.Stderr
96		err = xcmd.Run()
97	} else {
98		data, err = xcmd.CombinedOutput()
99	}
100	if err != nil && mode&CheckExit != 0 {
101		outputLock.Lock()
102		if len(data) > 0 {
103			xprintf("%s\n", data)
104		}
105		outputLock.Unlock()
106		if mode&Background != 0 {
107			// Prevent fatalf from waiting on our own goroutine's
108			// bghelper to exit:
109			bghelpers.Done()
110		}
111		fatalf("FAILED: %v: %v", strings.Join(cmd, " "), err)
112	}
113	if mode&ShowOutput != 0 {
114		outputLock.Lock()
115		os.Stdout.Write(data)
116		outputLock.Unlock()
117	}
118	if vflag > 2 {
119		errprintf("run: %s DONE\n", strings.Join(cmd, " "))
120	}
121	return string(data)
122}
123
124var maxbg = 4 /* maximum number of jobs to run at once */
125
126var (
127	bgwork = make(chan func(), 1e5)
128
129	bghelpers sync.WaitGroup
130
131	dieOnce sync.Once // guards close of dying
132	dying   = make(chan struct{})
133)
134
135func bginit() {
136	bghelpers.Add(maxbg)
137	for i := 0; i < maxbg; i++ {
138		go bghelper()
139	}
140}
141
142func bghelper() {
143	defer bghelpers.Done()
144	for {
145		select {
146		case <-dying:
147			return
148		case w := <-bgwork:
149			// Dying takes precedence over doing more work.
150			select {
151			case <-dying:
152				return
153			default:
154				w()
155			}
156		}
157	}
158}
159
160// bgrun is like run but runs the command in the background.
161// CheckExit|ShowOutput mode is implied (since output cannot be returned).
162// bgrun adds 1 to wg immediately, and calls Done when the work completes.
163func bgrun(wg *sync.WaitGroup, dir string, cmd ...string) {
164	wg.Add(1)
165	bgwork <- func() {
166		defer wg.Done()
167		run(dir, CheckExit|ShowOutput|Background, cmd...)
168	}
169}
170
171// bgwait waits for pending bgruns to finish.
172// bgwait must be called from only a single goroutine at a time.
173func bgwait(wg *sync.WaitGroup) {
174	done := make(chan struct{})
175	go func() {
176		wg.Wait()
177		close(done)
178	}()
179	select {
180	case <-done:
181	case <-dying:
182		// Don't return to the caller, to avoid reporting additional errors
183		// to the user.
184		select {}
185	}
186}
187
188// xgetwd returns the current directory.
189func xgetwd() string {
190	wd, err := os.Getwd()
191	if err != nil {
192		fatalf("%s", err)
193	}
194	return wd
195}
196
197// xrealwd returns the 'real' name for the given path.
198// real is defined as what xgetwd returns in that directory.
199func xrealwd(path string) string {
200	old := xgetwd()
201	if err := os.Chdir(path); err != nil {
202		fatalf("chdir %s: %v", path, err)
203	}
204	real := xgetwd()
205	if err := os.Chdir(old); err != nil {
206		fatalf("chdir %s: %v", old, err)
207	}
208	return real
209}
210
211// isdir reports whether p names an existing directory.
212func isdir(p string) bool {
213	fi, err := os.Stat(p)
214	return err == nil && fi.IsDir()
215}
216
217// isfile reports whether p names an existing file.
218func isfile(p string) bool {
219	fi, err := os.Stat(p)
220	return err == nil && fi.Mode().IsRegular()
221}
222
223// mtime returns the modification time of the file p.
224func mtime(p string) time.Time {
225	fi, err := os.Stat(p)
226	if err != nil {
227		return time.Time{}
228	}
229	return fi.ModTime()
230}
231
232// readfile returns the content of the named file.
233func readfile(file string) string {
234	data, err := os.ReadFile(file)
235	if err != nil {
236		fatalf("%v", err)
237	}
238	return string(data)
239}
240
241const (
242	writeExec = 1 << iota
243	writeSkipSame
244)
245
246// writefile writes text to the named file, creating it if needed.
247// if exec is non-zero, marks the file as executable.
248// If the file already exists and has the expected content,
249// it is not rewritten, to avoid changing the time stamp.
250func writefile(text, file string, flag int) {
251	new := []byte(text)
252	if flag&writeSkipSame != 0 {
253		old, err := os.ReadFile(file)
254		if err == nil && bytes.Equal(old, new) {
255			return
256		}
257	}
258	mode := os.FileMode(0666)
259	if flag&writeExec != 0 {
260		mode = 0777
261	}
262	xremove(file) // in case of symlink tricks by misc/reboot test
263	err := os.WriteFile(file, new, mode)
264	if err != nil {
265		fatalf("%v", err)
266	}
267}
268
269// xmkdir creates the directory p.
270func xmkdir(p string) {
271	err := os.Mkdir(p, 0777)
272	if err != nil {
273		fatalf("%v", err)
274	}
275}
276
277// xmkdirall creates the directory p and its parents, as needed.
278func xmkdirall(p string) {
279	err := os.MkdirAll(p, 0777)
280	if err != nil {
281		fatalf("%v", err)
282	}
283}
284
285// xremove removes the file p.
286func xremove(p string) {
287	if vflag > 2 {
288		errprintf("rm %s\n", p)
289	}
290	os.Remove(p)
291}
292
293// xremoveall removes the file or directory tree rooted at p.
294func xremoveall(p string) {
295	if vflag > 2 {
296		errprintf("rm -r %s\n", p)
297	}
298	os.RemoveAll(p)
299}
300
301// xreaddir replaces dst with a list of the names of the files and subdirectories in dir.
302// The names are relative to dir; they are not full paths.
303func xreaddir(dir string) []string {
304	f, err := os.Open(dir)
305	if err != nil {
306		fatalf("%v", err)
307	}
308	defer f.Close()
309	names, err := f.Readdirnames(-1)
310	if err != nil {
311		fatalf("reading %s: %v", dir, err)
312	}
313	return names
314}
315
316// xworkdir creates a new temporary directory to hold object files
317// and returns the name of that directory.
318func xworkdir() string {
319	name, err := os.MkdirTemp(os.Getenv("GOTMPDIR"), "go-tool-dist-")
320	if err != nil {
321		fatalf("%v", err)
322	}
323	return name
324}
325
326// fatalf prints an error message to standard error and exits.
327func fatalf(format string, args ...interface{}) {
328	fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...))
329
330	dieOnce.Do(func() { close(dying) })
331
332	// Wait for background goroutines to finish,
333	// so that exit handler that removes the work directory
334	// is not fighting with active writes or open files.
335	bghelpers.Wait()
336
337	xexit(2)
338}
339
340var atexits []func()
341
342// xexit exits the process with return code n.
343func xexit(n int) {
344	for i := len(atexits) - 1; i >= 0; i-- {
345		atexits[i]()
346	}
347	os.Exit(n)
348}
349
350// xatexit schedules the exit-handler f to be run when the program exits.
351func xatexit(f func()) {
352	atexits = append(atexits, f)
353}
354
355// xprintf prints a message to standard output.
356func xprintf(format string, args ...interface{}) {
357	fmt.Printf(format, args...)
358}
359
360// errprintf prints a message to standard output.
361func errprintf(format string, args ...interface{}) {
362	fmt.Fprintf(os.Stderr, format, args...)
363}
364
365// xsamefile reports whether f1 and f2 are the same file (or dir).
366func xsamefile(f1, f2 string) bool {
367	fi1, err1 := os.Stat(f1)
368	fi2, err2 := os.Stat(f2)
369	if err1 != nil || err2 != nil {
370		return f1 == f2
371	}
372	return os.SameFile(fi1, fi2)
373}
374
375func xgetgoarm() string {
376	// If we're building on an actual arm system, and not building
377	// a cross-compiling toolchain, try to exec ourselves
378	// to detect whether VFP is supported and set the default GOARM.
379	// Windows requires ARMv7, so we can skip the check.
380	// We've always assumed Android is ARMv7 too.
381	if gohostarch == "arm" && goarch == "arm" && goos == gohostos && goos != "windows" && goos != "android" {
382		// Try to exec ourselves in a mode to detect VFP support.
383		// Seeing how far it gets determines which instructions failed.
384		// The test is OS-agnostic.
385		out := run("", 0, os.Args[0], "-check-goarm")
386		v1ok := strings.Contains(out, "VFPv1 OK.")
387		v3ok := strings.Contains(out, "VFPv3 OK.")
388		if v1ok && v3ok {
389			return "7"
390		}
391		if v1ok {
392			return "6"
393		}
394		return "5"
395	}
396
397	// Otherwise, in the absence of local information, assume GOARM=7.
398	//
399	// We used to assume GOARM=5 in certain contexts but not others,
400	// which produced inconsistent results. For example if you cross-compiled
401	// for linux/arm from a windows/amd64 machine, you got GOARM=7 binaries,
402	// but if you cross-compiled for linux/arm from a linux/amd64 machine,
403	// you got GOARM=5 binaries. Now the default is independent of the
404	// host operating system, for better reproducibility of builds.
405	return "7"
406}
407
408func min(a, b int) int {
409	if a < b {
410		return a
411	}
412	return b
413}
414
415// elfIsLittleEndian detects if the ELF file is little endian.
416func elfIsLittleEndian(fn string) bool {
417	// read the ELF file header to determine the endianness without using the
418	// debug/elf package.
419	file, err := os.Open(fn)
420	if err != nil {
421		fatalf("failed to open file to determine endianness: %v", err)
422	}
423	defer file.Close()
424	var hdr [16]byte
425	if _, err := io.ReadFull(file, hdr[:]); err != nil {
426		fatalf("failed to read ELF header to determine endianness: %v", err)
427	}
428	// hdr[5] is EI_DATA byte, 1 is ELFDATA2LSB and 2 is ELFDATA2MSB
429	switch hdr[5] {
430	default:
431		fatalf("unknown ELF endianness of %s: EI_DATA = %d", fn, hdr[5])
432	case 1:
433		return true
434	case 2:
435		return false
436	}
437	panic("unreachable")
438}
439
440// count is a flag.Value that is like a flag.Bool and a flag.Int.
441// If used as -name, it increments the count, but -name=x sets the count.
442// Used for verbose flag -v.
443type count int
444
445func (c *count) String() string {
446	return fmt.Sprint(int(*c))
447}
448
449func (c *count) Set(s string) error {
450	switch s {
451	case "true":
452		*c++
453	case "false":
454		*c = 0
455	default:
456		n, err := strconv.Atoi(s)
457		if err != nil {
458			return fmt.Errorf("invalid count %q", s)
459		}
460		*c = count(n)
461	}
462	return nil
463}
464
465func (c *count) IsBoolFlag() bool {
466	return true
467}
468
469func xflagparse(maxargs int) {
470	flag.Var((*count)(&vflag), "v", "verbosity")
471	flag.Parse()
472	if maxargs >= 0 && flag.NArg() > maxargs {
473		flag.Usage()
474	}
475}
476