github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/cmd/go/main.go (about) 1 // Copyright 2011 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 "bufio" 9 "bytes" 10 "flag" 11 "fmt" 12 "go/build" 13 "io" 14 "log" 15 "os" 16 "os/exec" 17 "path" 18 "path/filepath" 19 "regexp" 20 "runtime" 21 "strings" 22 "sync" 23 "text/template" 24 "unicode" 25 "unicode/utf8" 26 ) 27 28 // A Command is an implementation of a go command 29 // like go build or go fix. 30 type Command struct { 31 // Run runs the command. 32 // The args are the arguments after the command name. 33 Run func(cmd *Command, args []string) 34 35 // UsageLine is the one-line usage message. 36 // The first word in the line is taken to be the command name. 37 UsageLine string 38 39 // Short is the short description shown in the 'go help' output. 40 Short string 41 42 // Long is the long message shown in the 'go help <this-command>' output. 43 Long string 44 45 // Flag is a set of flags specific to this command. 46 Flag flag.FlagSet 47 48 // CustomFlags indicates that the command will do its own 49 // flag parsing. 50 CustomFlags bool 51 } 52 53 // Name returns the command's name: the first word in the usage line. 54 func (c *Command) Name() string { 55 name := c.UsageLine 56 i := strings.Index(name, " ") 57 if i >= 0 { 58 name = name[:i] 59 } 60 return name 61 } 62 63 func (c *Command) Usage() { 64 fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine) 65 fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(c.Long)) 66 os.Exit(2) 67 } 68 69 // Runnable reports whether the command can be run; otherwise 70 // it is a documentation pseudo-command such as importpath. 71 func (c *Command) Runnable() bool { 72 return c.Run != nil 73 } 74 75 // Commands lists the available commands and help topics. 76 // The order here is the order in which they are printed by 'go help'. 77 var commands = []*Command{ 78 cmdBuild, 79 cmdClean, 80 cmdDoc, 81 cmdEnv, 82 cmdFix, 83 cmdFmt, 84 cmdGenerate, 85 cmdGet, 86 cmdInstall, 87 cmdList, 88 cmdRun, 89 cmdTest, 90 cmdTool, 91 cmdVersion, 92 cmdVet, 93 94 helpC, 95 helpBuildmode, 96 helpFileType, 97 helpGopath, 98 helpEnvironment, 99 helpImportPath, 100 helpPackages, 101 helpTestflag, 102 helpTestfunc, 103 } 104 105 var exitStatus = 0 106 var exitMu sync.Mutex 107 108 func setExitStatus(n int) { 109 exitMu.Lock() 110 if exitStatus < n { 111 exitStatus = n 112 } 113 exitMu.Unlock() 114 } 115 116 var origEnv []string 117 118 func main() { 119 _ = go11tag 120 flag.Usage = usage 121 flag.Parse() 122 log.SetFlags(0) 123 124 args := flag.Args() 125 if len(args) < 1 { 126 usage() 127 } 128 129 if args[0] == "help" { 130 help(args[1:]) 131 return 132 } 133 134 // Diagnose common mistake: GOPATH==GOROOT. 135 // This setting is equivalent to not setting GOPATH at all, 136 // which is not what most people want when they do it. 137 if gopath := os.Getenv("GOPATH"); gopath == runtime.GOROOT() { 138 fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath) 139 } else { 140 for _, p := range filepath.SplitList(gopath) { 141 // Note: using HasPrefix instead of Contains because a ~ can appear 142 // in the middle of directory elements, such as /tmp/git-1.8.2~rc3 143 // or C:\PROGRA~1. Only ~ as a path prefix has meaning to the shell. 144 if strings.HasPrefix(p, "~") { 145 fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot start with shell metacharacter '~': %q\n", p) 146 os.Exit(2) 147 } 148 if !filepath.IsAbs(p) { 149 fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nRun 'go help gopath' for usage.\n", p) 150 os.Exit(2) 151 } 152 } 153 } 154 155 if fi, err := os.Stat(goroot); err != nil || !fi.IsDir() { 156 fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: %v\n", goroot) 157 os.Exit(2) 158 } 159 160 // Set environment (GOOS, GOARCH, etc) explicitly. 161 // In theory all the commands we invoke should have 162 // the same default computation of these as we do, 163 // but in practice there might be skew 164 // This makes sure we all agree. 165 origEnv = os.Environ() 166 for _, env := range mkEnv() { 167 if os.Getenv(env.name) != env.value { 168 os.Setenv(env.name, env.value) 169 } 170 } 171 172 for _, cmd := range commands { 173 if cmd.Name() == args[0] && cmd.Runnable() { 174 cmd.Flag.Usage = func() { cmd.Usage() } 175 if cmd.CustomFlags { 176 args = args[1:] 177 } else { 178 cmd.Flag.Parse(args[1:]) 179 args = cmd.Flag.Args() 180 } 181 cmd.Run(cmd, args) 182 exit() 183 return 184 } 185 } 186 187 fmt.Fprintf(os.Stderr, "go: unknown subcommand %q\nRun 'go help' for usage.\n", args[0]) 188 setExitStatus(2) 189 exit() 190 } 191 192 var usageTemplate = `Go is a tool for managing Go source code. 193 194 Usage: 195 196 go command [arguments] 197 198 The commands are: 199 {{range .}}{{if .Runnable}} 200 {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} 201 202 Use "go help [command]" for more information about a command. 203 204 Additional help topics: 205 {{range .}}{{if not .Runnable}} 206 {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} 207 208 Use "go help [topic]" for more information about that topic. 209 210 ` 211 212 var helpTemplate = `{{if .Runnable}}usage: go {{.UsageLine}} 213 214 {{end}}{{.Long | trim}} 215 ` 216 217 var documentationTemplate = `// Copyright 2011 The Go Authors. All rights reserved. 218 // Use of this source code is governed by a BSD-style 219 // license that can be found in the LICENSE file. 220 221 // DO NOT EDIT THIS FILE. GENERATED BY mkalldocs.sh. 222 // Edit the documentation in other files and rerun mkalldocs.sh to generate this one. 223 224 /* 225 {{range .}}{{if .Short}}{{.Short | capitalize}} 226 227 {{end}}{{if .Runnable}}Usage: 228 229 go {{.UsageLine}} 230 231 {{end}}{{.Long | trim}} 232 233 234 {{end}}*/ 235 package main 236 ` 237 238 // An errWriter wraps a writer, recording whether a write error occurred. 239 type errWriter struct { 240 w io.Writer 241 err error 242 } 243 244 func (w *errWriter) Write(b []byte) (int, error) { 245 n, err := w.w.Write(b) 246 if err != nil { 247 w.err = err 248 } 249 return n, err 250 } 251 252 // tmpl executes the given template text on data, writing the result to w. 253 func tmpl(w io.Writer, text string, data interface{}) { 254 t := template.New("top") 255 t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize}) 256 template.Must(t.Parse(text)) 257 ew := &errWriter{w: w} 258 err := t.Execute(ew, data) 259 if ew.err != nil { 260 // I/O error writing. Ignore write on closed pipe. 261 if strings.Contains(ew.err.Error(), "pipe") { 262 os.Exit(1) 263 } 264 fatalf("writing output: %v", ew.err) 265 } 266 if err != nil { 267 panic(err) 268 } 269 } 270 271 func capitalize(s string) string { 272 if s == "" { 273 return s 274 } 275 r, n := utf8.DecodeRuneInString(s) 276 return string(unicode.ToTitle(r)) + s[n:] 277 } 278 279 func printUsage(w io.Writer) { 280 bw := bufio.NewWriter(w) 281 tmpl(bw, usageTemplate, commands) 282 bw.Flush() 283 } 284 285 func usage() { 286 // special case "go test -h" 287 if len(os.Args) > 1 && os.Args[1] == "test" { 288 os.Stderr.WriteString(testUsage + "\n\n" + 289 strings.TrimSpace(testFlag1) + "\n\n\t" + 290 strings.TrimSpace(testFlag2) + "\n") 291 os.Exit(2) 292 } 293 printUsage(os.Stderr) 294 os.Exit(2) 295 } 296 297 // help implements the 'help' command. 298 func help(args []string) { 299 if len(args) == 0 { 300 printUsage(os.Stdout) 301 // not exit 2: succeeded at 'go help'. 302 return 303 } 304 if len(args) != 1 { 305 fmt.Fprintf(os.Stderr, "usage: go help command\n\nToo many arguments given.\n") 306 os.Exit(2) // failed at 'go help' 307 } 308 309 arg := args[0] 310 311 // 'go help documentation' generates doc.go. 312 if arg == "documentation" { 313 buf := new(bytes.Buffer) 314 printUsage(buf) 315 usage := &Command{Long: buf.String()} 316 tmpl(os.Stdout, documentationTemplate, append([]*Command{usage}, commands...)) 317 return 318 } 319 320 for _, cmd := range commands { 321 if cmd.Name() == arg { 322 tmpl(os.Stdout, helpTemplate, cmd) 323 // not exit 2: succeeded at 'go help cmd'. 324 return 325 } 326 } 327 328 fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'go help'.\n", arg) 329 os.Exit(2) // failed at 'go help cmd' 330 } 331 332 // importPathsNoDotExpansion returns the import paths to use for the given 333 // command line, but it does no ... expansion. 334 func importPathsNoDotExpansion(args []string) []string { 335 if len(args) == 0 { 336 return []string{"."} 337 } 338 var out []string 339 for _, a := range args { 340 // Arguments are supposed to be import paths, but 341 // as a courtesy to Windows developers, rewrite \ to / 342 // in command-line arguments. Handles .\... and so on. 343 if filepath.Separator == '\\' { 344 a = strings.Replace(a, `\`, `/`, -1) 345 } 346 347 // Put argument in canonical form, but preserve leading ./. 348 if strings.HasPrefix(a, "./") { 349 a = "./" + path.Clean(a) 350 if a == "./." { 351 a = "." 352 } 353 } else { 354 a = path.Clean(a) 355 } 356 if isMetaPackage(a) { 357 out = append(out, allPackages(a)...) 358 continue 359 } 360 out = append(out, a) 361 } 362 return out 363 } 364 365 // importPaths returns the import paths to use for the given command line. 366 func importPaths(args []string) []string { 367 args = importPathsNoDotExpansion(args) 368 var out []string 369 for _, a := range args { 370 if strings.Contains(a, "...") { 371 if build.IsLocalImport(a) { 372 out = append(out, allPackagesInFS(a)...) 373 } else { 374 out = append(out, allPackages(a)...) 375 } 376 continue 377 } 378 out = append(out, a) 379 } 380 return out 381 } 382 383 var atexitFuncs []func() 384 385 func atexit(f func()) { 386 atexitFuncs = append(atexitFuncs, f) 387 } 388 389 func exit() { 390 for _, f := range atexitFuncs { 391 f() 392 } 393 os.Exit(exitStatus) 394 } 395 396 func fatalf(format string, args ...interface{}) { 397 errorf(format, args...) 398 exit() 399 } 400 401 func errorf(format string, args ...interface{}) { 402 log.Printf(format, args...) 403 setExitStatus(1) 404 } 405 406 func exitIfErrors() { 407 if exitStatus != 0 { 408 exit() 409 } 410 } 411 412 func run(cmdargs ...interface{}) { 413 cmdline := stringList(cmdargs...) 414 if buildN || buildX { 415 fmt.Printf("%s\n", strings.Join(cmdline, " ")) 416 if buildN { 417 return 418 } 419 } 420 421 cmd := exec.Command(cmdline[0], cmdline[1:]...) 422 cmd.Stdout = os.Stdout 423 cmd.Stderr = os.Stderr 424 if err := cmd.Run(); err != nil { 425 errorf("%v", err) 426 } 427 } 428 429 // envForDir returns a copy of the environment 430 // suitable for running in the given directory. 431 // The environment is the current process's environment 432 // but with an updated $PWD, so that an os.Getwd in the 433 // child will be faster. 434 func envForDir(dir string, base []string) []string { 435 // Internally we only use rooted paths, so dir is rooted. 436 // Even if dir is not rooted, no harm done. 437 return mergeEnvLists([]string{"PWD=" + dir}, base) 438 } 439 440 // mergeEnvLists merges the two environment lists such that 441 // variables with the same name in "in" replace those in "out". 442 // This always returns a newly allocated slice. 443 func mergeEnvLists(in, out []string) []string { 444 out = append([]string(nil), out...) 445 NextVar: 446 for _, inkv := range in { 447 k := strings.SplitAfterN(inkv, "=", 2)[0] 448 for i, outkv := range out { 449 if strings.HasPrefix(outkv, k) { 450 out[i] = inkv 451 continue NextVar 452 } 453 } 454 out = append(out, inkv) 455 } 456 return out 457 } 458 459 // matchPattern(pattern)(name) reports whether 460 // name matches pattern. Pattern is a limited glob 461 // pattern in which '...' means 'any string' and there 462 // is no other special syntax. 463 func matchPattern(pattern string) func(name string) bool { 464 re := regexp.QuoteMeta(pattern) 465 re = strings.Replace(re, `\.\.\.`, `.*`, -1) 466 // Special case: foo/... matches foo too. 467 if strings.HasSuffix(re, `/.*`) { 468 re = re[:len(re)-len(`/.*`)] + `(/.*)?` 469 } 470 reg := regexp.MustCompile(`^` + re + `$`) 471 return func(name string) bool { 472 return reg.MatchString(name) 473 } 474 } 475 476 // hasPathPrefix reports whether the path s begins with the 477 // elements in prefix. 478 func hasPathPrefix(s, prefix string) bool { 479 switch { 480 default: 481 return false 482 case len(s) == len(prefix): 483 return s == prefix 484 case len(s) > len(prefix): 485 if prefix != "" && prefix[len(prefix)-1] == '/' { 486 return strings.HasPrefix(s, prefix) 487 } 488 return s[len(prefix)] == '/' && s[:len(prefix)] == prefix 489 } 490 } 491 492 // hasFilePathPrefix reports whether the filesystem path s begins with the 493 // elements in prefix. 494 func hasFilePathPrefix(s, prefix string) bool { 495 sv := strings.ToUpper(filepath.VolumeName(s)) 496 pv := strings.ToUpper(filepath.VolumeName(prefix)) 497 s = s[len(sv):] 498 prefix = prefix[len(pv):] 499 switch { 500 default: 501 return false 502 case sv != pv: 503 return false 504 case len(s) == len(prefix): 505 return s == prefix 506 case len(s) > len(prefix): 507 if prefix != "" && prefix[len(prefix)-1] == filepath.Separator { 508 return strings.HasPrefix(s, prefix) 509 } 510 return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix 511 } 512 } 513 514 // expandPath returns the symlink-expanded form of path. 515 func expandPath(p string) string { 516 x, err := filepath.EvalSymlinks(p) 517 if err == nil { 518 return x 519 } 520 return p 521 } 522 523 // treeCanMatchPattern(pattern)(name) reports whether 524 // name or children of name can possibly match pattern. 525 // Pattern is the same limited glob accepted by matchPattern. 526 func treeCanMatchPattern(pattern string) func(name string) bool { 527 wildCard := false 528 if i := strings.Index(pattern, "..."); i >= 0 { 529 wildCard = true 530 pattern = pattern[:i] 531 } 532 return func(name string) bool { 533 return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || 534 wildCard && strings.HasPrefix(name, pattern) 535 } 536 } 537 538 // allPackages returns all the packages that can be found 539 // under the $GOPATH directories and $GOROOT matching pattern. 540 // The pattern is either "all" (all packages), "std" (standard packages), 541 // "cmd" (standard commands), or a path including "...". 542 func allPackages(pattern string) []string { 543 pkgs := matchPackages(pattern) 544 if len(pkgs) == 0 { 545 fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) 546 } 547 return pkgs 548 } 549 550 func matchPackages(pattern string) []string { 551 match := func(string) bool { return true } 552 treeCanMatch := func(string) bool { return true } 553 if !isMetaPackage(pattern) { 554 match = matchPattern(pattern) 555 treeCanMatch = treeCanMatchPattern(pattern) 556 } 557 558 have := map[string]bool{ 559 "builtin": true, // ignore pseudo-package that exists only for documentation 560 } 561 if !buildContext.CgoEnabled { 562 have["runtime/cgo"] = true // ignore during walk 563 } 564 var pkgs []string 565 566 for _, src := range buildContext.SrcDirs() { 567 if (pattern == "std" || pattern == "cmd") && src != gorootSrc { 568 continue 569 } 570 src = filepath.Clean(src) + string(filepath.Separator) 571 root := src 572 if pattern == "cmd" { 573 root += "cmd" + string(filepath.Separator) 574 } 575 filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { 576 if err != nil || !fi.IsDir() || path == src { 577 return nil 578 } 579 580 // Avoid .foo, _foo, and testdata directory trees. 581 _, elem := filepath.Split(path) 582 if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { 583 return filepath.SkipDir 584 } 585 586 name := filepath.ToSlash(path[len(src):]) 587 if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") { 588 // The name "std" is only the standard library. 589 // If the name is cmd, it's the root of the command tree. 590 return filepath.SkipDir 591 } 592 if !treeCanMatch(name) { 593 return filepath.SkipDir 594 } 595 if have[name] { 596 return nil 597 } 598 have[name] = true 599 if !match(name) { 600 return nil 601 } 602 _, err = buildContext.ImportDir(path, 0) 603 if err != nil { 604 if _, noGo := err.(*build.NoGoError); noGo { 605 return nil 606 } 607 } 608 pkgs = append(pkgs, name) 609 return nil 610 }) 611 } 612 return pkgs 613 } 614 615 // allPackagesInFS is like allPackages but is passed a pattern 616 // beginning ./ or ../, meaning it should scan the tree rooted 617 // at the given directory. There are ... in the pattern too. 618 func allPackagesInFS(pattern string) []string { 619 pkgs := matchPackagesInFS(pattern) 620 if len(pkgs) == 0 { 621 fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) 622 } 623 return pkgs 624 } 625 626 func matchPackagesInFS(pattern string) []string { 627 // Find directory to begin the scan. 628 // Could be smarter but this one optimization 629 // is enough for now, since ... is usually at the 630 // end of a path. 631 i := strings.Index(pattern, "...") 632 dir, _ := path.Split(pattern[:i]) 633 634 // pattern begins with ./ or ../. 635 // path.Clean will discard the ./ but not the ../. 636 // We need to preserve the ./ for pattern matching 637 // and in the returned import paths. 638 prefix := "" 639 if strings.HasPrefix(pattern, "./") { 640 prefix = "./" 641 } 642 match := matchPattern(pattern) 643 644 var pkgs []string 645 filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { 646 if err != nil || !fi.IsDir() { 647 return nil 648 } 649 if path == dir { 650 // filepath.Walk starts at dir and recurses. For the recursive case, 651 // the path is the result of filepath.Join, which calls filepath.Clean. 652 // The initial case is not Cleaned, though, so we do this explicitly. 653 // 654 // This converts a path like "./io/" to "io". Without this step, running 655 // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io 656 // package, because prepending the prefix "./" to the unclean path would 657 // result in "././io", and match("././io") returns false. 658 path = filepath.Clean(path) 659 } 660 661 // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..". 662 _, elem := filepath.Split(path) 663 dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." 664 if dot || strings.HasPrefix(elem, "_") || elem == "testdata" { 665 return filepath.SkipDir 666 } 667 668 name := prefix + filepath.ToSlash(path) 669 if !match(name) { 670 return nil 671 } 672 673 // We keep the directory if we can import it, or if we can't import it 674 // due to invalid Go source files. This means that directories containing 675 // parse errors will be built (and fail) instead of being silently skipped 676 // as not matching the pattern. Go 1.5 and earlier skipped, but that 677 // behavior means people miss serious mistakes. 678 // See golang.org/issue/11407. 679 if p, err := buildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) { 680 if _, noGo := err.(*build.NoGoError); !noGo { 681 log.Print(err) 682 } 683 return nil 684 } 685 pkgs = append(pkgs, name) 686 return nil 687 }) 688 return pkgs 689 } 690 691 // stringList's arguments should be a sequence of string or []string values. 692 // stringList flattens them into a single []string. 693 func stringList(args ...interface{}) []string { 694 var x []string 695 for _, arg := range args { 696 switch arg := arg.(type) { 697 case []string: 698 x = append(x, arg...) 699 case string: 700 x = append(x, arg) 701 default: 702 panic("stringList: invalid argument of type " + fmt.Sprintf("%T", arg)) 703 } 704 } 705 return x 706 } 707 708 // toFold returns a string with the property that 709 // strings.EqualFold(s, t) iff toFold(s) == toFold(t) 710 // This lets us test a large set of strings for fold-equivalent 711 // duplicates without making a quadratic number of calls 712 // to EqualFold. Note that strings.ToUpper and strings.ToLower 713 // have the desired property in some corner cases. 714 func toFold(s string) string { 715 // Fast path: all ASCII, no upper case. 716 // Most paths look like this already. 717 for i := 0; i < len(s); i++ { 718 c := s[i] 719 if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' { 720 goto Slow 721 } 722 } 723 return s 724 725 Slow: 726 var buf bytes.Buffer 727 for _, r := range s { 728 // SimpleFold(x) cycles to the next equivalent rune > x 729 // or wraps around to smaller values. Iterate until it wraps, 730 // and we've found the minimum value. 731 for { 732 r0 := r 733 r = unicode.SimpleFold(r0) 734 if r <= r0 { 735 break 736 } 737 } 738 // Exception to allow fast path above: A-Z => a-z 739 if 'A' <= r && r <= 'Z' { 740 r += 'a' - 'A' 741 } 742 buf.WriteRune(r) 743 } 744 return buf.String() 745 } 746 747 // foldDup reports a pair of strings from the list that are 748 // equal according to strings.EqualFold. 749 // It returns "", "" if there are no such strings. 750 func foldDup(list []string) (string, string) { 751 clash := map[string]string{} 752 for _, s := range list { 753 fold := toFold(s) 754 if t := clash[fold]; t != "" { 755 if s > t { 756 s, t = t, s 757 } 758 return s, t 759 } 760 clash[fold] = s 761 } 762 return "", "" 763 }