Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/go/internal/run/run.go

Documentation: cmd/go/internal/run

     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 run implements the ``go run'' command.
     6  package run
     7  
     8  import (
     9  	"context"
    10  	"fmt"
    11  	"go/build"
    12  	"os"
    13  	"path"
    14  	"path/filepath"
    15  	"strings"
    16  
    17  	"cmd/go/internal/base"
    18  	"cmd/go/internal/cfg"
    19  	"cmd/go/internal/load"
    20  	"cmd/go/internal/modload"
    21  	"cmd/go/internal/str"
    22  	"cmd/go/internal/work"
    23  )
    24  
    25  var CmdRun = &base.Command{
    26  	UsageLine: "go run [build flags] [-exec xprog] package [arguments...]",
    27  	Short:     "compile and run Go program",
    28  	Long: `
    29  Run compiles and runs the named main Go package.
    30  Typically the package is specified as a list of .go source files from a single
    31  directory, but it may also be an import path, file system path, or pattern
    32  matching a single known package, as in 'go run .' or 'go run my/cmd'.
    33  
    34  If the package argument has a version suffix (like @latest or @v1.0.0),
    35  "go run" builds the program in module-aware mode, ignoring the go.mod file in
    36  the current directory or any parent directory, if there is one. This is useful
    37  for running programs without affecting the dependencies of the main module.
    38  
    39  If the package argument doesn't have a version suffix, "go run" may run in
    40  module-aware mode or GOPATH mode, depending on the GO111MODULE environment
    41  variable and the presence of a go.mod file. See 'go help modules' for details.
    42  If module-aware mode is enabled, "go run" runs in the context of the main
    43  module.
    44  
    45  By default, 'go run' runs the compiled binary directly: 'a.out arguments...'.
    46  If the -exec flag is given, 'go run' invokes the binary using xprog:
    47  	'xprog a.out arguments...'.
    48  If the -exec flag is not given, GOOS or GOARCH is different from the system
    49  default, and a program named go_$GOOS_$GOARCH_exec can be found
    50  on the current search path, 'go run' invokes the binary using that program,
    51  for example 'go_js_wasm_exec a.out arguments...'. This allows execution of
    52  cross-compiled programs when a simulator or other execution method is
    53  available.
    54  
    55  The exit status of Run is not the exit status of the compiled binary.
    56  
    57  For more about build flags, see 'go help build'.
    58  For more about specifying packages, see 'go help packages'.
    59  
    60  See also: go build.
    61  	`,
    62  }
    63  
    64  func init() {
    65  	CmdRun.Run = runRun // break init loop
    66  
    67  	work.AddBuildFlags(CmdRun, work.DefaultBuildFlags)
    68  	CmdRun.Flag.Var((*base.StringsFlag)(&work.ExecCmd), "exec", "")
    69  }
    70  
    71  func printStderr(args ...interface{}) (int, error) {
    72  	return fmt.Fprint(os.Stderr, args...)
    73  }
    74  
    75  func runRun(ctx context.Context, cmd *base.Command, args []string) {
    76  	if shouldUseOutsideModuleMode(args) {
    77  		// Set global module flags for 'go run cmd@version'.
    78  		// This must be done before modload.Init, but we need to call work.BuildInit
    79  		// before loading packages, since it affects package locations, e.g.,
    80  		// for -race and -msan.
    81  		modload.ForceUseModules = true
    82  		modload.RootMode = modload.NoRoot
    83  		modload.AllowMissingModuleImports()
    84  		modload.Init()
    85  	}
    86  	work.BuildInit()
    87  	var b work.Builder
    88  	b.Init()
    89  	b.Print = printStderr
    90  
    91  	i := 0
    92  	for i < len(args) && strings.HasSuffix(args[i], ".go") {
    93  		i++
    94  	}
    95  	pkgOpts := load.PackageOpts{MainOnly: true}
    96  	var p *load.Package
    97  	if i > 0 {
    98  		files := args[:i]
    99  		for _, file := range files {
   100  			if strings.HasSuffix(file, "_test.go") {
   101  				// GoFilesPackage is going to assign this to TestGoFiles.
   102  				// Reject since it won't be part of the build.
   103  				base.Fatalf("go run: cannot run *_test.go files (%s)", file)
   104  			}
   105  		}
   106  		p = load.GoFilesPackage(ctx, pkgOpts, files)
   107  	} else if len(args) > 0 && !strings.HasPrefix(args[0], "-") {
   108  		arg := args[0]
   109  		var pkgs []*load.Package
   110  		if strings.Contains(arg, "@") && !build.IsLocalImport(arg) && !filepath.IsAbs(arg) {
   111  			var err error
   112  			pkgs, err = load.PackagesAndErrorsOutsideModule(ctx, pkgOpts, args[:1])
   113  			if err != nil {
   114  				base.Fatalf("go run: %v", err)
   115  			}
   116  		} else {
   117  			pkgs = load.PackagesAndErrors(ctx, pkgOpts, args[:1])
   118  		}
   119  
   120  		if len(pkgs) == 0 {
   121  			base.Fatalf("go run: no packages loaded from %s", arg)
   122  		}
   123  		if len(pkgs) > 1 {
   124  			var names []string
   125  			for _, p := range pkgs {
   126  				names = append(names, p.ImportPath)
   127  			}
   128  			base.Fatalf("go run: pattern %s matches multiple packages:\n\t%s", arg, strings.Join(names, "\n\t"))
   129  		}
   130  		p = pkgs[0]
   131  		i++
   132  	} else {
   133  		base.Fatalf("go run: no go files listed")
   134  	}
   135  	cmdArgs := args[i:]
   136  	load.CheckPackageErrors([]*load.Package{p})
   137  
   138  	p.Internal.OmitDebug = true
   139  	p.Target = "" // must build - not up to date
   140  	if p.Internal.CmdlineFiles {
   141  		//set executable name if go file is given as cmd-argument
   142  		var src string
   143  		if len(p.GoFiles) > 0 {
   144  			src = p.GoFiles[0]
   145  		} else if len(p.CgoFiles) > 0 {
   146  			src = p.CgoFiles[0]
   147  		} else {
   148  			// this case could only happen if the provided source uses cgo
   149  			// while cgo is disabled.
   150  			hint := ""
   151  			if !cfg.BuildContext.CgoEnabled {
   152  				hint = " (cgo is disabled)"
   153  			}
   154  			base.Fatalf("go run: no suitable source files%s", hint)
   155  		}
   156  		p.Internal.ExeName = src[:len(src)-len(".go")]
   157  	} else {
   158  		p.Internal.ExeName = path.Base(p.ImportPath)
   159  	}
   160  
   161  	a1 := b.LinkAction(work.ModeBuild, work.ModeBuild, p)
   162  	a := &work.Action{Mode: "go run", Func: buildRunProgram, Args: cmdArgs, Deps: []*work.Action{a1}}
   163  	b.Do(ctx, a)
   164  }
   165  
   166  // shouldUseOutsideModuleMode returns whether 'go run' will load packages in
   167  // module-aware mode, ignoring the go.mod file in the current directory. It
   168  // returns true if the first argument contains "@", does not begin with "-"
   169  // (resembling a flag) or end with ".go" (a file). The argument must not be a
   170  // local or absolute file path.
   171  //
   172  // These rules are slightly different than other commands. Whether or not
   173  // 'go run' uses this mode, it interprets arguments ending with ".go" as files
   174  // and uses arguments up to the last ".go" argument to comprise the package.
   175  // If there are no ".go" arguments, only the first argument is interpreted
   176  // as a package path, since there can be only one package.
   177  func shouldUseOutsideModuleMode(args []string) bool {
   178  	// NOTE: "@" not allowed in import paths, but it is allowed in non-canonical
   179  	// versions.
   180  	return len(args) > 0 &&
   181  		!strings.HasSuffix(args[0], ".go") &&
   182  		!strings.HasPrefix(args[0], "-") &&
   183  		strings.Contains(args[0], "@") &&
   184  		!build.IsLocalImport(args[0]) &&
   185  		!filepath.IsAbs(args[0])
   186  }
   187  
   188  // buildRunProgram is the action for running a binary that has already
   189  // been compiled. We ignore exit status.
   190  func buildRunProgram(b *work.Builder, ctx context.Context, a *work.Action) error {
   191  	cmdline := str.StringList(work.FindExecCmd(), a.Deps[0].Target, a.Args)
   192  	if cfg.BuildN || cfg.BuildX {
   193  		b.Showcmd("", "%s", strings.Join(cmdline, " "))
   194  		if cfg.BuildN {
   195  			return nil
   196  		}
   197  	}
   198  
   199  	base.RunStdin(cmdline)
   200  	return nil
   201  }
   202  

View as plain text