github.com/dannin/go@v0.0.0-20161031215817-d35dfd405eaa/src/cmd/dist/build.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 "encoding/json" 10 "flag" 11 "fmt" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "sort" 16 "strings" 17 "sync" 18 ) 19 20 // Initialization for any invocation. 21 22 // The usual variables. 23 var ( 24 goarch string 25 gobin string 26 gohostarch string 27 gohostos string 28 goos string 29 goarm string 30 go386 string 31 goroot string 32 goroot_final string 33 goextlinkenabled string 34 gogcflags string // For running built compiler 35 workdir string 36 tooldir string 37 oldgoos string 38 oldgoarch string 39 slash string 40 exe string 41 defaultcc string 42 defaultcflags string 43 defaultldflags string 44 defaultcxxtarget string 45 defaultcctarget string 46 defaultpkgconfigtarget string 47 rebuildall bool 48 defaultclang bool 49 50 vflag int // verbosity 51 ) 52 53 // The known architectures. 54 var okgoarch = []string{ 55 "386", 56 "amd64", 57 "amd64p32", 58 "arm", 59 "arm64", 60 "mips64", 61 "mips64le", 62 "ppc64", 63 "ppc64le", 64 "s390x", 65 } 66 67 // The known operating systems. 68 var okgoos = []string{ 69 "darwin", 70 "dragonfly", 71 "linux", 72 "android", 73 "solaris", 74 "freebsd", 75 "nacl", 76 "netbsd", 77 "openbsd", 78 "plan9", 79 "windows", 80 } 81 82 // find reports the first index of p in l[0:n], or else -1. 83 func find(p string, l []string) int { 84 for i, s := range l { 85 if p == s { 86 return i 87 } 88 } 89 return -1 90 } 91 92 // xinit handles initialization of the various global state, like goroot and goarch. 93 func xinit() { 94 goroot = os.Getenv("GOROOT") 95 if slash == "/" && len(goroot) > 1 || slash == `\` && len(goroot) > 3 { 96 // if not "/" or "c:\", then strip trailing path separator 97 goroot = strings.TrimSuffix(goroot, slash) 98 } 99 if goroot == "" { 100 fatal("$GOROOT must be set") 101 } 102 103 goroot_final = os.Getenv("GOROOT_FINAL") 104 if goroot_final == "" { 105 goroot_final = goroot 106 } 107 108 b := os.Getenv("GOBIN") 109 if b == "" { 110 b = goroot + slash + "bin" 111 } 112 gobin = b 113 114 b = os.Getenv("GOOS") 115 if b == "" { 116 b = gohostos 117 } 118 goos = b 119 if find(goos, okgoos) < 0 { 120 fatal("unknown $GOOS %s", goos) 121 } 122 123 b = os.Getenv("GOARM") 124 if b == "" { 125 b = xgetgoarm() 126 } 127 goarm = b 128 129 b = os.Getenv("GO386") 130 if b == "" { 131 if cansse2() { 132 b = "sse2" 133 } else { 134 b = "387" 135 } 136 } 137 go386 = b 138 139 p := pathf("%s/src/all.bash", goroot) 140 if !isfile(p) { 141 fatal("$GOROOT is not set correctly or not exported\n"+ 142 "\tGOROOT=%s\n"+ 143 "\t%s does not exist", goroot, p) 144 } 145 146 b = os.Getenv("GOHOSTARCH") 147 if b != "" { 148 gohostarch = b 149 } 150 151 if find(gohostarch, okgoarch) < 0 { 152 fatal("unknown $GOHOSTARCH %s", gohostarch) 153 } 154 155 b = os.Getenv("GOARCH") 156 if b == "" { 157 b = gohostarch 158 } 159 goarch = b 160 if find(goarch, okgoarch) < 0 { 161 fatal("unknown $GOARCH %s", goarch) 162 } 163 164 b = os.Getenv("GO_EXTLINK_ENABLED") 165 if b != "" { 166 if b != "0" && b != "1" { 167 fatal("unknown $GO_EXTLINK_ENABLED %s", b) 168 } 169 goextlinkenabled = b 170 } 171 172 gogcflags = os.Getenv("BOOT_GO_GCFLAGS") 173 174 b = os.Getenv("CC") 175 if b == "" { 176 // Use clang on OS X, because gcc is deprecated there. 177 // Xcode for OS X 10.9 Mavericks will ship a fake "gcc" binary that 178 // actually runs clang. We prepare different command 179 // lines for the two binaries, so it matters what we call it. 180 // See golang.org/issue/5822. 181 if defaultclang { 182 b = "clang" 183 } else { 184 b = "gcc" 185 } 186 } 187 defaultcc = b 188 189 defaultcflags = os.Getenv("CFLAGS") 190 191 defaultldflags = os.Getenv("LDFLAGS") 192 193 b = os.Getenv("CC_FOR_TARGET") 194 if b == "" { 195 b = defaultcc 196 } 197 defaultcctarget = b 198 199 b = os.Getenv("CXX_FOR_TARGET") 200 if b == "" { 201 b = os.Getenv("CXX") 202 if b == "" { 203 if defaultclang { 204 b = "clang++" 205 } else { 206 b = "g++" 207 } 208 } 209 } 210 defaultcxxtarget = b 211 212 b = os.Getenv("PKG_CONFIG") 213 if b == "" { 214 b = "pkg-config" 215 } 216 defaultpkgconfigtarget = b 217 218 // For tools being invoked but also for os.ExpandEnv. 219 os.Setenv("GO386", go386) 220 os.Setenv("GOARCH", goarch) 221 os.Setenv("GOARM", goarm) 222 os.Setenv("GOHOSTARCH", gohostarch) 223 os.Setenv("GOHOSTOS", gohostos) 224 os.Setenv("GOOS", goos) 225 os.Setenv("GOROOT", goroot) 226 os.Setenv("GOROOT_FINAL", goroot_final) 227 228 // Make the environment more predictable. 229 os.Setenv("LANG", "C") 230 os.Setenv("LANGUAGE", "en_US.UTF8") 231 232 workdir = xworkdir() 233 xatexit(rmworkdir) 234 235 tooldir = pathf("%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch) 236 } 237 238 // rmworkdir deletes the work directory. 239 func rmworkdir() { 240 if vflag > 1 { 241 errprintf("rm -rf %s\n", workdir) 242 } 243 xremoveall(workdir) 244 } 245 246 // Remove trailing spaces. 247 func chomp(s string) string { 248 return strings.TrimRight(s, " \t\r\n") 249 } 250 251 func branchtag(branch string) (tag string, precise bool) { 252 b := run(goroot, CheckExit, "git", "log", "--decorate=full", "--format=format:%d", "master.."+branch) 253 tag = branch 254 for _, line := range splitlines(b) { 255 // Each line is either blank, or looks like 256 // (tag: refs/tags/go1.4rc2, refs/remotes/origin/release-branch.go1.4, refs/heads/release-branch.go1.4) 257 // We need to find an element starting with refs/tags/. 258 i := strings.Index(line, " refs/tags/") 259 if i < 0 { 260 continue 261 } 262 i += len(" refs/tags/") 263 // The tag name ends at a comma or paren (prefer the first). 264 j := strings.Index(line[i:], ",") 265 if j < 0 { 266 j = strings.Index(line[i:], ")") 267 } 268 if j < 0 { 269 continue // malformed line; ignore it 270 } 271 tag = line[i : i+j] 272 if i == 0 { 273 precise = true // tag denotes HEAD 274 } 275 break 276 } 277 return 278 } 279 280 // findgoversion determines the Go version to use in the version string. 281 func findgoversion() string { 282 // The $GOROOT/VERSION file takes priority, for distributions 283 // without the source repo. 284 path := pathf("%s/VERSION", goroot) 285 if isfile(path) { 286 b := chomp(readfile(path)) 287 // Commands such as "dist version > VERSION" will cause 288 // the shell to create an empty VERSION file and set dist's 289 // stdout to its fd. dist in turn looks at VERSION and uses 290 // its content if available, which is empty at this point. 291 // Only use the VERSION file if it is non-empty. 292 if b != "" { 293 return b 294 } 295 } 296 297 // The $GOROOT/VERSION.cache file is a cache to avoid invoking 298 // git every time we run this command. Unlike VERSION, it gets 299 // deleted by the clean command. 300 path = pathf("%s/VERSION.cache", goroot) 301 if isfile(path) { 302 return chomp(readfile(path)) 303 } 304 305 // Show a nicer error message if this isn't a Git repo. 306 if !isGitRepo() { 307 fatal("FAILED: not a Git repo; must put a VERSION file in $GOROOT") 308 } 309 310 // Otherwise, use Git. 311 // What is the current branch? 312 branch := chomp(run(goroot, CheckExit, "git", "rev-parse", "--abbrev-ref", "HEAD")) 313 314 // What are the tags along the current branch? 315 tag := "devel" 316 precise := false 317 318 // If we're on a release branch, use the closest matching tag 319 // that is on the release branch (and not on the master branch). 320 if strings.HasPrefix(branch, "release-branch.") { 321 tag, precise = branchtag(branch) 322 } 323 324 if !precise { 325 // Tag does not point at HEAD; add hash and date to version. 326 tag += chomp(run(goroot, CheckExit, "git", "log", "-n", "1", "--format=format: +%h %cd", "HEAD")) 327 } 328 329 // Cache version. 330 writefile(tag, path, 0) 331 332 return tag 333 } 334 335 // isGitRepo reports whether the working directory is inside a Git repository. 336 func isGitRepo() bool { 337 // NB: simply checking the exit code of `git rev-parse --git-dir` would 338 // suffice here, but that requires deviating from the infrastructure 339 // provided by `run`. 340 gitDir := chomp(run(goroot, 0, "git", "rev-parse", "--git-dir")) 341 if !filepath.IsAbs(gitDir) { 342 gitDir = filepath.Join(goroot, gitDir) 343 } 344 fi, err := os.Stat(gitDir) 345 return err == nil && fi.IsDir() 346 } 347 348 /* 349 * Initial tree setup. 350 */ 351 352 // The old tools that no longer live in $GOBIN or $GOROOT/bin. 353 var oldtool = []string{ 354 "5a", "5c", "5g", "5l", 355 "6a", "6c", "6g", "6l", 356 "8a", "8c", "8g", "8l", 357 "9a", "9c", "9g", "9l", 358 "6cov", 359 "6nm", 360 "6prof", 361 "cgo", 362 "ebnflint", 363 "goapi", 364 "gofix", 365 "goinstall", 366 "gomake", 367 "gopack", 368 "gopprof", 369 "gotest", 370 "gotype", 371 "govet", 372 "goyacc", 373 "quietgcc", 374 } 375 376 // Unreleased directories (relative to $GOROOT) that should 377 // not be in release branches. 378 var unreleased = []string{ 379 "src/cmd/newlink", 380 "src/cmd/objwriter", 381 "src/debug/goobj", 382 "src/old", 383 } 384 385 // setup sets up the tree for the initial build. 386 func setup() { 387 // Create bin directory. 388 if p := pathf("%s/bin", goroot); !isdir(p) { 389 xmkdir(p) 390 } 391 392 // Create package directory. 393 if p := pathf("%s/pkg", goroot); !isdir(p) { 394 xmkdir(p) 395 } 396 397 p := pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch) 398 if rebuildall { 399 xremoveall(p) 400 } 401 xmkdirall(p) 402 403 if goos != gohostos || goarch != gohostarch { 404 p := pathf("%s/pkg/%s_%s", goroot, goos, goarch) 405 if rebuildall { 406 xremoveall(p) 407 } 408 xmkdirall(p) 409 } 410 411 // Create object directory. 412 // We keep it in pkg/ so that all the generated binaries 413 // are in one tree. If pkg/obj/libgc.a exists, it is a dreg from 414 // before we used subdirectories of obj. Delete all of obj 415 // to clean up. 416 if p := pathf("%s/pkg/obj/libgc.a", goroot); isfile(p) { 417 xremoveall(pathf("%s/pkg/obj", goroot)) 418 } 419 p = pathf("%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch) 420 if rebuildall { 421 xremoveall(p) 422 } 423 xmkdirall(p) 424 425 // Create tool directory. 426 // We keep it in pkg/, just like the object directory above. 427 if rebuildall { 428 xremoveall(tooldir) 429 } 430 xmkdirall(tooldir) 431 432 // Remove tool binaries from before the tool/gohostos_gohostarch 433 xremoveall(pathf("%s/bin/tool", goroot)) 434 435 // Remove old pre-tool binaries. 436 for _, old := range oldtool { 437 xremove(pathf("%s/bin/%s", goroot, old)) 438 } 439 440 // If $GOBIN is set and has a Go compiler, it must be cleaned. 441 for _, char := range "56789" { 442 if isfile(pathf("%s%s%c%s", gobin, slash, char, "g")) { 443 for _, old := range oldtool { 444 xremove(pathf("%s/%s", gobin, old)) 445 } 446 break 447 } 448 } 449 450 // For release, make sure excluded things are excluded. 451 goversion := findgoversion() 452 if strings.HasPrefix(goversion, "release.") || (strings.HasPrefix(goversion, "go") && !strings.Contains(goversion, "beta")) { 453 for _, dir := range unreleased { 454 if p := pathf("%s/%s", goroot, dir); isdir(p) { 455 fatal("%s should not exist in release build", p) 456 } 457 } 458 } 459 } 460 461 /* 462 * Tool building 463 */ 464 465 // deptab lists changes to the default dependencies for a given prefix. 466 // deps ending in /* read the whole directory; deps beginning with - 467 // exclude files with that prefix. 468 var deptab = []struct { 469 prefix string // prefix of target 470 dep []string // dependency tweaks for targets with that prefix 471 }{ 472 {"cmd/go", []string{ 473 "zdefaultcc.go", 474 "zosarch.go", 475 }}, 476 {"runtime/internal/sys", []string{ 477 "zversion.go", 478 }}, 479 {"go/build", []string{ 480 "zcgo.go", 481 }}, 482 } 483 484 // depsuffix records the allowed suffixes for source files. 485 var depsuffix = []string{ 486 ".s", 487 ".go", 488 } 489 490 // gentab records how to generate some trivial files. 491 var gentab = []struct { 492 nameprefix string 493 gen func(string, string) 494 }{ 495 {"zdefaultcc.go", mkzdefaultcc}, 496 {"zosarch.go", mkzosarch}, 497 {"zversion.go", mkzversion}, 498 {"zcgo.go", mkzcgo}, 499 500 // not generated anymore, but delete the file if we see it 501 {"enam.c", nil}, 502 {"anames5.c", nil}, 503 {"anames6.c", nil}, 504 {"anames8.c", nil}, 505 {"anames9.c", nil}, 506 } 507 508 // installed maps from a dir name (as given to install) to a chan 509 // closed when the dir's package is installed. 510 var installed = make(map[string]chan struct{}) 511 512 // install installs the library, package, or binary associated with dir, 513 // which is relative to $GOROOT/src. 514 func install(dir string) { 515 if ch, ok := installed[dir]; ok { 516 defer close(ch) 517 } 518 for _, dep := range builddeps[dir] { 519 <-installed[dep] 520 } 521 522 if vflag > 0 { 523 if goos != gohostos || goarch != gohostarch { 524 errprintf("%s (%s/%s)\n", dir, goos, goarch) 525 } else { 526 errprintf("%s\n", dir) 527 } 528 } 529 530 workdir := pathf("%s/%s", workdir, dir) 531 xmkdirall(workdir) 532 533 var clean []string 534 defer func() { 535 for _, name := range clean { 536 xremove(name) 537 } 538 }() 539 540 // path = full path to dir. 541 path := pathf("%s/src/%s", goroot, dir) 542 name := filepath.Base(dir) 543 544 ispkg := !strings.HasPrefix(dir, "cmd/") || strings.HasPrefix(dir, "cmd/internal/") || strings.HasPrefix(dir, "cmd/asm/internal/") 545 546 // Start final link command line. 547 // Note: code below knows that link.p[targ] is the target. 548 var ( 549 link []string 550 targ int 551 ispackcmd bool 552 ) 553 if ispkg { 554 // Go library (package). 555 ispackcmd = true 556 link = []string{"pack", pathf("%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir)} 557 targ = len(link) - 1 558 xmkdirall(filepath.Dir(link[targ])) 559 } else { 560 // Go command. 561 elem := name 562 if elem == "go" { 563 elem = "go_bootstrap" 564 } 565 link = []string{pathf("%s/link", tooldir), "-o", pathf("%s/%s%s", tooldir, elem, exe)} 566 targ = len(link) - 1 567 } 568 ttarg := mtime(link[targ]) 569 570 // Gather files that are sources for this target. 571 // Everything in that directory, and any target-specific 572 // additions. 573 files := xreaddir(path) 574 575 // Remove files beginning with . or _, 576 // which are likely to be editor temporary files. 577 // This is the same heuristic build.ScanDir uses. 578 // There do exist real C files beginning with _, 579 // so limit that check to just Go files. 580 files = filter(files, func(p string) bool { 581 return !strings.HasPrefix(p, ".") && (!strings.HasPrefix(p, "_") || !strings.HasSuffix(p, ".go")) 582 }) 583 584 for _, dt := range deptab { 585 if dir == dt.prefix || strings.HasSuffix(dt.prefix, "/") && strings.HasPrefix(dir, dt.prefix) { 586 for _, p := range dt.dep { 587 p = os.ExpandEnv(p) 588 files = append(files, p) 589 } 590 } 591 } 592 files = uniq(files) 593 594 // Convert to absolute paths. 595 for i, p := range files { 596 if !isabs(p) { 597 files[i] = pathf("%s/%s", path, p) 598 } 599 } 600 601 // Is the target up-to-date? 602 var gofiles, missing []string 603 stale := rebuildall 604 files = filter(files, func(p string) bool { 605 for _, suf := range depsuffix { 606 if strings.HasSuffix(p, suf) { 607 goto ok 608 } 609 } 610 return false 611 ok: 612 t := mtime(p) 613 if !t.IsZero() && !strings.HasSuffix(p, ".a") && !shouldbuild(p, dir) { 614 return false 615 } 616 if strings.HasSuffix(p, ".go") { 617 gofiles = append(gofiles, p) 618 } 619 if t.After(ttarg) { 620 stale = true 621 } 622 if t.IsZero() { 623 missing = append(missing, p) 624 } 625 return true 626 }) 627 628 // If there are no files to compile, we're done. 629 if len(files) == 0 { 630 return 631 } 632 633 if !stale { 634 return 635 } 636 637 // For package runtime, copy some files into the work space. 638 if dir == "runtime" || strings.HasPrefix(dir, "runtime/internal/") { 639 xmkdirall(pathf("%s/pkg/include", goroot)) 640 // For use by assembly and C files. 641 copyfile(pathf("%s/pkg/include/textflag.h", goroot), 642 pathf("%s/src/runtime/textflag.h", goroot), 0) 643 copyfile(pathf("%s/pkg/include/funcdata.h", goroot), 644 pathf("%s/src/runtime/funcdata.h", goroot), 0) 645 copyfile(pathf("%s/pkg/include/asm_ppc64x.h", goroot), 646 pathf("%s/src/runtime/asm_ppc64x.h", goroot), 0) 647 } 648 649 // Generate any missing files; regenerate existing ones. 650 for _, p := range files { 651 elem := filepath.Base(p) 652 for _, gt := range gentab { 653 if gt.gen == nil { 654 continue 655 } 656 if strings.HasPrefix(elem, gt.nameprefix) { 657 if vflag > 1 { 658 errprintf("generate %s\n", p) 659 } 660 gt.gen(path, p) 661 // Do not add generated file to clean list. 662 // In runtime, we want to be able to 663 // build the package with the go tool, 664 // and it assumes these generated files already 665 // exist (it does not know how to build them). 666 // The 'clean' command can remove 667 // the generated files. 668 goto built 669 } 670 } 671 // Did not rebuild p. 672 if find(p, missing) >= 0 { 673 fatal("missing file %s", p) 674 } 675 built: 676 } 677 678 if goos != gohostos || goarch != gohostarch { 679 // We've generated the right files; the go command can do the build. 680 if vflag > 1 { 681 errprintf("skip build for cross-compile %s\n", dir) 682 } 683 return 684 } 685 686 var archive string 687 // The next loop will compile individual non-Go files. 688 // Hand the Go files to the compiler en masse. 689 // For package runtime, this writes go_asm.h, which 690 // the assembly files will need. 691 pkg := dir 692 if strings.HasPrefix(dir, "cmd/") { 693 pkg = "main" 694 } 695 b := pathf("%s/_go_.a", workdir) 696 clean = append(clean, b) 697 if !ispackcmd { 698 link = append(link, b) 699 } else { 700 archive = b 701 } 702 compile := []string{pathf("%s/compile", tooldir), "-pack", "-o", b, "-p", pkg} 703 if gogcflags != "" { 704 compile = append(compile, strings.Fields(gogcflags)...) 705 } 706 if dir == "runtime" { 707 compile = append(compile, "-+", "-asmhdr", pathf("%s/go_asm.h", workdir)) 708 } 709 compile = append(compile, gofiles...) 710 run(path, CheckExit|ShowOutput, compile...) 711 712 // Compile the files. 713 var wg sync.WaitGroup 714 for _, p := range files { 715 if !strings.HasSuffix(p, ".s") { 716 continue 717 } 718 719 var compile []string 720 // Assembly file for a Go package. 721 compile = []string{ 722 pathf("%s/asm", tooldir), 723 "-I", workdir, 724 "-I", pathf("%s/pkg/include", goroot), 725 "-D", "GOOS_" + goos, 726 "-D", "GOARCH_" + goarch, 727 "-D", "GOOS_GOARCH_" + goos + "_" + goarch, 728 } 729 730 doclean := true 731 b := pathf("%s/%s", workdir, filepath.Base(p)) 732 733 // Change the last character of the output file (which was c or s). 734 b = b[:len(b)-1] + "o" 735 compile = append(compile, "-o", b, p) 736 bgrun(&wg, path, compile...) 737 738 link = append(link, b) 739 if doclean { 740 clean = append(clean, b) 741 } 742 } 743 bgwait(&wg) 744 745 if ispackcmd { 746 xremove(link[targ]) 747 dopack(link[targ], archive, link[targ+1:]) 748 return 749 } 750 751 // Remove target before writing it. 752 xremove(link[targ]) 753 run("", CheckExit|ShowOutput, link...) 754 } 755 756 // matchfield reports whether the field (x,y,z) matches this build. 757 // all the elements in the field must be satisfied. 758 func matchfield(f string) bool { 759 for _, tag := range strings.Split(f, ",") { 760 if !matchtag(tag) { 761 return false 762 } 763 } 764 return true 765 } 766 767 // matchtag reports whether the tag (x or !x) matches this build. 768 func matchtag(tag string) bool { 769 if tag == "" { 770 return false 771 } 772 if tag[0] == '!' { 773 if len(tag) == 1 || tag[1] == '!' { 774 return false 775 } 776 return !matchtag(tag[1:]) 777 } 778 return tag == "gc" || tag == goos || tag == goarch || tag == "cmd_go_bootstrap" || tag == "go1.1" || (goos == "android" && tag == "linux") 779 } 780 781 // shouldbuild reports whether we should build this file. 782 // It applies the same rules that are used with context tags 783 // in package go/build, except that the GOOS and GOARCH 784 // can appear anywhere in the file name, not just after _. 785 // In particular, they can be the entire file name (like windows.c). 786 // We also allow the special tag cmd_go_bootstrap. 787 // See ../go/bootstrap.go and package go/build. 788 func shouldbuild(file, dir string) bool { 789 // Check file name for GOOS or GOARCH. 790 name := filepath.Base(file) 791 excluded := func(list []string, ok string) bool { 792 for _, x := range list { 793 if x == ok { 794 continue 795 } 796 i := strings.Index(name, x) 797 if i < 0 { 798 continue 799 } 800 i += len(x) 801 if i == len(name) || name[i] == '.' || name[i] == '_' { 802 return true 803 } 804 } 805 return false 806 } 807 if excluded(okgoos, goos) || excluded(okgoarch, goarch) { 808 return false 809 } 810 811 // Omit test files. 812 if strings.Contains(name, "_test") { 813 return false 814 } 815 816 // Check file contents for // +build lines. 817 for _, p := range splitlines(readfile(file)) { 818 p = strings.TrimSpace(p) 819 if p == "" { 820 continue 821 } 822 code := p 823 i := strings.Index(code, "//") 824 if i > 0 { 825 code = strings.TrimSpace(code[:i]) 826 } 827 if code == "package documentation" { 828 return false 829 } 830 if code == "package main" && dir != "cmd/go" && dir != "cmd/cgo" { 831 return false 832 } 833 if !strings.HasPrefix(p, "//") { 834 break 835 } 836 if !strings.Contains(p, "+build") { 837 continue 838 } 839 fields := splitfields(p[2:]) 840 if len(fields) < 1 || fields[0] != "+build" { 841 continue 842 } 843 for _, p := range fields[1:] { 844 if matchfield(p) { 845 goto fieldmatch 846 } 847 } 848 return false 849 fieldmatch: 850 } 851 852 return true 853 } 854 855 // copy copies the file src to dst, via memory (so only good for small files). 856 func copyfile(dst, src string, flag int) { 857 if vflag > 1 { 858 errprintf("cp %s %s\n", src, dst) 859 } 860 writefile(readfile(src), dst, flag) 861 } 862 863 // dopack copies the package src to dst, 864 // appending the files listed in extra. 865 // The archive format is the traditional Unix ar format. 866 func dopack(dst, src string, extra []string) { 867 bdst := bytes.NewBufferString(readfile(src)) 868 for _, file := range extra { 869 b := readfile(file) 870 // find last path element for archive member name 871 i := strings.LastIndex(file, "/") + 1 872 j := strings.LastIndex(file, `\`) + 1 873 if i < j { 874 i = j 875 } 876 fmt.Fprintf(bdst, "%-16.16s%-12d%-6d%-6d%-8o%-10d`\n", file[i:], 0, 0, 0, 0644, len(b)) 877 bdst.WriteString(b) 878 if len(b)&1 != 0 { 879 bdst.WriteByte(0) 880 } 881 } 882 writefile(bdst.String(), dst, 0) 883 } 884 885 // builddeps records the build dependencies for the 'go bootstrap' command. 886 // It is a map[string][]string and generated by mkdeps.bash into deps.go. 887 888 // buildlist is the list of directories being built, sorted by name. 889 var buildlist = makeBuildlist() 890 891 func makeBuildlist() []string { 892 var all []string 893 for dir := range builddeps { 894 all = append(all, dir) 895 } 896 sort.Strings(all) 897 return all 898 } 899 900 var runtimegen = []string{ 901 "zaexperiment.h", 902 "zversion.go", 903 } 904 905 func clean() { 906 for _, name := range buildlist { 907 path := pathf("%s/src/%s", goroot, name) 908 // Remove generated files. 909 for _, elem := range xreaddir(path) { 910 for _, gt := range gentab { 911 if strings.HasPrefix(elem, gt.nameprefix) { 912 xremove(pathf("%s/%s", path, elem)) 913 } 914 } 915 } 916 // Remove generated binary named for directory. 917 if strings.HasPrefix(name, "cmd/") { 918 xremove(pathf("%s/%s", path, name[4:])) 919 } 920 } 921 922 // remove runtimegen files. 923 path := pathf("%s/src/runtime", goroot) 924 for _, elem := range runtimegen { 925 xremove(pathf("%s/%s", path, elem)) 926 } 927 928 if rebuildall { 929 // Remove object tree. 930 xremoveall(pathf("%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch)) 931 932 // Remove installed packages and tools. 933 xremoveall(pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch)) 934 xremoveall(pathf("%s/pkg/%s_%s", goroot, goos, goarch)) 935 xremoveall(pathf("%s/pkg/%s_%s_race", goroot, gohostos, gohostarch)) 936 xremoveall(pathf("%s/pkg/%s_%s_race", goroot, goos, goarch)) 937 xremoveall(tooldir) 938 939 // Remove cached version info. 940 xremove(pathf("%s/VERSION.cache", goroot)) 941 } 942 } 943 944 /* 945 * command implementations 946 */ 947 948 func usage() { 949 xprintf("usage: go tool dist [command]\n" + 950 "Commands are:\n" + 951 "\n" + 952 "banner print installation banner\n" + 953 "bootstrap rebuild everything\n" + 954 "clean deletes all built files\n" + 955 "env [-p] print environment (-p: include $PATH)\n" + 956 "install [dir] install individual directory\n" + 957 "list [-json] list all supported platforms\n" + 958 "test [-h] run Go test(s)\n" + 959 "version print Go version\n" + 960 "\n" + 961 "All commands take -v flags to emit extra information.\n", 962 ) 963 xexit(2) 964 } 965 966 // The env command prints the default environment. 967 func cmdenv() { 968 path := flag.Bool("p", false, "emit updated PATH") 969 plan9 := flag.Bool("9", false, "emit plan 9 syntax") 970 windows := flag.Bool("w", false, "emit windows syntax") 971 xflagparse(0) 972 973 format := "%s=\"%s\"\n" 974 switch { 975 case *plan9: 976 format = "%s='%s'\n" 977 case *windows: 978 format = "set %s=%s\r\n" 979 } 980 981 xprintf(format, "CC", defaultcc) 982 xprintf(format, "CC_FOR_TARGET", defaultcctarget) 983 xprintf(format, "GOROOT", goroot) 984 xprintf(format, "GOBIN", gobin) 985 xprintf(format, "GOARCH", goarch) 986 xprintf(format, "GOOS", goos) 987 xprintf(format, "GOHOSTARCH", gohostarch) 988 xprintf(format, "GOHOSTOS", gohostos) 989 xprintf(format, "GOTOOLDIR", tooldir) 990 if goarch == "arm" { 991 xprintf(format, "GOARM", goarm) 992 } 993 if goarch == "386" { 994 xprintf(format, "GO386", go386) 995 } 996 997 if *path { 998 sep := ":" 999 if gohostos == "windows" { 1000 sep = ";" 1001 } 1002 xprintf(format, "PATH", fmt.Sprintf("%s%s%s", gobin, sep, os.Getenv("PATH"))) 1003 } 1004 } 1005 1006 // The bootstrap command runs a build from scratch, 1007 // stopping at having installed the go_bootstrap command. 1008 func cmdbootstrap() { 1009 flag.BoolVar(&rebuildall, "a", rebuildall, "rebuild all") 1010 xflagparse(0) 1011 1012 if isdir(pathf("%s/src/pkg", goroot)) { 1013 fatal("\n\n"+ 1014 "The Go package sources have moved to $GOROOT/src.\n"+ 1015 "*** %s still exists. ***\n"+ 1016 "It probably contains stale files that may confuse the build.\n"+ 1017 "Please (check what's there and) remove it and try again.\n"+ 1018 "See https://golang.org/s/go14nopkg\n", 1019 pathf("%s/src/pkg", goroot)) 1020 } 1021 1022 if rebuildall { 1023 clean() 1024 } 1025 1026 setup() 1027 1028 checkCC() 1029 bootstrapBuildTools() 1030 1031 // For the main bootstrap, building for host os/arch. 1032 oldgoos = goos 1033 oldgoarch = goarch 1034 goos = gohostos 1035 goarch = gohostarch 1036 os.Setenv("GOHOSTARCH", gohostarch) 1037 os.Setenv("GOHOSTOS", gohostos) 1038 os.Setenv("GOARCH", goarch) 1039 os.Setenv("GOOS", goos) 1040 1041 // TODO(rsc): Enable when appropriate. 1042 // This step is only needed if we believe that the Go compiler built from Go 1.4 1043 // will produce different object files than the Go compiler built from itself. 1044 // In the absence of bugs, that should not happen. 1045 // And if there are bugs, they're more likely in the current development tree 1046 // than in a standard release like Go 1.4, so don't do this rebuild by default. 1047 if false { 1048 xprintf("##### Building Go toolchain using itself.\n") 1049 for _, dir := range buildlist { 1050 installed[dir] = make(chan struct{}) 1051 } 1052 var wg sync.WaitGroup 1053 for _, dir := range builddeps["cmd/go"] { 1054 wg.Add(1) 1055 dir := dir 1056 go func() { 1057 defer wg.Done() 1058 install(dir) 1059 }() 1060 } 1061 wg.Wait() 1062 xprintf("\n") 1063 } 1064 1065 xprintf("##### Building go_bootstrap for host, %s/%s.\n", gohostos, gohostarch) 1066 for _, dir := range buildlist { 1067 installed[dir] = make(chan struct{}) 1068 } 1069 for _, dir := range buildlist { 1070 go install(dir) 1071 } 1072 <-installed["cmd/go"] 1073 1074 goos = oldgoos 1075 goarch = oldgoarch 1076 os.Setenv("GOARCH", goarch) 1077 os.Setenv("GOOS", goos) 1078 1079 // Build runtime for actual goos/goarch too. 1080 if goos != gohostos || goarch != gohostarch { 1081 installed["runtime"] = make(chan struct{}) 1082 install("runtime") 1083 } 1084 } 1085 1086 // Cannot use go/build directly because cmd/dist for a new release 1087 // builds against an old release's go/build, which may be out of sync. 1088 // To reduce duplication, we generate the list for go/build from this. 1089 // 1090 // We list all supported platforms in this list, so that this is the 1091 // single point of truth for supported platforms. This list is used 1092 // by 'go tool dist list'. 1093 var cgoEnabled = map[string]bool{ 1094 "darwin/386": true, 1095 "darwin/amd64": true, 1096 "darwin/arm": true, 1097 "darwin/arm64": true, 1098 "dragonfly/amd64": true, 1099 "freebsd/386": true, 1100 "freebsd/amd64": true, 1101 "freebsd/arm": false, 1102 "linux/386": true, 1103 "linux/amd64": true, 1104 "linux/arm": true, 1105 "linux/arm64": true, 1106 "linux/ppc64": false, 1107 "linux/ppc64le": true, 1108 "linux/mips64": true, 1109 "linux/mips64le": true, 1110 "linux/s390x": true, 1111 "android/386": true, 1112 "android/amd64": true, 1113 "android/arm": true, 1114 "android/arm64": true, 1115 "nacl/386": false, 1116 "nacl/amd64p32": false, 1117 "nacl/arm": false, 1118 "netbsd/386": true, 1119 "netbsd/amd64": true, 1120 "netbsd/arm": true, 1121 "openbsd/386": true, 1122 "openbsd/amd64": true, 1123 "openbsd/arm": false, 1124 "plan9/386": false, 1125 "plan9/amd64": false, 1126 "plan9/arm": false, 1127 "solaris/amd64": true, 1128 "windows/386": true, 1129 "windows/amd64": true, 1130 } 1131 1132 func needCC() bool { 1133 switch os.Getenv("CGO_ENABLED") { 1134 case "1": 1135 return true 1136 case "0": 1137 return false 1138 } 1139 return cgoEnabled[gohostos+"/"+gohostarch] 1140 } 1141 1142 func checkCC() { 1143 if !needCC() { 1144 return 1145 } 1146 if output, err := exec.Command(defaultcc, "--help").CombinedOutput(); err != nil { 1147 outputHdr := "" 1148 if len(output) > 0 { 1149 outputHdr = "\nCommand output:\n\n" 1150 } 1151 fatal("cannot invoke C compiler %q: %v\n\n"+ 1152 "Go needs a system C compiler for use with cgo.\n"+ 1153 "To set a C compiler, set CC=the-compiler.\n"+ 1154 "To disable cgo, set CGO_ENABLED=0.\n%s%s", defaultcc, err, outputHdr, output) 1155 } 1156 } 1157 1158 func defaulttarg() string { 1159 // xgetwd might return a path with symlinks fully resolved, and if 1160 // there happens to be symlinks in goroot, then the hasprefix test 1161 // will never succeed. Instead, we use xrealwd to get a canonical 1162 // goroot/src before the comparison to avoid this problem. 1163 pwd := xgetwd() 1164 src := pathf("%s/src/", goroot) 1165 real_src := xrealwd(src) 1166 if !strings.HasPrefix(pwd, real_src) { 1167 fatal("current directory %s is not under %s", pwd, real_src) 1168 } 1169 pwd = pwd[len(real_src):] 1170 // guard against xrealwd returning the directory without the trailing / 1171 pwd = strings.TrimPrefix(pwd, "/") 1172 1173 return pwd 1174 } 1175 1176 // Install installs the list of packages named on the command line. 1177 func cmdinstall() { 1178 xflagparse(-1) 1179 1180 if flag.NArg() == 0 { 1181 install(defaulttarg()) 1182 } 1183 1184 for _, arg := range flag.Args() { 1185 install(arg) 1186 } 1187 } 1188 1189 // Clean deletes temporary objects. 1190 func cmdclean() { 1191 xflagparse(0) 1192 clean() 1193 } 1194 1195 // Banner prints the 'now you've installed Go' banner. 1196 func cmdbanner() { 1197 xflagparse(0) 1198 1199 xprintf("\n") 1200 xprintf("---\n") 1201 xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot) 1202 xprintf("Installed commands in %s\n", gobin) 1203 1204 if !xsamefile(goroot_final, goroot) { 1205 // If the files are to be moved, don't check that gobin 1206 // is on PATH; assume they know what they are doing. 1207 } else if gohostos == "plan9" { 1208 // Check that gobin is bound before /bin. 1209 pid := strings.Replace(readfile("#c/pid"), " ", "", -1) 1210 ns := fmt.Sprintf("/proc/%s/ns", pid) 1211 if !strings.Contains(readfile(ns), fmt.Sprintf("bind -b %s /bin", gobin)) { 1212 xprintf("*** You need to bind %s before /bin.\n", gobin) 1213 } 1214 } else { 1215 // Check that gobin appears in $PATH. 1216 pathsep := ":" 1217 if gohostos == "windows" { 1218 pathsep = ";" 1219 } 1220 if !strings.Contains(pathsep+os.Getenv("PATH")+pathsep, pathsep+gobin+pathsep) { 1221 xprintf("*** You need to add %s to your PATH.\n", gobin) 1222 } 1223 } 1224 1225 if !xsamefile(goroot_final, goroot) { 1226 xprintf("\n"+ 1227 "The binaries expect %s to be copied or moved to %s\n", 1228 goroot, goroot_final) 1229 } 1230 } 1231 1232 // Version prints the Go version. 1233 func cmdversion() { 1234 xflagparse(0) 1235 xprintf("%s\n", findgoversion()) 1236 } 1237 1238 // cmdlist lists all supported platforms. 1239 func cmdlist() { 1240 jsonFlag := flag.Bool("json", false, "produce JSON output") 1241 xflagparse(0) 1242 1243 var plats []string 1244 for p := range cgoEnabled { 1245 plats = append(plats, p) 1246 } 1247 sort.Strings(plats) 1248 1249 if !*jsonFlag { 1250 for _, p := range plats { 1251 xprintf("%s\n", p) 1252 } 1253 return 1254 } 1255 1256 type jsonResult struct { 1257 GOOS string 1258 GOARCH string 1259 CgoSupported bool 1260 } 1261 var results []jsonResult 1262 for _, p := range plats { 1263 fields := strings.Split(p, "/") 1264 results = append(results, jsonResult{ 1265 GOOS: fields[0], 1266 GOARCH: fields[1], 1267 CgoSupported: cgoEnabled[p]}) 1268 } 1269 out, err := json.MarshalIndent(results, "", "\t") 1270 if err != nil { 1271 fatal("json marshal error: %v", err) 1272 } 1273 if _, err := os.Stdout.Write(out); err != nil { 1274 fatal("write failed: %v", err) 1275 } 1276 }