// Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "bytes" "flag" "fmt" "io" "os" "os/exec" "path/filepath" "sort" "strconv" "strings" "sync" "time" ) // pathf is fmt.Sprintf for generating paths // (on windows it turns / into \ after the printf). func pathf(format string, args ...interface{}) string { return filepath.Clean(fmt.Sprintf(format, args...)) } // filter returns a slice containing the elements x from list for which f(x) == true. func filter(list []string, f func(string) bool) []string { var out []string for _, x := range list { if f(x) { out = append(out, x) } } return out } // uniq returns a sorted slice containing the unique elements of list. func uniq(list []string) []string { out := make([]string, len(list)) copy(out, list) sort.Strings(out) keep := out[:0] for _, x := range out { if len(keep) == 0 || keep[len(keep)-1] != x { keep = append(keep, x) } } return keep } const ( CheckExit = 1 << iota ShowOutput Background ) var outputLock sync.Mutex // run is like runEnv with no additional environment. func run(dir string, mode int, cmd ...string) string { return runEnv(dir, mode, nil, cmd...) } // runEnv runs the command line cmd in dir with additional environment env. // If mode has ShowOutput set and Background unset, run passes cmd's output to // stdout/stderr directly. Otherwise, run returns cmd's output as a string. // If mode has CheckExit set and the command fails, run calls fatalf. // If mode has Background set, this command is being run as a // Background job. Only bgrun should use the Background mode, // not other callers. func runEnv(dir string, mode int, env []string, cmd ...string) string { if vflag > 1 { errprintf("run: %s\n", strings.Join(cmd, " ")) } xcmd := exec.Command(cmd[0], cmd[1:]...) if env != nil { xcmd.Env = append(os.Environ(), env...) } setDir(xcmd, dir) var data []byte var err error // If we want to show command output and this is not // a background command, assume it's the only thing // running, so we can just let it write directly stdout/stderr // as it runs without fear of mixing the output with some // other command's output. Not buffering lets the output // appear as it is printed instead of once the command exits. // This is most important for the invocation of 'go build -v bootstrap/...'. if mode&(Background|ShowOutput) == ShowOutput { xcmd.Stdout = os.Stdout xcmd.Stderr = os.Stderr err = xcmd.Run() } else { data, err = xcmd.CombinedOutput() } if err != nil && mode&CheckExit != 0 { outputLock.Lock() if len(data) > 0 { xprintf("%s\n", data) } outputLock.Unlock() if mode&Background != 0 { // Prevent fatalf from waiting on our own goroutine's // bghelper to exit: bghelpers.Done() } fatalf("FAILED: %v: %v", strings.Join(cmd, " "), err) } if mode&ShowOutput != 0 { outputLock.Lock() os.Stdout.Write(data) outputLock.Unlock() } if vflag > 2 { errprintf("run: %s DONE\n", strings.Join(cmd, " ")) } return string(data) } var maxbg = 4 /* maximum number of jobs to run at once */ var ( bgwork = make(chan func(), 1e5) bghelpers sync.WaitGroup dieOnce sync.Once // guards close of dying dying = make(chan struct{}) ) func bginit() { bghelpers.Add(maxbg) for i := 0; i < maxbg; i++ { go bghelper() } } func bghelper() { defer bghelpers.Done() for { select { case <-dying: return case w := <-bgwork: // Dying takes precedence over doing more work. select { case <-dying: return default: w() } } } } // bgrun is like run but runs the command in the background. // CheckExit|ShowOutput mode is implied (since output cannot be returned). // bgrun adds 1 to wg immediately, and calls Done when the work completes. func bgrun(wg *sync.WaitGroup, dir string, cmd ...string) { wg.Add(1) bgwork <- func() { defer wg.Done() run(dir, CheckExit|ShowOutput|Background, cmd...) } } // bgwait waits for pending bgruns to finish. // bgwait must be called from only a single goroutine at a time. func bgwait(wg *sync.WaitGroup) { done := make(chan struct{}) go func() { wg.Wait() close(done) }() select { case <-done: case <-dying: // Don't return to the caller, to avoid reporting additional errors // to the user. select {} } } // xgetwd returns the current directory. func xgetwd() string { wd, err := os.Getwd() if err != nil { fatalf("%s", err) } return wd } // xrealwd returns the 'real' name for the given path. // real is defined as what xgetwd returns in that directory. func xrealwd(path string) string { old := xgetwd() if err := os.Chdir(path); err != nil { fatalf("chdir %s: %v", path, err) } real := xgetwd() if err := os.Chdir(old); err != nil { fatalf("chdir %s: %v", old, err) } return real } // isdir reports whether p names an existing directory. func isdir(p string) bool { fi, err := os.Stat(p) return err == nil && fi.IsDir() } // isfile reports whether p names an existing file. func isfile(p string) bool { fi, err := os.Stat(p) return err == nil && fi.Mode().IsRegular() } // mtime returns the modification time of the file p. func mtime(p string) time.Time { fi, err := os.Stat(p) if err != nil { return time.Time{} } return fi.ModTime() } // readfile returns the content of the named file. func readfile(file string) string { data, err := os.ReadFile(file) if err != nil { fatalf("%v", err) } return string(data) } const ( writeExec = 1 << iota writeSkipSame ) // writefile writes text to the named file, creating it if needed. // if exec is non-zero, marks the file as executable. // If the file already exists and has the expected content, // it is not rewritten, to avoid changing the time stamp. func writefile(text, file string, flag int) { new := []byte(text) if flag&writeSkipSame != 0 { old, err := os.ReadFile(file) if err == nil && bytes.Equal(old, new) { return } } mode := os.FileMode(0666) if flag&writeExec != 0 { mode = 0777 } xremove(file) // in case of symlink tricks by misc/reboot test err := os.WriteFile(file, new, mode) if err != nil { fatalf("%v", err) } } // xmkdir creates the directory p. func xmkdir(p string) { err := os.Mkdir(p, 0777) if err != nil { fatalf("%v", err) } } // xmkdirall creates the directory p and its parents, as needed. func xmkdirall(p string) { err := os.MkdirAll(p, 0777) if err != nil { fatalf("%v", err) } } // xremove removes the file p. func xremove(p string) { if vflag > 2 { errprintf("rm %s\n", p) } os.Remove(p) } // xremoveall removes the file or directory tree rooted at p. func xremoveall(p string) { if vflag > 2 { errprintf("rm -r %s\n", p) } os.RemoveAll(p) } // xreaddir replaces dst with a list of the names of the files and subdirectories in dir. // The names are relative to dir; they are not full paths. func xreaddir(dir string) []string { f, err := os.Open(dir) if err != nil { fatalf("%v", err) } defer f.Close() names, err := f.Readdirnames(-1) if err != nil { fatalf("reading %s: %v", dir, err) } return names } // xworkdir creates a new temporary directory to hold object files // and returns the name of that directory. func xworkdir() string { name, err := os.MkdirTemp(os.Getenv("GOTMPDIR"), "go-tool-dist-") if err != nil { fatalf("%v", err) } return name } // fatalf prints an error message to standard error and exits. func fatalf(format string, args ...interface{}) { fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...)) dieOnce.Do(func() { close(dying) }) // Wait for background goroutines to finish, // so that exit handler that removes the work directory // is not fighting with active writes or open files. bghelpers.Wait() xexit(2) } var atexits []func() // xexit exits the process with return code n. func xexit(n int) { for i := len(atexits) - 1; i >= 0; i-- { atexits[i]() } os.Exit(n) } // xatexit schedules the exit-handler f to be run when the program exits. func xatexit(f func()) { atexits = append(atexits, f) } // xprintf prints a message to standard output. func xprintf(format string, args ...interface{}) { fmt.Printf(format, args...) } // errprintf prints a message to standard output. func errprintf(format string, args ...interface{}) { fmt.Fprintf(os.Stderr, format, args...) } // xsamefile reports whether f1 and f2 are the same file (or dir). func xsamefile(f1, f2 string) bool { fi1, err1 := os.Stat(f1) fi2, err2 := os.Stat(f2) if err1 != nil || err2 != nil { return f1 == f2 } return os.SameFile(fi1, fi2) } func xgetgoarm() string { // If we're building on an actual arm system, and not building // a cross-compiling toolchain, try to exec ourselves // to detect whether VFP is supported and set the default GOARM. // Windows requires ARMv7, so we can skip the check. // We've always assumed Android is ARMv7 too. if gohostarch == "arm" && goarch == "arm" && goos == gohostos && goos != "windows" && goos != "android" { // Try to exec ourselves in a mode to detect VFP support. // Seeing how far it gets determines which instructions failed. // The test is OS-agnostic. out := run("", 0, os.Args[0], "-check-goarm") v1ok := strings.Contains(out, "VFPv1 OK.") v3ok := strings.Contains(out, "VFPv3 OK.") if v1ok && v3ok { return "7" } if v1ok { return "6" } return "5" } // Otherwise, in the absence of local information, assume GOARM=7. // // We used to assume GOARM=5 in certain contexts but not others, // which produced inconsistent results. For example if you cross-compiled // for linux/arm from a windows/amd64 machine, you got GOARM=7 binaries, // but if you cross-compiled for linux/arm from a linux/amd64 machine, // you got GOARM=5 binaries. Now the default is independent of the // host operating system, for better reproducibility of builds. return "7" } func min(a, b int) int { if a < b { return a } return b } // elfIsLittleEndian detects if the ELF file is little endian. func elfIsLittleEndian(fn string) bool { // read the ELF file header to determine the endianness without using the // debug/elf package. file, err := os.Open(fn) if err != nil { fatalf("failed to open file to determine endianness: %v", err) } defer file.Close() var hdr [16]byte if _, err := io.ReadFull(file, hdr[:]); err != nil { fatalf("failed to read ELF header to determine endianness: %v", err) } // hdr[5] is EI_DATA byte, 1 is ELFDATA2LSB and 2 is ELFDATA2MSB switch hdr[5] { default: fatalf("unknown ELF endianness of %s: EI_DATA = %d", fn, hdr[5]) case 1: return true case 2: return false } panic("unreachable") } // count is a flag.Value that is like a flag.Bool and a flag.Int. // If used as -name, it increments the count, but -name=x sets the count. // Used for verbose flag -v. type count int func (c *count) String() string { return fmt.Sprint(int(*c)) } func (c *count) Set(s string) error { switch s { case "true": *c++ case "false": *c = 0 default: n, err := strconv.Atoi(s) if err != nil { return fmt.Errorf("invalid count %q", s) } *c = count(n) } return nil } func (c *count) IsBoolFlag() bool { return true } func xflagparse(maxargs int) { flag.Var((*count)(&vflag), "v", "verbosity") flag.Parse() if maxargs >= 0 && flag.NArg() > maxargs { flag.Usage() } }