| |
| |
| |
|
|
| package main |
|
|
| import ( |
| "bytes" |
| "flag" |
| "fmt" |
| "io" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "sort" |
| "strconv" |
| "strings" |
| "sync" |
| "time" |
| ) |
|
|
| |
| |
| func pathf(format string, args ...any) string { |
| return filepath.Clean(fmt.Sprintf(format, args...)) |
| } |
|
|
| |
| 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 |
| } |
|
|
| |
| 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 |
|
|
| |
| func run(dir string, mode int, cmd ...string) string { |
| return runEnv(dir, mode, nil, cmd...) |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| 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 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 { |
| |
| |
| 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 |
|
|
| var ( |
| bgwork = make(chan func(), 1e5) |
|
|
| bghelpers sync.WaitGroup |
|
|
| dieOnce sync.Once |
| 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: |
| |
| select { |
| case <-dying: |
| return |
| default: |
| w() |
| } |
| } |
| } |
| } |
|
|
| |
| |
| |
| func bgrun(wg *sync.WaitGroup, dir string, cmd ...string) { |
| wg.Add(1) |
| bgwork <- func() { |
| defer wg.Done() |
| run(dir, CheckExit|ShowOutput|Background, cmd...) |
| } |
| } |
|
|
| |
| |
| func bgwait(wg *sync.WaitGroup) { |
| done := make(chan struct{}) |
| go func() { |
| wg.Wait() |
| close(done) |
| }() |
| select { |
| case <-done: |
| case <-dying: |
| |
| |
| select {} |
| } |
| } |
|
|
| |
| func xgetwd() string { |
| wd, err := os.Getwd() |
| if err != nil { |
| fatalf("%s", err) |
| } |
| return wd |
| } |
|
|
| |
| |
| 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 |
| } |
|
|
| |
| func isdir(p string) bool { |
| fi, err := os.Stat(p) |
| return err == nil && fi.IsDir() |
| } |
|
|
| |
| func isfile(p string) bool { |
| fi, err := os.Stat(p) |
| return err == nil && fi.Mode().IsRegular() |
| } |
|
|
| |
| func mtime(p string) time.Time { |
| fi, err := os.Stat(p) |
| if err != nil { |
| return time.Time{} |
| } |
| return fi.ModTime() |
| } |
|
|
| |
| func readfile(file string) string { |
| data, err := os.ReadFile(file) |
| if err != nil { |
| fatalf("%v", err) |
| } |
| return string(data) |
| } |
|
|
| const ( |
| writeExec = 1 << iota |
| writeSkipSame |
| ) |
|
|
| |
| |
| |
| |
| 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) |
| err := os.WriteFile(file, new, mode) |
| if err != nil { |
| fatalf("%v", err) |
| } |
| } |
|
|
| |
| func xmkdir(p string) { |
| err := os.Mkdir(p, 0777) |
| if err != nil { |
| fatalf("%v", err) |
| } |
| } |
|
|
| |
| func xmkdirall(p string) { |
| err := os.MkdirAll(p, 0777) |
| if err != nil { |
| fatalf("%v", err) |
| } |
| } |
|
|
| |
| func xremove(p string) { |
| if vflag > 2 { |
| errprintf("rm %s\n", p) |
| } |
| os.Remove(p) |
| } |
|
|
| |
| func xremoveall(p string) { |
| if vflag > 2 { |
| errprintf("rm -r %s\n", p) |
| } |
| os.RemoveAll(p) |
| } |
|
|
| |
| |
| 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 |
| } |
|
|
| |
| |
| func xworkdir() string { |
| name, err := os.MkdirTemp(os.Getenv("GOTMPDIR"), "go-tool-dist-") |
| if err != nil { |
| fatalf("%v", err) |
| } |
| return name |
| } |
|
|
| |
| func fatalf(format string, args ...any) { |
| fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...)) |
|
|
| dieOnce.Do(func() { close(dying) }) |
|
|
| |
| |
| |
| bghelpers.Wait() |
|
|
| xexit(2) |
| } |
|
|
| var atexits []func() |
|
|
| |
| func xexit(n int) { |
| for i := len(atexits) - 1; i >= 0; i-- { |
| atexits[i]() |
| } |
| os.Exit(n) |
| } |
|
|
| |
| func xatexit(f func()) { |
| atexits = append(atexits, f) |
| } |
|
|
| |
| func xprintf(format string, args ...any) { |
| fmt.Printf(format, args...) |
| } |
|
|
| |
| func errprintf(format string, args ...any) { |
| fmt.Fprintf(os.Stderr, format, args...) |
| } |
|
|
| func xgetgoarm() string { |
| |
| |
| |
| |
| if gohostarch == "arm" && goarch == "arm" && goos == gohostos && goos != "android" { |
| |
| |
| |
| 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" |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| return "7" |
| } |
|
|
| |
| func elfIsLittleEndian(fn string) bool { |
| |
| |
| 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) |
| } |
| |
| 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") |
| } |
|
|
| |
| |
| |
| 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() |
| } |
| } |
|
|