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 "sync/atomic" 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, run collects cmd's output and returns it as a string; 76 // otherwise, run prints cmd's output to standard output after the command finishes. 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 bgdied.Done() 113 } 114 fatal("FAILED: %v: %v", strings.Join(cmd, " "), err) 115 } 116 if mode&ShowOutput != 0 { 117 outputLock.Lock() 118 os.Stdout.Write(data) 119 outputLock.Unlock() 120 } 121 if vflag > 2 { 122 errprintf("run: %s DONE\n", strings.Join(cmd, " ")) 123 } 124 return string(data) 125 } 126 127 var maxbg = 4 /* maximum number of jobs to run at once */ 128 129 var ( 130 bgwork = make(chan func(), 1e5) 131 bgdone = make(chan struct{}, 1e5) 132 133 bgdied sync.WaitGroup 134 nwork int32 135 ndone int32 136 137 dying = make(chan bool) 138 nfatal int32 139 ) 140 141 func bginit() { 142 bgdied.Add(maxbg) 143 for i := 0; i < maxbg; i++ { 144 go bghelper() 145 } 146 } 147 148 func bghelper() { 149 for { 150 w := <-bgwork 151 w() 152 153 // Stop if we're dying. 154 if atomic.LoadInt32(&nfatal) > 0 { 155 bgdied.Done() 156 return 157 } 158 } 159 } 160 161 // bgrun is like run but runs the command in the background. 162 // CheckExit|ShowOutput mode is implied (since output cannot be returned). 163 func bgrun(dir string, cmd ...string) { 164 bgwork <- func() { 165 run(dir, CheckExit|ShowOutput|Background, cmd...) 166 } 167 } 168 169 // bgwait waits for pending bgruns to finish. 170 // bgwait must be called from only a single goroutine at a time. 171 func bgwait() { 172 var wg sync.WaitGroup 173 wg.Add(maxbg) 174 done := make(chan bool) 175 for i := 0; i < maxbg; i++ { 176 bgwork <- func() { 177 wg.Done() 178 179 // Hold up bg goroutine until either the wait finishes 180 // or the program starts dying due to a call to fatal. 181 select { 182 case <-dying: 183 case <-done: 184 } 185 } 186 } 187 wg.Wait() 188 close(done) 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 // Wait for background goroutines to finish, 359 // so that exit handler that removes the work directory 360 // is not fighting with active writes or open files. 361 if atomic.AddInt32(&nfatal, 1) == 1 { 362 close(dying) 363 } 364 for i := 0; i < maxbg; i++ { 365 bgwork <- func() {} // wake up workers so they notice nfatal > 0 366 } 367 bgdied.Wait() 368 369 xexit(2) 370 } 371 372 var atexits []func() 373 374 // xexit exits the process with return code n. 375 func xexit(n int) { 376 for i := len(atexits) - 1; i >= 0; i-- { 377 atexits[i]() 378 } 379 os.Exit(n) 380 } 381 382 // xatexit schedules the exit-handler f to be run when the program exits. 383 func xatexit(f func()) { 384 atexits = append(atexits, f) 385 } 386 387 // xprintf prints a message to standard output. 388 func xprintf(format string, args ...interface{}) { 389 fmt.Printf(format, args...) 390 } 391 392 // errprintf prints a message to standard output. 393 func errprintf(format string, args ...interface{}) { 394 fmt.Fprintf(os.Stderr, format, args...) 395 } 396 397 // main takes care of OS-specific startup and dispatches to xmain. 398 func main() { 399 os.Setenv("TERM", "dumb") // disable escape codes in clang errors 400 401 slash = string(filepath.Separator) 402 403 gohostos = runtime.GOOS 404 switch gohostos { 405 case "darwin": 406 // Even on 64-bit platform, darwin uname -m prints i386. 407 if strings.Contains(run("", CheckExit, "sysctl", "machdep.cpu.extfeatures"), "EM64T") { 408 gohostarch = "amd64" 409 } 410 case "solaris": 411 // Even on 64-bit platform, solaris uname -m prints i86pc. 412 out := run("", CheckExit, "isainfo", "-n") 413 if strings.Contains(out, "amd64") { 414 gohostarch = "amd64" 415 } 416 if strings.Contains(out, "i386") { 417 gohostarch = "386" 418 } 419 case "plan9": 420 gohostarch = os.Getenv("objtype") 421 if gohostarch == "" { 422 fatal("$objtype is unset") 423 } 424 case "windows": 425 exe = ".exe" 426 } 427 428 sysinit() 429 430 if gohostarch == "" { 431 // Default Unix system. 432 out := run("", CheckExit, "uname", "-m") 433 switch { 434 case strings.Contains(out, "x86_64"), strings.Contains(out, "amd64"): 435 gohostarch = "amd64" 436 case strings.Contains(out, "86"): 437 gohostarch = "386" 438 case strings.Contains(out, "arm"): 439 gohostarch = "arm" 440 case strings.Contains(out, "aarch64"): 441 gohostarch = "arm64" 442 case strings.Contains(out, "ppc64le"): 443 gohostarch = "ppc64le" 444 case strings.Contains(out, "ppc64"): 445 gohostarch = "ppc64" 446 case gohostos == "darwin": 447 if strings.Contains(run("", CheckExit, "uname", "-v"), "RELEASE_ARM_") { 448 gohostarch = "arm" 449 } 450 default: 451 fatal("unknown architecture: %s", out) 452 } 453 } 454 455 if gohostarch == "arm" { 456 maxbg = min(maxbg, runtime.NumCPU()) 457 } 458 bginit() 459 460 // The OS X 10.6 linker does not support external linking mode. 461 // See golang.org/issue/5130. 462 // 463 // OS X 10.6 does not work with clang either, but OS X 10.9 requires it. 464 // It seems to work with OS X 10.8, so we default to clang for 10.8 and later. 465 // See golang.org/issue/5822. 466 // 467 // Roughly, OS X 10.N shows up as uname release (N+4), 468 // so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12. 469 if gohostos == "darwin" { 470 rel := run("", CheckExit, "uname", "-r") 471 if i := strings.Index(rel, "."); i >= 0 { 472 rel = rel[:i] 473 } 474 osx, _ := strconv.Atoi(rel) 475 if osx <= 6+4 { 476 goextlinkenabled = "0" 477 } 478 if osx >= 8+4 { 479 defaultclang = true 480 } 481 } 482 483 xinit() 484 xmain() 485 xexit(0) 486 } 487 488 // xsamefile reports whether f1 and f2 are the same file (or dir) 489 func xsamefile(f1, f2 string) bool { 490 fi1, err1 := os.Stat(f1) 491 fi2, err2 := os.Stat(f2) 492 if err1 != nil || err2 != nil { 493 return f1 == f2 494 } 495 return os.SameFile(fi1, fi2) 496 } 497 498 func xgetgoarm() string { 499 if goos == "nacl" { 500 // NaCl guarantees VFPv3 and is always cross-compiled. 501 return "7" 502 } 503 if goos == "darwin" { 504 // Assume all darwin/arm devices are have VFPv3. This 505 // port is also mostly cross-compiled, so it makes little 506 // sense to auto-detect the setting. 507 return "7" 508 } 509 if gohostarch != "arm" || goos != gohostos { 510 // Conservative default for cross-compilation. 511 return "5" 512 } 513 if goos == "freebsd" || goos == "openbsd" { 514 // FreeBSD has broken VFP support. 515 // OpenBSD currently only supports softfloat. 516 return "5" 517 } 518 if goos != "linux" { 519 // All other arm platforms that we support 520 // require ARMv7. 521 return "7" 522 } 523 cpuinfo := readfile("/proc/cpuinfo") 524 goarm := "5" 525 for _, line := range splitlines(cpuinfo) { 526 line := strings.SplitN(line, ":", 2) 527 if len(line) < 2 { 528 continue 529 } 530 if strings.TrimSpace(line[0]) != "Features" { 531 continue 532 } 533 features := splitfields(line[1]) 534 sort.Strings(features) // so vfpv3 sorts after vfp 535 536 // Infer GOARM value from the vfp features available 537 // on this host. Values of GOARM detected are: 538 // 5: no vfp support was found 539 // 6: vfp (v1) support was detected, but no higher 540 // 7: vfpv3 support was detected. 541 // This matches the assertions in runtime.checkarm. 542 for _, f := range features { 543 switch f { 544 case "vfp": 545 goarm = "6" 546 case "vfpv3": 547 goarm = "7" 548 } 549 } 550 } 551 return goarm 552 } 553 554 func min(a, b int) int { 555 if a < b { 556 return a 557 } 558 return b 559 } 560