github.com/peggyl/go@v0.0.0-20151008231540-ae315999c2d5/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/ioutil" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "runtime" 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). 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 // Prevent fatal from waiting on our own goroutine's 112 // bghelper to exit: 113 bghelpers.Done() 114 } 115 fatal("FAILED: %v: %v", strings.Join(cmd, " "), err) 116 } 117 if mode&ShowOutput != 0 { 118 outputLock.Lock() 119 os.Stdout.Write(data) 120 outputLock.Unlock() 121 } 122 if vflag > 2 { 123 errprintf("run: %s DONE\n", strings.Join(cmd, " ")) 124 } 125 return string(data) 126 } 127 128 var maxbg = 4 /* maximum number of jobs to run at once */ 129 130 var ( 131 bgwork = make(chan func(), 1e5) 132 bgdone = make(chan struct{}, 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 slash = string(filepath.Separator) 397 398 gohostos = runtime.GOOS 399 switch gohostos { 400 case "darwin": 401 // Even on 64-bit platform, darwin uname -m prints i386. 402 if strings.Contains(run("", CheckExit, "sysctl", "machdep.cpu.extfeatures"), "EM64T") { 403 gohostarch = "amd64" 404 } 405 case "solaris": 406 // Even on 64-bit platform, solaris uname -m prints i86pc. 407 out := run("", CheckExit, "isainfo", "-n") 408 if strings.Contains(out, "amd64") { 409 gohostarch = "amd64" 410 } 411 if strings.Contains(out, "i386") { 412 gohostarch = "386" 413 } 414 case "plan9": 415 gohostarch = os.Getenv("objtype") 416 if gohostarch == "" { 417 fatal("$objtype is unset") 418 } 419 case "windows": 420 exe = ".exe" 421 } 422 423 sysinit() 424 425 if gohostarch == "" { 426 // Default Unix system. 427 out := run("", CheckExit, "uname", "-m") 428 switch { 429 case strings.Contains(out, "x86_64"), strings.Contains(out, "amd64"): 430 gohostarch = "amd64" 431 case strings.Contains(out, "86"): 432 gohostarch = "386" 433 case strings.Contains(out, "arm"): 434 gohostarch = "arm" 435 case strings.Contains(out, "aarch64"): 436 gohostarch = "arm64" 437 case strings.Contains(out, "ppc64le"): 438 gohostarch = "ppc64le" 439 case strings.Contains(out, "ppc64"): 440 gohostarch = "ppc64" 441 case gohostos == "darwin": 442 if strings.Contains(run("", CheckExit, "uname", "-v"), "RELEASE_ARM_") { 443 gohostarch = "arm" 444 } 445 default: 446 fatal("unknown architecture: %s", out) 447 } 448 } 449 450 if gohostarch == "arm" { 451 maxbg = min(maxbg, runtime.NumCPU()) 452 } 453 bginit() 454 455 // The OS X 10.6 linker does not support external linking mode. 456 // See golang.org/issue/5130. 457 // 458 // OS X 10.6 does not work with clang either, but OS X 10.9 requires it. 459 // It seems to work with OS X 10.8, so we default to clang for 10.8 and later. 460 // See golang.org/issue/5822. 461 // 462 // Roughly, OS X 10.N shows up as uname release (N+4), 463 // so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12. 464 if gohostos == "darwin" { 465 rel := run("", CheckExit, "uname", "-r") 466 if i := strings.Index(rel, "."); i >= 0 { 467 rel = rel[:i] 468 } 469 osx, _ := strconv.Atoi(rel) 470 if osx <= 6+4 { 471 goextlinkenabled = "0" 472 } 473 if osx >= 8+4 { 474 defaultclang = true 475 } 476 } 477 478 if len(os.Args) > 1 && os.Args[1] == "-check-goarm" { 479 useVFPv1() // might fail with SIGILL 480 println("VFPv1 OK.") 481 useVFPv3() // might fail with SIGILL 482 println("VFPv3 OK.") 483 os.Exit(0) 484 } 485 486 xinit() 487 xmain() 488 xexit(0) 489 } 490 491 // xsamefile reports whether f1 and f2 are the same file (or dir) 492 func xsamefile(f1, f2 string) bool { 493 fi1, err1 := os.Stat(f1) 494 fi2, err2 := os.Stat(f2) 495 if err1 != nil || err2 != nil { 496 return f1 == f2 497 } 498 return os.SameFile(fi1, fi2) 499 } 500 501 func xgetgoarm() string { 502 if goos == "nacl" { 503 // NaCl guarantees VFPv3 and is always cross-compiled. 504 return "7" 505 } 506 if goos == "darwin" { 507 // Assume all darwin/arm devices are have VFPv3. This 508 // port is also mostly cross-compiled, so it makes little 509 // sense to auto-detect the setting. 510 return "7" 511 } 512 if gohostarch != "arm" || goos != gohostos { 513 // Conservative default for cross-compilation. 514 return "5" 515 } 516 if goos == "freebsd" || goos == "openbsd" { 517 // FreeBSD has broken VFP support. 518 // OpenBSD currently only supports softfloat. 519 return "5" 520 } 521 522 // Try to exec ourselves in a mode to detect VFP support. 523 // Seeing how far it gets determines which instructions failed. 524 // The test is OS-agnostic. 525 out := run("", 0, os.Args[0], "-check-goarm") 526 v1ok := strings.Contains(out, "VFPv1 OK.") 527 v3ok := strings.Contains(out, "VFPv3 OK.") 528 529 if v1ok && v3ok { 530 return "7" 531 } 532 if v1ok { 533 return "6" 534 } 535 return "5" 536 } 537 538 func min(a, b int) int { 539 if a < b { 540 return a 541 } 542 return b 543 }