@@ -8,13 +8,16 @@ package run
8
8
import (
9
9
"context"
10
10
"fmt"
11
+ "go/build"
11
12
"os"
12
13
"path"
14
+ "path/filepath"
13
15
"strings"
14
16
15
17
"cmd/go/internal/base"
16
18
"cmd/go/internal/cfg"
17
19
"cmd/go/internal/load"
20
+ "cmd/go/internal/modload"
18
21
"cmd/go/internal/str"
19
22
"cmd/go/internal/work"
20
23
)
@@ -24,10 +27,21 @@ var CmdRun = &base.Command{
24
27
Short : "compile and run Go program" ,
25
28
Long : `
26
29
Run compiles and runs the named main Go package.
27
- Typically the package is specified as a list of .go source files from a single directory,
28
- but it may also be an import path, file system path, or pattern
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
29
32
matching a single known package, as in 'go run .' or 'go run my/cmd'.
30
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
+
31
45
By default, 'go run' runs the compiled binary directly: 'a.out arguments...'.
32
46
If the -exec flag is given, 'go run' invokes the binary using xprog:
33
47
'xprog a.out arguments...'.
@@ -59,10 +73,21 @@ func printStderr(args ...interface{}) (int, error) {
59
73
}
60
74
61
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
+ }
62
86
work .BuildInit ()
63
87
var b work.Builder
64
88
b .Init ()
65
89
b .Print = printStderr
90
+
66
91
i := 0
67
92
for i < len (args ) && strings .HasSuffix (args [i ], ".go" ) {
68
93
i ++
@@ -79,28 +104,40 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) {
79
104
}
80
105
p = load .GoFilesPackage (ctx , load.PackageOpts {}, files )
81
106
} else if len (args ) > 0 && ! strings .HasPrefix (args [0 ], "-" ) {
82
- pkgs := load .PackagesAndErrors (ctx , load.PackageOpts {}, args [:1 ])
107
+ arg := args [0 ]
108
+ pkgOpts := load.PackageOpts {MainOnly : true }
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
+
83
120
if len (pkgs ) == 0 {
84
- base .Fatalf ("go run: no packages loaded from %s" , args [ 0 ] )
121
+ base .Fatalf ("go run: no packages loaded from %s" , arg )
85
122
}
86
123
if len (pkgs ) > 1 {
87
124
var names []string
88
125
for _ , p := range pkgs {
89
126
names = append (names , p .ImportPath )
90
127
}
91
- base .Fatalf ("go run: pattern %s matches multiple packages:\n \t %s" , args [ 0 ] , strings .Join (names , "\n \t " ))
128
+ base .Fatalf ("go run: pattern %s matches multiple packages:\n \t %s" , arg , strings .Join (names , "\n \t " ))
92
129
}
93
130
p = pkgs [0 ]
94
131
i ++
95
132
} else {
96
133
base .Fatalf ("go run: no go files listed" )
97
134
}
98
135
cmdArgs := args [i :]
99
- load .CheckPackageErrors ([]* load.Package {p })
100
-
101
136
if p .Name != "main" {
102
137
base .Fatalf ("go run: cannot run non-main package" )
103
138
}
139
+ load .CheckPackageErrors ([]* load.Package {p })
140
+
104
141
p .Internal .OmitDebug = true
105
142
p .Target = "" // must build - not up to date
106
143
if p .Internal .CmdlineFiles {
@@ -123,11 +160,34 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) {
123
160
} else {
124
161
p .Internal .ExeName = path .Base (p .ImportPath )
125
162
}
163
+
126
164
a1 := b .LinkAction (work .ModeBuild , work .ModeBuild , p )
127
165
a := & work.Action {Mode : "go run" , Func : buildRunProgram , Args : cmdArgs , Deps : []* work.Action {a1 }}
128
166
b .Do (ctx , a )
129
167
}
130
168
169
+ // shouldUseOutsideModuleMode returns whether 'go run' will load packages in
170
+ // module-aware mode, ignoring the go.mod file in the current directory. It
171
+ // returns true if the first argument contains "@", does not begin with "-"
172
+ // (resembling a flag) or end with ".go" (a file). The argument must not be a
173
+ // local or absolute file path.
174
+ //
175
+ // These rules are slightly different than other commands. Whether or not
176
+ // 'go run' uses this mode, it interprets arguments ending with ".go" as files
177
+ // and uses arguments up to the last ".go" argument to comprise the package.
178
+ // If there are no ".go" arguments, only the first argument is interpreted
179
+ // as a package path, since there can be only one package.
180
+ func shouldUseOutsideModuleMode (args []string ) bool {
181
+ // NOTE: "@" not allowed in import paths, but it is allowed in non-canonical
182
+ // versions.
183
+ return len (args ) > 0 &&
184
+ ! strings .HasSuffix (args [0 ], ".go" ) &&
185
+ ! strings .HasPrefix (args [0 ], "-" ) &&
186
+ strings .Contains (args [0 ], "@" ) &&
187
+ ! build .IsLocalImport (args [0 ]) &&
188
+ ! filepath .IsAbs (args [0 ])
189
+ }
190
+
131
191
// buildRunProgram is the action for running a binary that has already
132
192
// been compiled. We ignore exit status.
133
193
func buildRunProgram (b * work.Builder , ctx context.Context , a * work.Action ) error {
0 commit comments