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