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