summaryrefslogtreecommitdiffstats
path: root/src/cmd/go/internal/run/run.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/go/internal/run/run.go')
-rw-r--r--src/cmd/go/internal/run/run.go212
1 files changed, 212 insertions, 0 deletions
diff --git a/src/cmd/go/internal/run/run.go b/src/cmd/go/internal/run/run.go
new file mode 100644
index 0000000..a97d975
--- /dev/null
+++ b/src/cmd/go/internal/run/run.go
@@ -0,0 +1,212 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package run implements the “go run” command.
+package run
+
+import (
+ "context"
+ "go/build"
+ "path"
+ "path/filepath"
+ "strings"
+
+ "cmd/go/internal/base"
+ "cmd/go/internal/cfg"
+ "cmd/go/internal/load"
+ "cmd/go/internal/modload"
+ "cmd/go/internal/str"
+ "cmd/go/internal/work"
+)
+
+var CmdRun = &base.Command{
+ UsageLine: "go run [build flags] [-exec xprog] package [arguments...]",
+ Short: "compile and run Go program",
+ Long: `
+Run compiles and runs the named main Go package.
+Typically the package is specified as a list of .go source files from a single
+directory, but it may also be an import path, file system path, or pattern
+matching a single known package, as in 'go run .' or 'go run my/cmd'.
+
+If the package argument has a version suffix (like @latest or @v1.0.0),
+"go run" builds the program in module-aware mode, ignoring the go.mod file in
+the current directory or any parent directory, if there is one. This is useful
+for running programs without affecting the dependencies of the main module.
+
+If the package argument doesn't have a version suffix, "go run" may run in
+module-aware mode or GOPATH mode, depending on the GO111MODULE environment
+variable and the presence of a go.mod file. See 'go help modules' for details.
+If module-aware mode is enabled, "go run" runs in the context of the main
+module.
+
+By default, 'go run' runs the compiled binary directly: 'a.out arguments...'.
+If the -exec flag is given, 'go run' invokes the binary using xprog:
+ 'xprog a.out arguments...'.
+If the -exec flag is not given, GOOS or GOARCH is different from the system
+default, and a program named go_$GOOS_$GOARCH_exec can be found
+on the current search path, 'go run' invokes the binary using that program,
+for example 'go_js_wasm_exec a.out arguments...'. This allows execution of
+cross-compiled programs when a simulator or other execution method is
+available.
+
+By default, 'go run' compiles the binary without generating the information
+used by debuggers, to reduce build time. To include debugger information in
+the binary, use 'go build'.
+
+The exit status of Run is not the exit status of the compiled binary.
+
+For more about build flags, see 'go help build'.
+For more about specifying packages, see 'go help packages'.
+
+See also: go build.
+ `,
+}
+
+func init() {
+ CmdRun.Run = runRun // break init loop
+
+ work.AddBuildFlags(CmdRun, work.DefaultBuildFlags)
+ if cfg.Experiment != nil && cfg.Experiment.CoverageRedesign {
+ work.AddCoverFlags(CmdRun, nil)
+ }
+ CmdRun.Flag.Var((*base.StringsFlag)(&work.ExecCmd), "exec", "")
+}
+
+func runRun(ctx context.Context, cmd *base.Command, args []string) {
+ if shouldUseOutsideModuleMode(args) {
+ // Set global module flags for 'go run cmd@version'.
+ // This must be done before modload.Init, but we need to call work.BuildInit
+ // before loading packages, since it affects package locations, e.g.,
+ // for -race and -msan.
+ modload.ForceUseModules = true
+ modload.RootMode = modload.NoRoot
+ modload.AllowMissingModuleImports()
+ modload.Init()
+ } else {
+ modload.InitWorkfile()
+ }
+
+ work.BuildInit()
+ b := work.NewBuilder("")
+ defer func() {
+ if err := b.Close(); err != nil {
+ base.Fatal(err)
+ }
+ }()
+
+ i := 0
+ for i < len(args) && strings.HasSuffix(args[i], ".go") {
+ i++
+ }
+ pkgOpts := load.PackageOpts{MainOnly: true}
+ var p *load.Package
+ if i > 0 {
+ files := args[:i]
+ for _, file := range files {
+ if strings.HasSuffix(file, "_test.go") {
+ // GoFilesPackage is going to assign this to TestGoFiles.
+ // Reject since it won't be part of the build.
+ base.Fatalf("go: cannot run *_test.go files (%s)", file)
+ }
+ }
+ p = load.GoFilesPackage(ctx, pkgOpts, files)
+ } else if len(args) > 0 && !strings.HasPrefix(args[0], "-") {
+ arg := args[0]
+ var pkgs []*load.Package
+ if strings.Contains(arg, "@") && !build.IsLocalImport(arg) && !filepath.IsAbs(arg) {
+ var err error
+ pkgs, err = load.PackagesAndErrorsOutsideModule(ctx, pkgOpts, args[:1])
+ if err != nil {
+ base.Fatal(err)
+ }
+ } else {
+ pkgs = load.PackagesAndErrors(ctx, pkgOpts, args[:1])
+ }
+
+ if len(pkgs) == 0 {
+ base.Fatalf("go: no packages loaded from %s", arg)
+ }
+ if len(pkgs) > 1 {
+ var names []string
+ for _, p := range pkgs {
+ names = append(names, p.ImportPath)
+ }
+ base.Fatalf("go: pattern %s matches multiple packages:\n\t%s", arg, strings.Join(names, "\n\t"))
+ }
+ p = pkgs[0]
+ i++
+ } else {
+ base.Fatalf("go: no go files listed")
+ }
+ cmdArgs := args[i:]
+ load.CheckPackageErrors([]*load.Package{p})
+
+ if cfg.Experiment.CoverageRedesign && cfg.BuildCover {
+ load.PrepareForCoverageBuild([]*load.Package{p})
+ }
+
+ p.Internal.OmitDebug = true
+ p.Target = "" // must build - not up to date
+ if p.Internal.CmdlineFiles {
+ //set executable name if go file is given as cmd-argument
+ var src string
+ if len(p.GoFiles) > 0 {
+ src = p.GoFiles[0]
+ } else if len(p.CgoFiles) > 0 {
+ src = p.CgoFiles[0]
+ } else {
+ // this case could only happen if the provided source uses cgo
+ // while cgo is disabled.
+ hint := ""
+ if !cfg.BuildContext.CgoEnabled {
+ hint = " (cgo is disabled)"
+ }
+ base.Fatalf("go: no suitable source files%s", hint)
+ }
+ p.Internal.ExeName = src[:len(src)-len(".go")]
+ } else {
+ p.Internal.ExeName = path.Base(p.ImportPath)
+ }
+
+ a1 := b.LinkAction(work.ModeBuild, work.ModeBuild, p)
+ a := &work.Action{Mode: "go run", Actor: work.ActorFunc(buildRunProgram), Args: cmdArgs, Deps: []*work.Action{a1}}
+ b.Do(ctx, a)
+}
+
+// shouldUseOutsideModuleMode returns whether 'go run' will load packages in
+// module-aware mode, ignoring the go.mod file in the current directory. It
+// returns true if the first argument contains "@", does not begin with "-"
+// (resembling a flag) or end with ".go" (a file). The argument must not be a
+// local or absolute file path.
+//
+// These rules are slightly different than other commands. Whether or not
+// 'go run' uses this mode, it interprets arguments ending with ".go" as files
+// and uses arguments up to the last ".go" argument to comprise the package.
+// If there are no ".go" arguments, only the first argument is interpreted
+// as a package path, since there can be only one package.
+func shouldUseOutsideModuleMode(args []string) bool {
+ // NOTE: "@" not allowed in import paths, but it is allowed in non-canonical
+ // versions.
+ return len(args) > 0 &&
+ !strings.HasSuffix(args[0], ".go") &&
+ !strings.HasPrefix(args[0], "-") &&
+ strings.Contains(args[0], "@") &&
+ !build.IsLocalImport(args[0]) &&
+ !filepath.IsAbs(args[0])
+}
+
+// buildRunProgram is the action for running a binary that has already
+// been compiled. We ignore exit status.
+func buildRunProgram(b *work.Builder, ctx context.Context, a *work.Action) error {
+ cmdline := str.StringList(work.FindExecCmd(), a.Deps[0].Target, a.Args)
+ if cfg.BuildN || cfg.BuildX {
+ b.Shell(a).ShowCmd("", "%s", strings.Join(cmdline, " "))
+ if cfg.BuildN {
+ return nil
+ }
+ }
+
+ base.RunStdin(cmdline)
+ return nil
+}