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