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