@@ -6,19 +6,24 @@ package main
6
6
7
7
import (
8
8
"bytes"
9
+ "fmt"
9
10
"log"
10
11
"os"
11
12
"os/exec"
12
13
"path/filepath"
14
+ "regexp"
13
15
"strings"
14
16
"sync"
17
+
18
+ "golang.org/x/mod/semver"
15
19
)
16
20
17
21
// A Dir describes a directory holding code by specifying
18
22
// the expected import path and the file system directory.
19
23
type Dir struct {
20
24
importPath string // import path for that dir
21
25
dir string // file system directory
26
+ inModule bool
22
27
}
23
28
24
29
// Dirs is a structure for scanning the directory tree.
@@ -113,9 +118,14 @@ func (d *Dirs) bfsWalkRoot(root Dir) {
113
118
if name [0 ] == '.' || name [0 ] == '_' || name == "testdata" {
114
119
continue
115
120
}
116
- // Ignore vendor when using modules.
117
- if usingModules && name == "vendor" {
118
- continue
121
+ // When in a module, ignore vendor directories and stop at module boundaries.
122
+ if root .inModule {
123
+ if name == "vendor" {
124
+ continue
125
+ }
126
+ if fi , err := os .Stat (filepath .Join (dir , name , "go.mod" )); err == nil && ! fi .IsDir () {
127
+ continue
128
+ }
119
129
}
120
130
// Remember this (fully qualified) directory for the next pass.
121
131
next = append (next , filepath .Join (dir , name ))
@@ -129,7 +139,7 @@ func (d *Dirs) bfsWalkRoot(root Dir) {
129
139
}
130
140
importPath += filepath .ToSlash (dir [len (root .dir )+ 1 :])
131
141
}
132
- d .scan <- Dir {importPath , dir }
142
+ d .scan <- Dir {importPath , dir , root . inModule }
133
143
}
134
144
}
135
145
@@ -156,14 +166,20 @@ var codeRootsCache struct {
156
166
var usingModules bool
157
167
158
168
func findCodeRoots () []Dir {
159
- list := []Dir {{"" , filepath .Join (buildCtx .GOROOT , "src" )}}
160
-
169
+ var list []Dir
161
170
if ! testGOPATH {
162
171
// Check for use of modules by 'go env GOMOD',
163
172
// which reports a go.mod file path if modules are enabled.
164
173
stdout , _ := exec .Command ("go" , "env" , "GOMOD" ).Output ()
165
174
gomod := string (bytes .TrimSpace (stdout ))
175
+
166
176
usingModules = len (gomod ) > 0
177
+ if usingModules {
178
+ list = append (list ,
179
+ Dir {dir : filepath .Join (buildCtx .GOROOT , "src" ), inModule : true },
180
+ Dir {importPath : "cmd" , dir : filepath .Join (buildCtx .GOROOT , "src" , "cmd" ), inModule : true })
181
+ }
182
+
167
183
if gomod == os .DevNull {
168
184
// Modules are enabled, but the working directory is outside any module.
169
185
// We can still access std, cmd, and packages specified as source files
@@ -174,8 +190,9 @@ func findCodeRoots() []Dir {
174
190
}
175
191
176
192
if ! usingModules {
193
+ list = append (list , Dir {dir : filepath .Join (buildCtx .GOROOT , "src" )})
177
194
for _ , root := range splitGopath () {
178
- list = append (list , Dir {"" , filepath .Join (root , "src" )})
195
+ list = append (list , Dir {dir : filepath .Join (root , "src" )})
179
196
}
180
197
return list
181
198
}
@@ -185,6 +202,21 @@ func findCodeRoots() []Dir {
185
202
// to handle the entire file system search and become go/packages,
186
203
// but for now enumerating the module roots lets us fit modules
187
204
// into the current code with as few changes as possible.
205
+ mainMod , vendorEnabled , err := vendorEnabled ()
206
+ if err != nil {
207
+ return list
208
+ }
209
+ if vendorEnabled {
210
+ // Add the vendor directory to the search path ahead of "std".
211
+ // That way, if the main module *is* "std", we will identify the path
212
+ // without the "vendor/" prefix before the one with that prefix.
213
+ list = append ([]Dir {{dir : filepath .Join (mainMod .Dir , "vendor" ), inModule : false }}, list ... )
214
+ if mainMod .Path != "std" {
215
+ list = append (list , Dir {importPath : mainMod .Path , dir : mainMod .Dir , inModule : true })
216
+ }
217
+ return list
218
+ }
219
+
188
220
cmd := exec .Command ("go" , "list" , "-m" , "-f={{.Path}}\t {{.Dir}}" , "all" )
189
221
cmd .Stderr = os .Stderr
190
222
out , _ := cmd .Output ()
@@ -195,9 +227,77 @@ func findCodeRoots() []Dir {
195
227
}
196
228
path , dir := line [:i ], line [i + 1 :]
197
229
if dir != "" {
198
- list = append (list , Dir {path , dir })
230
+ list = append (list , Dir {importPath : path , dir : dir , inModule : true })
199
231
}
200
232
}
201
233
202
234
return list
203
235
}
236
+
237
+ // The functions below are derived from x/tools/internal/imports at CL 203017.
238
+
239
+ type moduleJSON struct {
240
+ Path , Dir , GoVersion string
241
+ }
242
+
243
+ var modFlagRegexp = regexp .MustCompile (`-mod[ =](\w+)` )
244
+
245
+ // vendorEnabled indicates if vendoring is enabled.
246
+ // Inspired by setDefaultBuildMod in modload/init.go
247
+ func vendorEnabled () (* moduleJSON , bool , error ) {
248
+ mainMod , go114 , err := getMainModuleAnd114 ()
249
+ if err != nil {
250
+ return nil , false , err
251
+ }
252
+
253
+ stdout , _ := exec .Command ("go" , "env" , "GOFLAGS" ).Output ()
254
+ goflags := string (bytes .TrimSpace (stdout ))
255
+ matches := modFlagRegexp .FindStringSubmatch (goflags )
256
+ var modFlag string
257
+ if len (matches ) != 0 {
258
+ modFlag = matches [1 ]
259
+ }
260
+ if modFlag != "" {
261
+ // Don't override an explicit '-mod=' argument.
262
+ return mainMod , modFlag == "vendor" , nil
263
+ }
264
+ if mainMod == nil || ! go114 {
265
+ return mainMod , false , nil
266
+ }
267
+ // Check 1.14's automatic vendor mode.
268
+ if fi , err := os .Stat (filepath .Join (mainMod .Dir , "vendor" )); err == nil && fi .IsDir () {
269
+ if mainMod .GoVersion != "" && semver .Compare ("v" + mainMod .GoVersion , "v1.14" ) >= 0 {
270
+ // The Go version is at least 1.14, and a vendor directory exists.
271
+ // Set -mod=vendor by default.
272
+ return mainMod , true , nil
273
+ }
274
+ }
275
+ return mainMod , false , nil
276
+ }
277
+
278
+ // getMainModuleAnd114 gets the main module's information and whether the
279
+ // go command in use is 1.14+. This is the information needed to figure out
280
+ // if vendoring should be enabled.
281
+ func getMainModuleAnd114 () (* moduleJSON , bool , error ) {
282
+ const format = `{{.Path}}
283
+ {{.Dir}}
284
+ {{.GoVersion}}
285
+ {{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}}
286
+ `
287
+ cmd := exec .Command ("go" , "list" , "-m" , "-f" , format )
288
+ cmd .Stderr = os .Stderr
289
+ stdout , err := cmd .Output ()
290
+ if err != nil {
291
+ return nil , false , nil
292
+ }
293
+ lines := strings .Split (string (stdout ), "\n " )
294
+ if len (lines ) < 5 {
295
+ return nil , false , fmt .Errorf ("unexpected stdout: %q" , stdout )
296
+ }
297
+ mod := & moduleJSON {
298
+ Path : lines [0 ],
299
+ Dir : lines [1 ],
300
+ GoVersion : lines [2 ],
301
+ }
302
+ return mod , lines [3 ] == "go1.14" , nil
303
+ }
0 commit comments