PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/src/cmd/go/main.go

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