]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/go/main.go
[dev.cc] all: merge master (5f1efe7) into dev.cc
[gostls13.git] / src / cmd / go / main.go
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.Runnable() {
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 of type " + fmt.Sprintf("%T", arg))
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 }