github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/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 "fmt" 9 "io/ioutil" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "runtime" 14 "sort" 15 "strconv" 16 "strings" 17 "sync" 18 "sync/atomic" 19 "time" 20 ) 21 22 // pathf is fmt.Sprintf for generating paths 23 // (on windows it turns / into \ after the printf). 24 func 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. 29 func 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. 40 func 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 53 // splitlines returns a slice with the result of splitting 54 // the input p after each \n. 55 func splitlines(p string) []string { 56 return strings.SplitAfter(p, "\n") 57 } 58 59 // splitfields replaces the vector v with the result of splitting 60 // the input p into non-empty fields containing no spaces. 61 func splitfields(p string) []string { 62 return strings.Fields(p) 63 } 64 65 const ( 66 CheckExit = 1 << iota 67 ShowOutput 68 Background 69 ) 70 71 var outputLock sync.Mutex 72 73 // run runs the command line cmd in dir. 74 // If mode has ShowOutput set, run collects cmd's output and returns it as a string; 75 // otherwise, run prints cmd's output to standard output after the command finishes. 76 // If mode has CheckExit set and the command fails, run calls fatal. 77 // If mode has Background set, this command is being run as a 78 // Background job. Only bgrun should use the Background mode, 79 // not other callers. 80 func run(dir string, mode int, cmd ...string) string { 81 if vflag > 1 { 82 errprintf("run: %s\n", strings.Join(cmd, " ")) 83 } 84 85 xcmd := exec.Command(cmd[0], cmd[1:]...) 86 xcmd.Dir = dir 87 var data []byte 88 var err error 89 90 // If we want to show command output and this is not 91 // a background command, assume it's the only thing 92 // running, so we can just let it write directly stdout/stderr 93 // as it runs without fear of mixing the output with some 94 // other command's output. Not buffering lets the output 95 // appear as it is printed instead of once the command exits. 96 // This is most important for the invocation of 'go1.4 build -v bootstrap/...'. 97 if mode&(Background|ShowOutput) == ShowOutput { 98 xcmd.Stdout = os.Stdout 99 xcmd.Stderr = os.Stderr 100 err = xcmd.Run() 101 } else { 102 data, err = xcmd.CombinedOutput() 103 } 104 if err != nil && mode&CheckExit != 0 { 105 outputLock.Lock() 106 if len(data) > 0 { 107 xprintf("%s\n", data) 108 } 109 outputLock.Unlock() 110 if mode&Background != 0 { 111 bgdied.Done() 112 } 113 fatal("FAILED: %v: %v", strings.Join(cmd, " "), err) 114 } 115 if mode&ShowOutput != 0 { 116 outputLock.Lock() 117 os.Stdout.Write(data) 118 outputLock.Unlock() 119 } 120 if vflag > 2 { 121 errprintf("run: %s DONE\n", strings.Join(cmd, " ")) 122 } 123 return string(data) 124 } 125 126 var maxbg = 4 /* maximum number of jobs to run at once */ 127 128 var ( 129 bgwork = make(chan func(), 1e5) 130 bgdone = make(chan struct{}, 1e5) 131 132 bgdied sync.WaitGroup 133 nwork int32 134 ndone int32 135 136 dying = make(chan bool) 137 nfatal int32 138 ) 139 140 func bginit() { 141 bgdied.Add(maxbg) 142 for i := 0; i < maxbg; i++ { 143 go bghelper() 144 } 145 } 146 147 func bghelper() { 148 for { 149 w := <-bgwork 150 w() 151 152 // Stop if we're dying. 153 if atomic.LoadInt32(&nfatal) > 0 { 154 bgdied.Done() 155 return 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 func bgrun(dir string, cmd ...string) { 163 bgwork <- func() { 164 run(dir, CheckExit|ShowOutput|Background, cmd...) 165 } 166 } 167 168 // bgwait waits for pending bgruns to finish. 169 // bgwait must be called from only a single goroutine at a time. 170 func bgwait() { 171 var wg sync.WaitGroup 172 wg.Add(maxbg) 173 done := make(chan bool) 174 for i := 0; i < maxbg; i++ { 175 bgwork <- func() { 176 wg.Done() 177 178 // Hold up bg goroutine until either the wait finishes 179 // or the program starts dying due to a call to fatal. 180 select { 181 case <-dying: 182 case <-done: 183 } 184 } 185 } 186 wg.Wait() 187 close(done) 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 // writefile writes b to the named file, creating it if needed. if 249 // exec is non-zero, marks the file as executable. 250 func writefile(b, file string, exec int) { 251 mode := os.FileMode(0666) 252 if exec != 0 { 253 mode = 0777 254 } 255 err := ioutil.WriteFile(file, []byte(b), mode) 256 if err != nil { 257 fatal("%v", err) 258 } 259 } 260 261 // xmkdir creates the directory p. 262 func xmkdir(p string) { 263 err := os.Mkdir(p, 0777) 264 if err != nil { 265 fatal("%v", err) 266 } 267 } 268 269 // xmkdirall creates the directory p and its parents, as needed. 270 func xmkdirall(p string) { 271 err := os.MkdirAll(p, 0777) 272 if err != nil { 273 fatal("%v", err) 274 } 275 } 276 277 // xremove removes the file p. 278 func xremove(p string) { 279 if vflag > 2 { 280 errprintf("rm %s\n", p) 281 } 282 os.Remove(p) 283 } 284 285 // xremoveall removes the file or directory tree rooted at p. 286 func xremoveall(p string) { 287 if vflag > 2 { 288 errprintf("rm -r %s\n", p) 289 } 290 os.RemoveAll(p) 291 } 292 293 // xreaddir replaces dst with a list of the names of the files and subdirectories in dir. 294 // The names are relative to dir; they are not full paths. 295 func xreaddir(dir string) []string { 296 f, err := os.Open(dir) 297 if err != nil { 298 fatal("%v", err) 299 } 300 defer f.Close() 301 names, err := f.Readdirnames(-1) 302 if err != nil { 303 fatal("reading %s: %v", dir, err) 304 } 305 return names 306 } 307 308 // xreaddir replaces dst with a list of the names of the files in dir. 309 // The names are relative to dir; they are not full paths. 310 func xreaddirfiles(dir string) []string { 311 f, err := os.Open(dir) 312 if err != nil { 313 fatal("%v", err) 314 } 315 defer f.Close() 316 infos, err := f.Readdir(-1) 317 if err != nil { 318 fatal("reading %s: %v", dir, err) 319 } 320 var names []string 321 for _, fi := range infos { 322 if !fi.IsDir() { 323 names = append(names, fi.Name()) 324 } 325 } 326 return names 327 } 328 329 // xworkdir creates a new temporary directory to hold object files 330 // and returns the name of that directory. 331 func xworkdir() string { 332 name, err := ioutil.TempDir("", "go-tool-dist-") 333 if err != nil { 334 fatal("%v", err) 335 } 336 return name 337 } 338 339 // fatal prints an error message to standard error and exits. 340 func fatal(format string, args ...interface{}) { 341 fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...)) 342 343 // Wait for background goroutines to finish, 344 // so that exit handler that removes the work directory 345 // is not fighting with active writes or open files. 346 if atomic.AddInt32(&nfatal, 1) == 1 { 347 close(dying) 348 } 349 for i := 0; i < maxbg; i++ { 350 bgwork <- func() {} // wake up workers so they notice nfatal > 0 351 } 352 bgdied.Wait() 353 354 xexit(2) 355 } 356 357 var atexits []func() 358 359 // xexit exits the process with return code n. 360 func xexit(n int) { 361 for i := len(atexits) - 1; i >= 0; i-- { 362 atexits[i]() 363 } 364 os.Exit(n) 365 } 366 367 // xatexit schedules the exit-handler f to be run when the program exits. 368 func xatexit(f func()) { 369 atexits = append(atexits, f) 370 } 371 372 // xprintf prints a message to standard output. 373 func xprintf(format string, args ...interface{}) { 374 fmt.Printf(format, args...) 375 } 376 377 // errprintf prints a message to standard output. 378 func errprintf(format string, args ...interface{}) { 379 fmt.Fprintf(os.Stderr, format, args...) 380 } 381 382 // main takes care of OS-specific startup and dispatches to xmain. 383 func main() { 384 os.Setenv("TERM", "dumb") // disable escape codes in clang errors 385 386 slash = string(filepath.Separator) 387 388 gohostos = runtime.GOOS 389 switch gohostos { 390 case "darwin": 391 // Even on 64-bit platform, darwin uname -m prints i386. 392 if strings.Contains(run("", CheckExit, "sysctl", "machdep.cpu.extfeatures"), "EM64T") { 393 gohostarch = "amd64" 394 } 395 case "solaris": 396 // Even on 64-bit platform, solaris uname -m prints i86pc. 397 out := run("", CheckExit, "isainfo", "-n") 398 if strings.Contains(out, "amd64") { 399 gohostarch = "amd64" 400 } 401 if strings.Contains(out, "i386") { 402 gohostarch = "386" 403 } 404 case "plan9": 405 gohostarch = os.Getenv("objtype") 406 if gohostarch == "" { 407 fatal("$objtype is unset") 408 } 409 case "windows": 410 exe = ".exe" 411 } 412 413 sysinit() 414 415 if gohostarch == "" { 416 // Default Unix system. 417 out := run("", CheckExit, "uname", "-m") 418 switch { 419 case strings.Contains(out, "x86_64"), strings.Contains(out, "amd64"): 420 gohostarch = "amd64" 421 case strings.Contains(out, "86"): 422 gohostarch = "386" 423 case strings.Contains(out, "arm"): 424 gohostarch = "arm" 425 case strings.Contains(out, "aarch64"): 426 gohostarch = "arm64" 427 case strings.Contains(out, "ppc64le"): 428 gohostarch = "ppc64le" 429 case strings.Contains(out, "ppc64"): 430 gohostarch = "ppc64" 431 case gohostos == "darwin": 432 if strings.Contains(run("", CheckExit, "uname", "-v"), "RELEASE_ARM_") { 433 gohostarch = "arm" 434 } 435 default: 436 fatal("unknown architecture: %s", out) 437 } 438 } 439 440 if gohostarch == "arm" { 441 maxbg = min(maxbg, runtime.NumCPU()) 442 } 443 bginit() 444 445 // The OS X 10.6 linker does not support external linking mode. 446 // See golang.org/issue/5130. 447 // 448 // OS X 10.6 does not work with clang either, but OS X 10.9 requires it. 449 // It seems to work with OS X 10.8, so we default to clang for 10.8 and later. 450 // See golang.org/issue/5822. 451 // 452 // Roughly, OS X 10.N shows up as uname release (N+4), 453 // so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12. 454 if gohostos == "darwin" { 455 rel := run("", CheckExit, "uname", "-r") 456 if i := strings.Index(rel, "."); i >= 0 { 457 rel = rel[:i] 458 } 459 osx, _ := strconv.Atoi(rel) 460 if osx <= 6+4 { 461 goextlinkenabled = "0" 462 } 463 if osx >= 8+4 { 464 defaultclang = true 465 } 466 } 467 468 xinit() 469 xmain() 470 xexit(0) 471 } 472 473 // xsamefile reports whether f1 and f2 are the same file (or dir) 474 func xsamefile(f1, f2 string) bool { 475 fi1, err1 := os.Stat(f1) 476 fi2, err2 := os.Stat(f2) 477 if err1 != nil || err2 != nil { 478 return f1 == f2 479 } 480 return os.SameFile(fi1, fi2) 481 } 482 483 func xgetgoarm() string { 484 if goos == "nacl" { 485 // NaCl guarantees VFPv3 and is always cross-compiled. 486 return "7" 487 } 488 if goos == "darwin" { 489 // Assume all darwin/arm devices are have VFPv3. This 490 // port is also mostly cross-compiled, so it makes little 491 // sense to auto-detect the setting. 492 return "7" 493 } 494 if gohostarch != "arm" || goos != gohostos { 495 // Conservative default for cross-compilation. 496 return "5" 497 } 498 if goos == "freebsd" || goos == "openbsd" { 499 // FreeBSD has broken VFP support. 500 // OpenBSD currently only supports softfloat. 501 return "5" 502 } 503 if goos != "linux" { 504 // All other arm platforms that we support 505 // require ARMv7. 506 return "7" 507 } 508 cpuinfo := readfile("/proc/cpuinfo") 509 goarm := "5" 510 for _, line := range splitlines(cpuinfo) { 511 line := strings.SplitN(line, ":", 2) 512 if len(line) < 2 { 513 continue 514 } 515 if strings.TrimSpace(line[0]) != "Features" { 516 continue 517 } 518 features := splitfields(line[1]) 519 sort.Strings(features) // so vfpv3 sorts after vfp 520 521 // Infer GOARM value from the vfp features available 522 // on this host. Values of GOARM detected are: 523 // 5: no vfp support was found 524 // 6: vfp (v1) support was detected, but no higher 525 // 7: vfpv3 support was detected. 526 // This matches the assertions in runtime.checkarm. 527 for _, f := range features { 528 switch f { 529 case "vfp": 530 goarm = "6" 531 case "vfpv3": 532 goarm = "7" 533 } 534 } 535 } 536 return goarm 537 } 538 539 func min(a, b int) int { 540 if a < b { 541 return a 542 } 543 return b 544 }