github.com/flyinox/gosm@v0.0.0-20171117061539-16768cb62077/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 "fmt" 10 "io" 11 "io/ioutil" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "runtime" 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 // splitlines returns a slice with the result of splitting 55 // the input p after each \n. 56 func splitlines(p string) []string { 57 return strings.SplitAfter(p, "\n") 58 } 59 60 // splitfields replaces the vector v with the result of splitting 61 // the input p into non-empty fields containing no spaces. 62 func splitfields(p string) []string { 63 return strings.Fields(p) 64 } 65 66 const ( 67 CheckExit = 1 << iota 68 ShowOutput 69 Background 70 ) 71 72 var outputLock sync.Mutex 73 74 // run runs the command line cmd in dir. 75 // If mode has ShowOutput set and Background unset, run passes cmd's output to 76 // stdout/stderr directly. Otherwise, run returns cmd's output as a string. 77 // If mode has CheckExit set and the command fails, run calls fatal. 78 // If mode has Background set, this command is being run as a 79 // Background job. Only bgrun should use the Background mode, 80 // not other callers. 81 func run(dir string, mode int, cmd ...string) string { 82 if vflag > 1 { 83 errprintf("run: %s\n", strings.Join(cmd, " ")) 84 } 85 86 xcmd := exec.Command(cmd[0], cmd[1:]...) 87 xcmd.Dir = dir 88 var data []byte 89 var err error 90 91 // If we want to show command output and this is not 92 // a background command, assume it's the only thing 93 // running, so we can just let it write directly stdout/stderr 94 // as it runs without fear of mixing the output with some 95 // other command's output. Not buffering lets the output 96 // appear as it is printed instead of once the command exits. 97 // This is most important for the invocation of 'go1.4 build -v bootstrap/...'. 98 if mode&(Background|ShowOutput) == ShowOutput { 99 xcmd.Stdout = os.Stdout 100 xcmd.Stderr = os.Stderr 101 err = xcmd.Run() 102 } else { 103 data, err = xcmd.CombinedOutput() 104 } 105 if err != nil && mode&CheckExit != 0 { 106 outputLock.Lock() 107 if len(data) > 0 { 108 xprintf("%s\n", data) 109 } 110 outputLock.Unlock() 111 if mode&Background != 0 { 112 // Prevent fatal from waiting on our own goroutine's 113 // bghelper to exit: 114 bghelpers.Done() 115 } 116 fatal("FAILED: %v: %v", strings.Join(cmd, " "), err) 117 } 118 if mode&ShowOutput != 0 { 119 outputLock.Lock() 120 os.Stdout.Write(data) 121 outputLock.Unlock() 122 } 123 if vflag > 2 { 124 errprintf("run: %s DONE\n", strings.Join(cmd, " ")) 125 } 126 return string(data) 127 } 128 129 var maxbg = 4 /* maximum number of jobs to run at once */ 130 131 var ( 132 bgwork = make(chan func(), 1e5) 133 134 bghelpers sync.WaitGroup 135 136 dieOnce sync.Once // guards close of dying 137 dying = make(chan struct{}) 138 ) 139 140 func bginit() { 141 bghelpers.Add(maxbg) 142 for i := 0; i < maxbg; i++ { 143 go bghelper() 144 } 145 } 146 147 func bghelper() { 148 defer bghelpers.Done() 149 for { 150 select { 151 case <-dying: 152 return 153 case w := <-bgwork: 154 // Dying takes precedence over doing more work. 155 select { 156 case <-dying: 157 return 158 default: 159 w() 160 } 161 } 162 } 163 } 164 165 // bgrun is like run but runs the command in the background. 166 // CheckExit|ShowOutput mode is implied (since output cannot be returned). 167 // bgrun adds 1 to wg immediately, and calls Done when the work completes. 168 func bgrun(wg *sync.WaitGroup, dir string, cmd ...string) { 169 wg.Add(1) 170 bgwork <- func() { 171 defer wg.Done() 172 run(dir, CheckExit|ShowOutput|Background, cmd...) 173 } 174 } 175 176 // bgwait waits for pending bgruns to finish. 177 // bgwait must be called from only a single goroutine at a time. 178 func bgwait(wg *sync.WaitGroup) { 179 done := make(chan struct{}) 180 go func() { 181 wg.Wait() 182 close(done) 183 }() 184 select { 185 case <-done: 186 case <-dying: 187 } 188 } 189 190 // xgetwd returns the current directory. 191 func xgetwd() string { 192 wd, err := os.Getwd() 193 if err != nil { 194 fatal("%s", err) 195 } 196 return wd 197 } 198 199 // xrealwd returns the 'real' name for the given path. 200 // real is defined as what xgetwd returns in that directory. 201 func xrealwd(path string) string { 202 old := xgetwd() 203 if err := os.Chdir(path); err != nil { 204 fatal("chdir %s: %v", path, err) 205 } 206 real := xgetwd() 207 if err := os.Chdir(old); err != nil { 208 fatal("chdir %s: %v", old, err) 209 } 210 return real 211 } 212 213 // isdir reports whether p names an existing directory. 214 func isdir(p string) bool { 215 fi, err := os.Stat(p) 216 return err == nil && fi.IsDir() 217 } 218 219 // isfile reports whether p names an existing file. 220 func isfile(p string) bool { 221 fi, err := os.Stat(p) 222 return err == nil && fi.Mode().IsRegular() 223 } 224 225 // mtime returns the modification time of the file p. 226 func mtime(p string) time.Time { 227 fi, err := os.Stat(p) 228 if err != nil { 229 return time.Time{} 230 } 231 return fi.ModTime() 232 } 233 234 // isabs reports whether p is an absolute path. 235 func isabs(p string) bool { 236 return filepath.IsAbs(p) 237 } 238 239 // readfile returns the content of the named file. 240 func readfile(file string) string { 241 data, err := ioutil.ReadFile(file) 242 if err != nil { 243 fatal("%v", err) 244 } 245 return string(data) 246 } 247 248 const ( 249 writeExec = 1 << iota 250 writeSkipSame 251 ) 252 253 // writefile writes b to the named file, creating it if needed. 254 // if exec is non-zero, marks the file as executable. 255 // If the file already exists and has the expected content, 256 // it is not rewritten, to avoid changing the time stamp. 257 func writefile(b, file string, flag int) { 258 new := []byte(b) 259 if flag&writeSkipSame != 0 { 260 old, err := ioutil.ReadFile(file) 261 if err == nil && bytes.Equal(old, new) { 262 return 263 } 264 } 265 mode := os.FileMode(0666) 266 if flag&writeExec != 0 { 267 mode = 0777 268 } 269 err := ioutil.WriteFile(file, new, mode) 270 if err != nil { 271 fatal("%v", err) 272 } 273 } 274 275 // xmkdir creates the directory p. 276 func xmkdir(p string) { 277 err := os.Mkdir(p, 0777) 278 if err != nil { 279 fatal("%v", err) 280 } 281 } 282 283 // xmkdirall creates the directory p and its parents, as needed. 284 func xmkdirall(p string) { 285 err := os.MkdirAll(p, 0777) 286 if err != nil { 287 fatal("%v", err) 288 } 289 } 290 291 // xremove removes the file p. 292 func xremove(p string) { 293 if vflag > 2 { 294 errprintf("rm %s\n", p) 295 } 296 os.Remove(p) 297 } 298 299 // xremoveall removes the file or directory tree rooted at p. 300 func xremoveall(p string) { 301 if vflag > 2 { 302 errprintf("rm -r %s\n", p) 303 } 304 os.RemoveAll(p) 305 } 306 307 // xreaddir replaces dst with a list of the names of the files and subdirectories in dir. 308 // The names are relative to dir; they are not full paths. 309 func xreaddir(dir string) []string { 310 f, err := os.Open(dir) 311 if err != nil { 312 fatal("%v", err) 313 } 314 defer f.Close() 315 names, err := f.Readdirnames(-1) 316 if err != nil { 317 fatal("reading %s: %v", dir, err) 318 } 319 return names 320 } 321 322 // xreaddir replaces dst with a list of the names of the files in dir. 323 // The names are relative to dir; they are not full paths. 324 func xreaddirfiles(dir string) []string { 325 f, err := os.Open(dir) 326 if err != nil { 327 fatal("%v", err) 328 } 329 defer f.Close() 330 infos, err := f.Readdir(-1) 331 if err != nil { 332 fatal("reading %s: %v", dir, err) 333 } 334 var names []string 335 for _, fi := range infos { 336 if !fi.IsDir() { 337 names = append(names, fi.Name()) 338 } 339 } 340 return names 341 } 342 343 // xworkdir creates a new temporary directory to hold object files 344 // and returns the name of that directory. 345 func xworkdir() string { 346 name, err := ioutil.TempDir("", "go-tool-dist-") 347 if err != nil { 348 fatal("%v", err) 349 } 350 return name 351 } 352 353 // fatal prints an error message to standard error and exits. 354 func fatal(format string, args ...interface{}) { 355 fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...)) 356 357 dieOnce.Do(func() { close(dying) }) 358 359 // Wait for background goroutines to finish, 360 // so that exit handler that removes the work directory 361 // is not fighting with active writes or open files. 362 bghelpers.Wait() 363 364 xexit(2) 365 } 366 367 var atexits []func() 368 369 // xexit exits the process with return code n. 370 func xexit(n int) { 371 for i := len(atexits) - 1; i >= 0; i-- { 372 atexits[i]() 373 } 374 os.Exit(n) 375 } 376 377 // xatexit schedules the exit-handler f to be run when the program exits. 378 func xatexit(f func()) { 379 atexits = append(atexits, f) 380 } 381 382 // xprintf prints a message to standard output. 383 func xprintf(format string, args ...interface{}) { 384 fmt.Printf(format, args...) 385 } 386 387 // errprintf prints a message to standard output. 388 func errprintf(format string, args ...interface{}) { 389 fmt.Fprintf(os.Stderr, format, args...) 390 } 391 392 // main takes care of OS-specific startup and dispatches to xmain. 393 func main() { 394 os.Setenv("TERM", "dumb") // disable escape codes in clang errors 395 396 // provide -check-armv6k first, before checking for $GOROOT so that 397 // it is possible to run this check without having $GOROOT available. 398 if len(os.Args) > 1 && os.Args[1] == "-check-armv6k" { 399 useARMv6K() // might fail with SIGILL 400 println("ARMv6K supported.") 401 os.Exit(0) 402 } 403 404 slash = string(filepath.Separator) 405 406 gohostos = runtime.GOOS 407 switch gohostos { 408 case "darwin": 409 // Even on 64-bit platform, darwin uname -m prints i386. 410 // We don't support any of the OS X versions that run on 32-bit-only hardware anymore. 411 gohostarch = "amd64" 412 case "freebsd": 413 // Since FreeBSD 10 gcc is no longer part of the base system. 414 defaultclang = true 415 case "solaris": 416 // Even on 64-bit platform, solaris uname -m prints i86pc. 417 out := run("", CheckExit, "isainfo", "-n") 418 if strings.Contains(out, "amd64") { 419 gohostarch = "amd64" 420 } 421 if strings.Contains(out, "i386") { 422 gohostarch = "386" 423 } 424 case "plan9": 425 gohostarch = os.Getenv("objtype") 426 if gohostarch == "" { 427 fatal("$objtype is unset") 428 } 429 case "windows": 430 exe = ".exe" 431 } 432 433 sysinit() 434 435 if gohostarch == "" { 436 // Default Unix system. 437 out := run("", CheckExit, "uname", "-m") 438 switch { 439 case strings.Contains(out, "x86_64"), strings.Contains(out, "amd64"): 440 gohostarch = "amd64" 441 case strings.Contains(out, "86"): 442 gohostarch = "386" 443 case strings.Contains(out, "arm"): 444 gohostarch = "arm" 445 case strings.Contains(out, "aarch64"): 446 gohostarch = "arm64" 447 case strings.Contains(out, "ppc64le"): 448 gohostarch = "ppc64le" 449 case strings.Contains(out, "ppc64"): 450 gohostarch = "ppc64" 451 case strings.Contains(out, "mips64"): 452 gohostarch = "mips64" 453 if elfIsLittleEndian(os.Args[0]) { 454 gohostarch = "mips64le" 455 } 456 case strings.Contains(out, "mips"): 457 gohostarch = "mips" 458 if elfIsLittleEndian(os.Args[0]) { 459 gohostarch = "mipsle" 460 } 461 case strings.Contains(out, "s390x"): 462 gohostarch = "s390x" 463 case gohostos == "darwin": 464 if strings.Contains(run("", CheckExit, "uname", "-v"), "RELEASE_ARM_") { 465 gohostarch = "arm" 466 } 467 default: 468 fatal("unknown architecture: %s", out) 469 } 470 } 471 472 if gohostarch == "arm" || gohostarch == "mips64" || gohostarch == "mips64le" { 473 maxbg = min(maxbg, runtime.NumCPU()) 474 } 475 bginit() 476 477 // The OS X 10.6 linker does not support external linking mode. 478 // See golang.org/issue/5130. 479 // 480 // OS X 10.6 does not work with clang either, but OS X 10.9 requires it. 481 // It seems to work with OS X 10.8, so we default to clang for 10.8 and later. 482 // See golang.org/issue/5822. 483 // 484 // Roughly, OS X 10.N shows up as uname release (N+4), 485 // so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12. 486 if gohostos == "darwin" { 487 rel := run("", CheckExit, "uname", "-r") 488 if i := strings.Index(rel, "."); i >= 0 { 489 rel = rel[:i] 490 } 491 osx, _ := strconv.Atoi(rel) 492 if osx <= 6+4 { 493 goextlinkenabled = "0" 494 } 495 if osx >= 8+4 { 496 defaultclang = true 497 } 498 } 499 500 if len(os.Args) > 1 && os.Args[1] == "-check-goarm" { 501 useVFPv1() // might fail with SIGILL 502 println("VFPv1 OK.") 503 useVFPv3() // might fail with SIGILL 504 println("VFPv3 OK.") 505 os.Exit(0) 506 } 507 508 xinit() 509 xmain() 510 xexit(0) 511 } 512 513 // xsamefile reports whether f1 and f2 are the same file (or dir) 514 func xsamefile(f1, f2 string) bool { 515 fi1, err1 := os.Stat(f1) 516 fi2, err2 := os.Stat(f2) 517 if err1 != nil || err2 != nil { 518 return f1 == f2 519 } 520 return os.SameFile(fi1, fi2) 521 } 522 523 func xgetgoarm() string { 524 if goos == "nacl" { 525 // NaCl guarantees VFPv3 and is always cross-compiled. 526 return "7" 527 } 528 if goos == "darwin" { 529 // Assume all darwin/arm devices are have VFPv3. This 530 // port is also mostly cross-compiled, so it makes little 531 // sense to auto-detect the setting. 532 return "7" 533 } 534 if gohostarch != "arm" || goos != gohostos { 535 // Conservative default for cross-compilation. 536 return "5" 537 } 538 if goos == "freebsd" || goos == "openbsd" { 539 // FreeBSD has broken VFP support. 540 // OpenBSD currently only supports softfloat. 541 return "5" 542 } 543 544 // Try to exec ourselves in a mode to detect VFP support. 545 // Seeing how far it gets determines which instructions failed. 546 // The test is OS-agnostic. 547 out := run("", 0, os.Args[0], "-check-goarm") 548 v1ok := strings.Contains(out, "VFPv1 OK.") 549 v3ok := strings.Contains(out, "VFPv3 OK.") 550 551 if v1ok && v3ok { 552 return "7" 553 } 554 if v1ok { 555 return "6" 556 } 557 return "5" 558 } 559 560 func min(a, b int) int { 561 if a < b { 562 return a 563 } 564 return b 565 } 566 567 // elfIsLittleEndian detects if the ELF file is little endian. 568 func elfIsLittleEndian(fn string) bool { 569 // read the ELF file header to determine the endianness without using the 570 // debug/elf package. 571 file, err := os.Open(fn) 572 if err != nil { 573 fatal("failed to open file to determine endianness: %v", err) 574 } 575 defer file.Close() 576 var hdr [16]byte 577 if _, err := io.ReadFull(file, hdr[:]); err != nil { 578 fatal("failed to read ELF header to determine endianness: %v", err) 579 } 580 // hdr[5] is EI_DATA byte, 1 is ELFDATA2LSB and 2 is ELFDATA2MSB 581 switch hdr[5] { 582 default: 583 fatal("unknown ELF endianness of %s: EI_DATA = %d", fn, hdr[5]) 584 case 1: 585 return true 586 case 2: 587 return false 588 } 589 panic("unreachable") 590 }