github.com/aloncn/graphics-go@v0.0.1/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 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 // This always returns a newly allocated slice. 458 func mergeEnvLists(in, out []string) []string { 459 out = append([]string(nil), out...) 460 NextVar: 461 for _, inkv := range in { 462 k := strings.SplitAfterN(inkv, "=", 2)[0] 463 for i, outkv := range out { 464 if strings.HasPrefix(outkv, k) { 465 out[i] = inkv 466 continue NextVar 467 } 468 } 469 out = append(out, inkv) 470 } 471 return out 472 } 473 474 // matchPattern(pattern)(name) reports whether 475 // name matches pattern. Pattern is a limited glob 476 // pattern in which '...' means 'any string' and there 477 // is no other special syntax. 478 func matchPattern(pattern string) func(name string) bool { 479 re := regexp.QuoteMeta(pattern) 480 re = strings.Replace(re, `\.\.\.`, `.*`, -1) 481 // Special case: foo/... matches foo too. 482 if strings.HasSuffix(re, `/.*`) { 483 re = re[:len(re)-len(`/.*`)] + `(/.*)?` 484 } 485 reg := regexp.MustCompile(`^` + re + `$`) 486 return func(name string) bool { 487 return reg.MatchString(name) 488 } 489 } 490 491 // hasPathPrefix reports whether the path s begins with the 492 // elements in prefix. 493 func hasPathPrefix(s, prefix string) bool { 494 switch { 495 default: 496 return false 497 case len(s) == len(prefix): 498 return s == prefix 499 case len(s) > len(prefix): 500 if prefix != "" && prefix[len(prefix)-1] == '/' { 501 return strings.HasPrefix(s, prefix) 502 } 503 return s[len(prefix)] == '/' && s[:len(prefix)] == prefix 504 } 505 } 506 507 // hasFilePathPrefix reports whether the filesystem path s begins with the 508 // elements in prefix. 509 func hasFilePathPrefix(s, prefix string) bool { 510 sv := strings.ToUpper(filepath.VolumeName(s)) 511 pv := strings.ToUpper(filepath.VolumeName(prefix)) 512 s = s[len(sv):] 513 prefix = prefix[len(pv):] 514 switch { 515 default: 516 return false 517 case sv != pv: 518 return false 519 case len(s) == len(prefix): 520 return s == prefix 521 case len(s) > len(prefix): 522 if prefix != "" && prefix[len(prefix)-1] == filepath.Separator { 523 return strings.HasPrefix(s, prefix) 524 } 525 return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix 526 } 527 } 528 529 // expandPath returns the symlink-expanded form of path. 530 func expandPath(p string) string { 531 x, err := filepath.EvalSymlinks(p) 532 if err == nil { 533 return x 534 } 535 return p 536 } 537 538 // treeCanMatchPattern(pattern)(name) reports whether 539 // name or children of name can possibly match pattern. 540 // Pattern is the same limited glob accepted by matchPattern. 541 func treeCanMatchPattern(pattern string) func(name string) bool { 542 wildCard := false 543 if i := strings.Index(pattern, "..."); i >= 0 { 544 wildCard = true 545 pattern = pattern[:i] 546 } 547 return func(name string) bool { 548 return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || 549 wildCard && strings.HasPrefix(name, pattern) 550 } 551 } 552 553 // allPackages returns all the packages that can be found 554 // under the $GOPATH directories and $GOROOT matching pattern. 555 // The pattern is either "all" (all packages), "std" (standard packages), 556 // "cmd" (standard commands), or a path including "...". 557 func allPackages(pattern string) []string { 558 pkgs := matchPackages(pattern) 559 if len(pkgs) == 0 { 560 fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) 561 } 562 return pkgs 563 } 564 565 func matchPackages(pattern string) []string { 566 match := func(string) bool { return true } 567 treeCanMatch := func(string) bool { return true } 568 if !isMetaPackage(pattern) { 569 match = matchPattern(pattern) 570 treeCanMatch = treeCanMatchPattern(pattern) 571 } 572 573 have := map[string]bool{ 574 "builtin": true, // ignore pseudo-package that exists only for documentation 575 } 576 if !buildContext.CgoEnabled { 577 have["runtime/cgo"] = true // ignore during walk 578 } 579 var pkgs []string 580 581 for _, src := range buildContext.SrcDirs() { 582 if (pattern == "std" || pattern == "cmd") && src != gorootSrc { 583 continue 584 } 585 src = filepath.Clean(src) + string(filepath.Separator) 586 root := src 587 if pattern == "cmd" { 588 root += "cmd" + string(filepath.Separator) 589 } 590 filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { 591 if err != nil || !fi.IsDir() || path == src { 592 return nil 593 } 594 595 // Avoid .foo, _foo, and testdata directory trees. 596 _, elem := filepath.Split(path) 597 if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { 598 return filepath.SkipDir 599 } 600 601 name := filepath.ToSlash(path[len(src):]) 602 if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") { 603 // The name "std" is only the standard library. 604 // If the name is cmd, it's the root of the command tree. 605 return filepath.SkipDir 606 } 607 if !treeCanMatch(name) { 608 return filepath.SkipDir 609 } 610 if have[name] { 611 return nil 612 } 613 have[name] = true 614 if !match(name) { 615 return nil 616 } 617 _, err = buildContext.ImportDir(path, 0) 618 if err != nil { 619 if _, noGo := err.(*build.NoGoError); noGo { 620 return nil 621 } 622 } 623 pkgs = append(pkgs, name) 624 return nil 625 }) 626 } 627 return pkgs 628 } 629 630 // allPackagesInFS is like allPackages but is passed a pattern 631 // beginning ./ or ../, meaning it should scan the tree rooted 632 // at the given directory. There are ... in the pattern too. 633 func allPackagesInFS(pattern string) []string { 634 pkgs := matchPackagesInFS(pattern) 635 if len(pkgs) == 0 { 636 fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) 637 } 638 return pkgs 639 } 640 641 func matchPackagesInFS(pattern string) []string { 642 // Find directory to begin the scan. 643 // Could be smarter but this one optimization 644 // is enough for now, since ... is usually at the 645 // end of a path. 646 i := strings.Index(pattern, "...") 647 dir, _ := path.Split(pattern[:i]) 648 649 // pattern begins with ./ or ../. 650 // path.Clean will discard the ./ but not the ../. 651 // We need to preserve the ./ for pattern matching 652 // and in the returned import paths. 653 prefix := "" 654 if strings.HasPrefix(pattern, "./") { 655 prefix = "./" 656 } 657 match := matchPattern(pattern) 658 659 var pkgs []string 660 filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { 661 if err != nil || !fi.IsDir() { 662 return nil 663 } 664 if path == dir { 665 // filepath.Walk starts at dir and recurses. For the recursive case, 666 // the path is the result of filepath.Join, which calls filepath.Clean. 667 // The initial case is not Cleaned, though, so we do this explicitly. 668 // 669 // This converts a path like "./io/" to "io". Without this step, running 670 // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io 671 // package, because prepending the prefix "./" to the unclean path would 672 // result in "././io", and match("././io") returns false. 673 path = filepath.Clean(path) 674 } 675 676 // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..". 677 _, elem := filepath.Split(path) 678 dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." 679 if dot || strings.HasPrefix(elem, "_") || elem == "testdata" { 680 return filepath.SkipDir 681 } 682 683 name := prefix + filepath.ToSlash(path) 684 if !match(name) { 685 return nil 686 } 687 688 // We keep the directory if we can import it, or if we can't import it 689 // due to invalid Go source files. This means that directories containing 690 // parse errors will be built (and fail) instead of being silently skipped 691 // as not matching the pattern. Go 1.5 and earlier skipped, but that 692 // behavior means people miss serious mistakes. 693 // See golang.org/issue/11407. 694 if p, err := buildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) { 695 if _, noGo := err.(*build.NoGoError); !noGo { 696 log.Print(err) 697 } 698 return nil 699 } 700 pkgs = append(pkgs, name) 701 return nil 702 }) 703 return pkgs 704 } 705 706 // stringList's arguments should be a sequence of string or []string values. 707 // stringList flattens them into a single []string. 708 func stringList(args ...interface{}) []string { 709 var x []string 710 for _, arg := range args { 711 switch arg := arg.(type) { 712 case []string: 713 x = append(x, arg...) 714 case string: 715 x = append(x, arg) 716 default: 717 panic("stringList: invalid argument of type " + fmt.Sprintf("%T", arg)) 718 } 719 } 720 return x 721 } 722 723 // toFold returns a string with the property that 724 // strings.EqualFold(s, t) iff toFold(s) == toFold(t) 725 // This lets us test a large set of strings for fold-equivalent 726 // duplicates without making a quadratic number of calls 727 // to EqualFold. Note that strings.ToUpper and strings.ToLower 728 // have the desired property in some corner cases. 729 func toFold(s string) string { 730 // Fast path: all ASCII, no upper case. 731 // Most paths look like this already. 732 for i := 0; i < len(s); i++ { 733 c := s[i] 734 if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' { 735 goto Slow 736 } 737 } 738 return s 739 740 Slow: 741 var buf bytes.Buffer 742 for _, r := range s { 743 // SimpleFold(x) cycles to the next equivalent rune > x 744 // or wraps around to smaller values. Iterate until it wraps, 745 // and we've found the minimum value. 746 for { 747 r0 := r 748 r = unicode.SimpleFold(r0) 749 if r <= r0 { 750 break 751 } 752 } 753 // Exception to allow fast path above: A-Z => a-z 754 if 'A' <= r && r <= 'Z' { 755 r += 'a' - 'A' 756 } 757 buf.WriteRune(r) 758 } 759 return buf.String() 760 } 761 762 // foldDup reports a pair of strings from the list that are 763 // equal according to strings.EqualFold. 764 // It returns "", "" if there are no such strings. 765 func foldDup(list []string) (string, string) { 766 clash := map[string]string{} 767 for _, s := range list { 768 fold := toFold(s) 769 if t := clash[fold]; t != "" { 770 if s > t { 771 s, t = t, s 772 } 773 return s, t 774 } 775 clash[fold] = s 776 } 777 return "", "" 778 }