@@ -3,12 +3,13 @@ package interp
3
3
import (
4
4
"fmt"
5
5
"io/fs"
6
- "os"
7
6
"path/filepath"
8
7
"strings"
8
+
9
+ "golang.org/x/tools/go/packages"
9
10
)
10
11
11
- // importSrc calls gta on the source code for the package identified by
12
+ // importSrc calls global tag analysis on the source code for the package identified by
12
13
// importPath. rPath is the relative path to the directory containing the source
13
14
// code for the package. It can also be "main" as a special value.
14
15
func (interp * Interpreter ) importSrc (rPath , importPath string , skipTest bool ) (string , error ) {
@@ -23,24 +24,14 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s
23
24
return name , nil
24
25
}
25
26
26
- // For relative import paths in the form "./xxx" or "../xxx", the initial
27
- // base path is the directory of the interpreter input file, or "." if no file
28
- // was provided.
29
- // In all other cases, absolute import paths are resolved from the GOPATH
30
- // and the nested "vendor" directories.
27
+ // resolve relative and absolute import paths
31
28
if isPathRelative (importPath ) {
32
29
if rPath == mainID {
33
30
rPath = "."
34
31
}
35
32
dir = filepath .Join (filepath .Dir (interp .name ), rPath , importPath )
36
- } else if dir , rPath , err = interp .pkgDir (interp .context .GOPATH , rPath , importPath ); err != nil {
37
- // Try again, assuming a root dir at the source location.
38
- if rPath , err = interp .rootFromSourceLocation (); err != nil {
39
- return "" , err
40
- }
41
- if dir , rPath , err = interp .pkgDir (interp .context .GOPATH , rPath , importPath ); err != nil {
42
- return "" , err
43
- }
33
+ } else if dir , err = interp .getPackageDir (importPath ); err != nil {
34
+ return "" , err
44
35
}
45
36
46
37
if interp .rdir [importPath ] {
@@ -171,119 +162,87 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s
171
162
return pkgName , nil
172
163
}
173
164
174
- // rootFromSourceLocation returns the path to the directory containing the input
175
- // Go file given to the interpreter, relative to $GOPATH/src.
176
- // It is meant to be called in the case when the initial input is a main package.
177
- func (interp * Interpreter ) rootFromSourceLocation () (string , error ) {
178
- sourceFile := interp .name
179
- if sourceFile == DefaultSourceName {
180
- return "" , nil
181
- }
182
- wd , err := os .Getwd ()
165
+ // getPackageDir uses the GOPATH to find the absolute path of an import path.
166
+ func (interp * Interpreter ) getPackageDir (importPath string ) (string , error ) {
167
+ // search the standard library and Go modules.
168
+ config := packages.Config {}
169
+ config .Env = append (config .Env , "GOPATH=" + interp .context .GOPATH , "GOCACHE=" + interp .opt .env ["goCache" ], "GOTOOLDIR=" + interp .opt .env ["goToolDir" ])
170
+ pkgs , err := packages .Load (& config , importPath )
183
171
if err != nil {
184
- return "" , err
172
+ return "" , fmt . Errorf ( "an error occurred retrieving a package from the GOPATH: %v \n %v \n If Access is denied, run in administrator" , importPath , err )
185
173
}
186
- pkgDir := filepath .Join (wd , filepath .Dir (sourceFile ))
187
- root := strings .TrimPrefix (pkgDir , filepath .Join (interp .context .GOPATH , "src" )+ "/" )
188
- if root == wd {
189
- return "" , fmt .Errorf ("package location %s not in GOPATH" , pkgDir )
174
+
175
+ // confirm the import path is found.
176
+ for _ , pkg := range pkgs {
177
+ for _ , goFile := range pkg .GoFiles {
178
+ if strings .Contains (filepath .Dir (goFile ), pkg .Name ) {
179
+ return filepath .Dir (goFile ), nil
180
+ }
181
+ }
190
182
}
191
- return root , nil
192
- }
193
183
194
- // pkgDir returns the absolute path in filesystem for a package given its import path
195
- // and the root of the subtree dependencies.
196
- func (interp * Interpreter ) pkgDir (goPath string , root , importPath string ) (string , string , error ) {
197
- rPath := filepath .Join (root , "vendor" )
198
- dir := filepath .Join (goPath , "src" , rPath , importPath )
184
+ // check for certain go tools located in GOTOOLDIR.
185
+ if interp .opt .env ["goToolDir" ] != "" {
186
+ // search for the go directory before searching for packages.
187
+ // this approach prevents the computer from searching the entire filesystem.
188
+ godir , err := searchUpDirPath (interp .opt .env ["goToolDir" ], "go" , false )
189
+ if err != nil {
190
+ return "" , fmt .Errorf ("an import source could not be found: %q\n The current GOPATH=%v, GOCACHE=%v, GOTOOLDIR=%v\n %v" , importPath , interp .context .GOPATH , interp .opt .env ["goCache" ], interp .opt .env ["goToolDir" ], err )
191
+ }
199
192
200
- if _ , err := fs .Stat (interp .opt .filesystem , dir ); err == nil {
201
- return dir , rPath , nil // found!
193
+ absimportpath , err := searchDirs (godir , importPath )
194
+ if err != nil {
195
+ return "" , fmt .Errorf ("an import source could not be found: %q\n The current GOPATH=%v, GOCACHE=%v, GOTOOLDIR=%v\n %v" , importPath , interp .context .GOPATH , interp .opt .env ["goCache" ], interp .opt .env ["goToolDir" ], err )
196
+ }
197
+ return absimportpath , nil
202
198
}
203
199
204
- dir = filepath .Join (goPath , "src" , effectivePkg (root , importPath ))
200
+ return "" , fmt .Errorf ("an import source could not be found: %q. Set the GOPATH and/or GOTOOLDIR environment variable from Interpreter.Options" , importPath )
201
+ }
205
202
206
- if _ , err := fs .Stat (interp .opt .filesystem , dir ); err == nil {
207
- return dir , root , nil // found!
203
+ // searchUpDirPath searches up a directory path in order to find a target directory.
204
+ func searchUpDirPath (initial string , target string , isCaseSensitive bool ) (string , error ) {
205
+ // strings.Split always returns [:0] as filepath.Dir returns "." or the last directory.
206
+ splitdir := strings .Split (filepath .Clean (initial ), string (filepath .Separator ))
207
+ if len (splitdir ) == 1 {
208
+ return "" , fmt .Errorf ("the target directory %q is not within the path %q" , target , initial )
208
209
}
209
210
210
- if len (root ) == 0 {
211
- if interp .context .GOPATH == "" {
212
- return "" , "" , fmt .Errorf ("unable to find source related to: %q. Either the GOPATH environment variable, or the Interpreter.Options.GoPath needs to be set" , importPath )
213
- }
214
- return "" , "" , fmt .Errorf ("unable to find source related to: %q" , importPath )
211
+ updir := splitdir [len (splitdir )- 1 ]
212
+ if ! isCaseSensitive {
213
+ updir = strings .ToLower (updir )
215
214
}
216
-
217
- rootPath := filepath .Join (goPath , "src" , root )
218
- prevRoot , err := previousRoot (interp .opt .filesystem , rootPath , root )
219
- if err != nil {
220
- return "" , "" , err
215
+ if updir == target {
216
+ return initial , nil
221
217
}
222
-
223
- return interp .pkgDir (goPath , prevRoot , importPath )
218
+ return searchUpDirPath (filepath .Dir (initial ), target , isCaseSensitive )
224
219
}
225
220
226
- const vendor = "vendor"
227
-
228
- // Find the previous source root (vendor > vendor > ... > GOPATH).
229
- func previousRoot (filesystem fs.FS , rootPath , root string ) (string , error ) {
230
- rootPath = filepath .Clean (rootPath )
231
- parent , final := filepath .Split (rootPath )
232
- parent = filepath .Clean (parent )
233
-
234
- // TODO(mpl): maybe it works for the special case main, but can't be bothered for now.
235
- if root != mainID && final != vendor {
236
- root = strings .TrimSuffix (root , string (filepath .Separator ))
237
- prefix := strings .TrimSuffix (strings .TrimSuffix (rootPath , root ), string (filepath .Separator ))
238
-
239
- // look for the closest vendor in one of our direct ancestors, as it takes priority.
240
- var vendored string
241
- for {
242
- fi , err := fs .Stat (filesystem , filepath .Join (parent , vendor ))
243
- if err == nil && fi .IsDir () {
244
- vendored = strings .TrimPrefix (strings .TrimPrefix (parent , prefix ), string (filepath .Separator ))
245
- break
246
- }
247
- if ! os .IsNotExist (err ) {
248
- return "" , err
249
- }
250
-
251
- // stop when we reach GOPATH/src/blah
252
- parent = filepath .Dir (parent )
253
- if parent == prefix {
254
- break
255
- }
221
+ // searchDirs searches within a directory (and its subdirectories) in an attempt to find a filepath.
222
+ func searchDirs (initial string , target string ) (string , error ) {
223
+ absfilepath , err := filepath .Abs (initial )
224
+ if err != nil {
225
+ return "" , err
226
+ }
256
227
257
- // just an additional failsafe, stop if we reach the filesystem root, or dot (if
258
- // we are dealing with relative paths).
259
- // TODO(mpl): It should probably be a critical error actually,
260
- // as we shouldn't have gone that high up in the tree.
261
- if parent == string ( filepath . Separator ) || parent == "." {
262
- break
228
+ // find the go directory.
229
+ var foundpath string
230
+ filter := func ( path string , d fs. DirEntry , err error ) error {
231
+ if d . IsDir () {
232
+ if d . Name () == target {
233
+ foundpath = path
263
234
}
264
235
}
265
-
266
- if vendored != "" {
267
- return vendored , nil
268
- }
236
+ return nil
269
237
}
270
-
271
- // TODO(mpl): the algorithm below might be redundant with the one above,
272
- // but keeping it for now. Investigate/simplify/remove later.
273
- splitRoot := strings .Split (root , string (filepath .Separator ))
274
- var index int
275
- for i := len (splitRoot ) - 1 ; i >= 0 ; i -- {
276
- if splitRoot [i ] == "vendor" {
277
- index = i
278
- break
279
- }
238
+ if err = filepath .WalkDir (absfilepath , filter ); err != nil {
239
+ return "" , fmt .Errorf ("An error occurred searching for a directory.\n %v" , err )
280
240
}
281
241
282
- if index == 0 {
283
- return "" , nil
242
+ if foundpath != "" {
243
+ return foundpath , nil
284
244
}
285
-
286
- return filepath .Join (splitRoot [:index ]... ), nil
245
+ return "" , fmt .Errorf ("The target filepath %q is not within the path %q" , target , initial )
287
246
}
288
247
289
248
func effectivePkg (root , path string ) string {
0 commit comments