github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/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 a == "all" || a == "std" || a == "cmd" { 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 var logf = log.Printf 407 408 func exitIfErrors() { 409 if exitStatus != 0 { 410 exit() 411 } 412 } 413 414 func run(cmdargs ...interface{}) { 415 cmdline := stringList(cmdargs...) 416 if buildN || buildX { 417 fmt.Printf("%s\n", strings.Join(cmdline, " ")) 418 if buildN { 419 return 420 } 421 } 422 423 cmd := exec.Command(cmdline[0], cmdline[1:]...) 424 cmd.Stdout = os.Stdout 425 cmd.Stderr = os.Stderr 426 if err := cmd.Run(); err != nil { 427 errorf("%v", err) 428 } 429 } 430 431 func runOut(dir string, cmdargs ...interface{}) []byte { 432 cmdline := stringList(cmdargs...) 433 cmd := exec.Command(cmdline[0], cmdline[1:]...) 434 cmd.Dir = dir 435 out, err := cmd.CombinedOutput() 436 if err != nil { 437 os.Stderr.Write(out) 438 errorf("%v", err) 439 out = nil 440 } 441 return out 442 } 443 444 // envForDir returns a copy of the environment 445 // suitable for running in the given directory. 446 // The environment is the current process's environment 447 // but with an updated $PWD, so that an os.Getwd in the 448 // child will be faster. 449 func envForDir(dir string, base []string) []string { 450 // Internally we only use rooted paths, so dir is rooted. 451 // Even if dir is not rooted, no harm done. 452 return mergeEnvLists([]string{"PWD=" + dir}, base) 453 } 454 455 // mergeEnvLists merges the two environment lists such that 456 // variables with the same name in "in" replace those in "out". 457 func mergeEnvLists(in, out []string) []string { 458 NextVar: 459 for _, inkv := range in { 460 k := strings.SplitAfterN(inkv, "=", 2)[0] 461 for i, outkv := range out { 462 if strings.HasPrefix(outkv, k) { 463 out[i] = inkv 464 continue NextVar 465 } 466 } 467 out = append(out, inkv) 468 } 469 return out 470 } 471 472 // matchPattern(pattern)(name) reports whether 473 // name matches pattern. Pattern is a limited glob 474 // pattern in which '...' means 'any string' and there 475 // is no other special syntax. 476 func matchPattern(pattern string) func(name string) bool { 477 re := regexp.QuoteMeta(pattern) 478 re = strings.Replace(re, `\.\.\.`, `.*`, -1) 479 // Special case: foo/... matches foo too. 480 if strings.HasSuffix(re, `/.*`) { 481 re = re[:len(re)-len(`/.*`)] + `(/.*)?` 482 } 483 reg := regexp.MustCompile(`^` + re + `$`) 484 return func(name string) bool { 485 return reg.MatchString(name) 486 } 487 } 488 489 // hasPathPrefix reports whether the path s begins with the 490 // elements in prefix. 491 func hasPathPrefix(s, prefix string) bool { 492 switch { 493 default: 494 return false 495 case len(s) == len(prefix): 496 return s == prefix 497 case len(s) > len(prefix): 498 if prefix != "" && prefix[len(prefix)-1] == '/' { 499 return strings.HasPrefix(s, prefix) 500 } 501 return s[len(prefix)] == '/' && s[:len(prefix)] == prefix 502 } 503 } 504 505 // hasFilePathPrefix reports whether the filesystem path s begins with the 506 // elements in prefix. 507 func hasFilePathPrefix(s, prefix string) bool { 508 sv := strings.ToUpper(filepath.VolumeName(s)) 509 pv := strings.ToUpper(filepath.VolumeName(prefix)) 510 s = s[len(sv):] 511 prefix = prefix[len(pv):] 512 switch { 513 default: 514 return false 515 case sv != pv: 516 return false 517 case len(s) == len(prefix): 518 return s == prefix 519 case len(s) > len(prefix): 520 if prefix != "" && prefix[len(prefix)-1] == filepath.Separator { 521 return strings.HasPrefix(s, prefix) 522 } 523 return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix 524 } 525 } 526 527 // treeCanMatchPattern(pattern)(name) reports whether 528 // name or children of name can possibly match pattern. 529 // Pattern is the same limited glob accepted by matchPattern. 530 func treeCanMatchPattern(pattern string) func(name string) bool { 531 wildCard := false 532 if i := strings.Index(pattern, "..."); i >= 0 { 533 wildCard = true 534 pattern = pattern[:i] 535 } 536 return func(name string) bool { 537 return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || 538 wildCard && strings.HasPrefix(name, pattern) 539 } 540 } 541 542 // allPackages returns all the packages that can be found 543 // under the $GOPATH directories and $GOROOT matching pattern. 544 // The pattern is either "all" (all packages), "std" (standard packages), 545 // "cmd" (standard commands), or a path including "...". 546 func allPackages(pattern string) []string { 547 pkgs := matchPackages(pattern) 548 if len(pkgs) == 0 { 549 fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) 550 } 551 return pkgs 552 } 553 554 func matchPackages(pattern string) []string { 555 match := func(string) bool { return true } 556 treeCanMatch := func(string) bool { return true } 557 if pattern != "all" && pattern != "std" && pattern != "cmd" { 558 match = matchPattern(pattern) 559 treeCanMatch = treeCanMatchPattern(pattern) 560 } 561 562 have := map[string]bool{ 563 "builtin": true, // ignore pseudo-package that exists only for documentation 564 } 565 if !buildContext.CgoEnabled { 566 have["runtime/cgo"] = true // ignore during walk 567 } 568 var pkgs []string 569 570 for _, src := range buildContext.SrcDirs() { 571 if (pattern == "std" || pattern == "cmd") && src != gorootSrc { 572 continue 573 } 574 src = filepath.Clean(src) + string(filepath.Separator) 575 root := src 576 if pattern == "cmd" { 577 root += "cmd" + string(filepath.Separator) 578 } 579 filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { 580 if err != nil || !fi.IsDir() || path == src { 581 return nil 582 } 583 584 // Avoid .foo, _foo, and testdata directory trees. 585 _, elem := filepath.Split(path) 586 if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { 587 return filepath.SkipDir 588 } 589 590 name := filepath.ToSlash(path[len(src):]) 591 if pattern == "std" && (strings.Contains(name, ".") || name == "cmd") { 592 // The name "std" is only the standard library. 593 // If the name has a dot, assume it's a domain name for go get, 594 // and if the name is cmd, it's the root of the command tree. 595 return filepath.SkipDir 596 } 597 if !treeCanMatch(name) { 598 return filepath.SkipDir 599 } 600 if have[name] { 601 return nil 602 } 603 have[name] = true 604 if !match(name) { 605 return nil 606 } 607 _, err = buildContext.ImportDir(path, 0) 608 if err != nil { 609 if _, noGo := err.(*build.NoGoError); noGo { 610 return nil 611 } 612 } 613 pkgs = append(pkgs, name) 614 return nil 615 }) 616 } 617 return pkgs 618 } 619 620 // allPackagesInFS is like allPackages but is passed a pattern 621 // beginning ./ or ../, meaning it should scan the tree rooted 622 // at the given directory. There are ... in the pattern too. 623 func allPackagesInFS(pattern string) []string { 624 pkgs := matchPackagesInFS(pattern) 625 if len(pkgs) == 0 { 626 fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) 627 } 628 return pkgs 629 } 630 631 func matchPackagesInFS(pattern string) []string { 632 // Find directory to begin the scan. 633 // Could be smarter but this one optimization 634 // is enough for now, since ... is usually at the 635 // end of a path. 636 i := strings.Index(pattern, "...") 637 dir, _ := path.Split(pattern[:i]) 638 639 // pattern begins with ./ or ../. 640 // path.Clean will discard the ./ but not the ../. 641 // We need to preserve the ./ for pattern matching 642 // and in the returned import paths. 643 prefix := "" 644 if strings.HasPrefix(pattern, "./") { 645 prefix = "./" 646 } 647 match := matchPattern(pattern) 648 649 var pkgs []string 650 filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { 651 if err != nil || !fi.IsDir() { 652 return nil 653 } 654 if path == dir { 655 // filepath.Walk starts at dir and recurses. For the recursive case, 656 // the path is the result of filepath.Join, which calls filepath.Clean. 657 // The initial case is not Cleaned, though, so we do this explicitly. 658 // 659 // This converts a path like "./io/" to "io". Without this step, running 660 // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io 661 // package, because prepending the prefix "./" to the unclean path would 662 // result in "././io", and match("././io") returns false. 663 path = filepath.Clean(path) 664 } 665 666 // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..". 667 _, elem := filepath.Split(path) 668 dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." 669 if dot || strings.HasPrefix(elem, "_") || elem == "testdata" { 670 return filepath.SkipDir 671 } 672 673 name := prefix + filepath.ToSlash(path) 674 if !match(name) { 675 return nil 676 } 677 if _, err = build.ImportDir(path, 0); err != nil { 678 if _, noGo := err.(*build.NoGoError); !noGo { 679 log.Print(err) 680 } 681 return nil 682 } 683 pkgs = append(pkgs, name) 684 return nil 685 }) 686 return pkgs 687 } 688 689 // stringList's arguments should be a sequence of string or []string values. 690 // stringList flattens them into a single []string. 691 func stringList(args ...interface{}) []string { 692 var x []string 693 for _, arg := range args { 694 switch arg := arg.(type) { 695 case []string: 696 x = append(x, arg...) 697 case string: 698 x = append(x, arg) 699 default: 700 panic("stringList: invalid argument of type " + fmt.Sprintf("%T", arg)) 701 } 702 } 703 return x 704 } 705 706 // toFold returns a string with the property that 707 // strings.EqualFold(s, t) iff toFold(s) == toFold(t) 708 // This lets us test a large set of strings for fold-equivalent 709 // duplicates without making a quadratic number of calls 710 // to EqualFold. Note that strings.ToUpper and strings.ToLower 711 // have the desired property in some corner cases. 712 func toFold(s string) string { 713 // Fast path: all ASCII, no upper case. 714 // Most paths look like this already. 715 for i := 0; i < len(s); i++ { 716 c := s[i] 717 if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' { 718 goto Slow 719 } 720 } 721 return s 722 723 Slow: 724 var buf bytes.Buffer 725 for _, r := range s { 726 // SimpleFold(x) cycles to the next equivalent rune > x 727 // or wraps around to smaller values. Iterate until it wraps, 728 // and we've found the minimum value. 729 for { 730 r0 := r 731 r = unicode.SimpleFold(r0) 732 if r <= r0 { 733 break 734 } 735 } 736 // Exception to allow fast path above: A-Z => a-z 737 if 'A' <= r && r <= 'Z' { 738 r += 'a' - 'A' 739 } 740 buf.WriteRune(r) 741 } 742 return buf.String() 743 } 744 745 // foldDup reports a pair of strings from the list that are 746 // equal according to strings.EqualFold. 747 // It returns "", "" if there are no such strings. 748 func foldDup(list []string) (string, string) { 749 clash := map[string]string{} 750 for _, s := range list { 751 fold := toFold(s) 752 if t := clash[fold]; t != "" { 753 if s > t { 754 s, t = t, s 755 } 756 return s, t 757 } 758 clash[fold] = s 759 } 760 return "", "" 761 }