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