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  }