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