Source file 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  //go:generate ./mkalldocs.sh
     6  
     7  package main
     8  
     9  import (
    10  	"cmd/go/internal/workcmd"
    11  	"context"
    12  	"flag"
    13  	"fmt"
    14  	"internal/buildcfg"
    15  	"log"
    16  	"os"
    17  	"path/filepath"
    18  	"runtime"
    19  	"strings"
    20  
    21  	"cmd/go/internal/base"
    22  	"cmd/go/internal/bug"
    23  	"cmd/go/internal/cfg"
    24  	"cmd/go/internal/clean"
    25  	"cmd/go/internal/doc"
    26  	"cmd/go/internal/envcmd"
    27  	"cmd/go/internal/fix"
    28  	"cmd/go/internal/fmtcmd"
    29  	"cmd/go/internal/generate"
    30  	"cmd/go/internal/get"
    31  	"cmd/go/internal/help"
    32  	"cmd/go/internal/list"
    33  	"cmd/go/internal/modcmd"
    34  	"cmd/go/internal/modfetch"
    35  	"cmd/go/internal/modget"
    36  	"cmd/go/internal/modload"
    37  	"cmd/go/internal/run"
    38  	"cmd/go/internal/test"
    39  	"cmd/go/internal/tool"
    40  	"cmd/go/internal/trace"
    41  	"cmd/go/internal/version"
    42  	"cmd/go/internal/vet"
    43  	"cmd/go/internal/work"
    44  )
    45  
    46  func init() {
    47  	base.Go.Commands = []*base.Command{
    48  		bug.CmdBug,
    49  		work.CmdBuild,
    50  		clean.CmdClean,
    51  		doc.CmdDoc,
    52  		envcmd.CmdEnv,
    53  		fix.CmdFix,
    54  		fmtcmd.CmdFmt,
    55  		generate.CmdGenerate,
    56  		modget.CmdGet,
    57  		work.CmdInstall,
    58  		list.CmdList,
    59  		modcmd.CmdMod,
    60  		workcmd.CmdWork,
    61  		run.CmdRun,
    62  		test.CmdTest,
    63  		tool.CmdTool,
    64  		version.CmdVersion,
    65  		vet.CmdVet,
    66  
    67  		help.HelpBuildConstraint,
    68  		help.HelpBuildmode,
    69  		help.HelpC,
    70  		help.HelpCache,
    71  		help.HelpEnvironment,
    72  		help.HelpFileType,
    73  		modload.HelpGoMod,
    74  		help.HelpGopath,
    75  		get.HelpGopathGet,
    76  		modfetch.HelpGoproxy,
    77  		help.HelpImportPath,
    78  		modload.HelpModules,
    79  		modget.HelpModuleGet,
    80  		modfetch.HelpModuleAuth,
    81  		help.HelpPackages,
    82  		modfetch.HelpPrivate,
    83  		test.HelpTestflag,
    84  		test.HelpTestfunc,
    85  		modget.HelpVCS,
    86  	}
    87  }
    88  
    89  func main() {
    90  	_ = go11tag
    91  	flag.Usage = base.Usage
    92  	flag.Parse()
    93  	log.SetFlags(0)
    94  
    95  	args := flag.Args()
    96  	if len(args) < 1 {
    97  		base.Usage()
    98  	}
    99  
   100  	if args[0] == "get" || args[0] == "help" {
   101  		if !modload.WillBeEnabled() {
   102  			// Replace module-aware get with GOPATH get if appropriate.
   103  			*modget.CmdGet = *get.CmdGet
   104  		}
   105  	}
   106  
   107  	cfg.CmdName = args[0] // for error messages
   108  	if args[0] == "help" {
   109  		help.Help(os.Stdout, args[1:])
   110  		return
   111  	}
   112  
   113  	// Diagnose common mistake: GOPATH==GOROOT.
   114  	// This setting is equivalent to not setting GOPATH at all,
   115  	// which is not what most people want when they do it.
   116  	if gopath := cfg.BuildContext.GOPATH; filepath.Clean(gopath) == filepath.Clean(runtime.GOROOT()) {
   117  		fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath)
   118  	} else {
   119  		for _, p := range filepath.SplitList(gopath) {
   120  			// Some GOPATHs have empty directory elements - ignore them.
   121  			// See issue 21928 for details.
   122  			if p == "" {
   123  				continue
   124  			}
   125  			// Note: using HasPrefix instead of Contains because a ~ can appear
   126  			// in the middle of directory elements, such as /tmp/git-1.8.2~rc3
   127  			// or C:\PROGRA~1. Only ~ as a path prefix has meaning to the shell.
   128  			if strings.HasPrefix(p, "~") {
   129  				fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot start with shell metacharacter '~': %q\n", p)
   130  				os.Exit(2)
   131  			}
   132  			if !filepath.IsAbs(p) {
   133  				if cfg.Getenv("GOPATH") == "" {
   134  					// We inferred $GOPATH from $HOME and did a bad job at it.
   135  					// Instead of dying, uninfer it.
   136  					cfg.BuildContext.GOPATH = ""
   137  				} else {
   138  					fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nFor more details see: 'go help gopath'\n", p)
   139  					os.Exit(2)
   140  				}
   141  			}
   142  		}
   143  	}
   144  
   145  	if cfg.GOROOT == "" {
   146  		fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: 'go' binary is trimmed and GOROOT is not set\n")
   147  		os.Exit(2)
   148  	}
   149  	if fi, err := os.Stat(cfg.GOROOT); err != nil || !fi.IsDir() {
   150  		fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: %v\n", cfg.GOROOT)
   151  		os.Exit(2)
   152  	}
   153  
   154  BigCmdLoop:
   155  	for bigCmd := base.Go; ; {
   156  		for _, cmd := range bigCmd.Commands {
   157  			if cmd.Name() != args[0] {
   158  				continue
   159  			}
   160  			if len(cmd.Commands) > 0 {
   161  				bigCmd = cmd
   162  				args = args[1:]
   163  				if len(args) == 0 {
   164  					help.PrintUsage(os.Stderr, bigCmd)
   165  					base.SetExitStatus(2)
   166  					base.Exit()
   167  				}
   168  				if args[0] == "help" {
   169  					// Accept 'go mod help' and 'go mod help foo' for 'go help mod' and 'go help mod foo'.
   170  					help.Help(os.Stdout, append(strings.Split(cfg.CmdName, " "), args[1:]...))
   171  					return
   172  				}
   173  				cfg.CmdName += " " + args[0]
   174  				continue BigCmdLoop
   175  			}
   176  			if !cmd.Runnable() {
   177  				continue
   178  			}
   179  			invoke(cmd, args)
   180  			base.Exit()
   181  			return
   182  		}
   183  		helpArg := ""
   184  		if i := strings.LastIndex(cfg.CmdName, " "); i >= 0 {
   185  			helpArg = " " + cfg.CmdName[:i]
   186  		}
   187  		fmt.Fprintf(os.Stderr, "go %s: unknown command\nRun 'go help%s' for usage.\n", cfg.CmdName, helpArg)
   188  		base.SetExitStatus(2)
   189  		base.Exit()
   190  	}
   191  }
   192  
   193  func invoke(cmd *base.Command, args []string) {
   194  	// 'go env' handles checking the build config
   195  	if cmd != envcmd.CmdEnv {
   196  		buildcfg.Check()
   197  		if cfg.ExperimentErr != nil {
   198  			base.Fatalf("go: %v", cfg.ExperimentErr)
   199  		}
   200  	}
   201  
   202  	// Set environment (GOOS, GOARCH, etc) explicitly.
   203  	// In theory all the commands we invoke should have
   204  	// the same default computation of these as we do,
   205  	// but in practice there might be skew
   206  	// This makes sure we all agree.
   207  	cfg.OrigEnv = os.Environ()
   208  	cfg.CmdEnv = envcmd.MkEnv()
   209  	for _, env := range cfg.CmdEnv {
   210  		if os.Getenv(env.Name) != env.Value {
   211  			os.Setenv(env.Name, env.Value)
   212  		}
   213  	}
   214  
   215  	cmd.Flag.Usage = func() { cmd.Usage() }
   216  	if cmd.CustomFlags {
   217  		args = args[1:]
   218  	} else {
   219  		base.SetFromGOFLAGS(&cmd.Flag)
   220  		cmd.Flag.Parse(args[1:])
   221  		args = cmd.Flag.Args()
   222  	}
   223  	ctx := maybeStartTrace(context.Background())
   224  	ctx, span := trace.StartSpan(ctx, fmt.Sprint("Running ", cmd.Name(), " command"))
   225  	cmd.Run(ctx, cmd, args)
   226  	span.Done()
   227  }
   228  
   229  func init() {
   230  	base.Usage = mainUsage
   231  }
   232  
   233  func mainUsage() {
   234  	help.PrintUsage(os.Stderr, base.Go)
   235  	os.Exit(2)
   236  }
   237  
   238  func maybeStartTrace(pctx context.Context) context.Context {
   239  	if cfg.DebugTrace == "" {
   240  		return pctx
   241  	}
   242  
   243  	ctx, close, err := trace.Start(pctx, cfg.DebugTrace)
   244  	if err != nil {
   245  		base.Fatalf("failed to start trace: %v", err)
   246  	}
   247  	base.AtExit(func() {
   248  		if err := close(); err != nil {
   249  			base.Fatalf("failed to stop trace: %v", err)
   250  		}
   251  	})
   252  
   253  	return ctx
   254  }
   255  

View as plain text