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