@@ -215,6 +215,12 @@ func reloadPackage(arg string, stk *importStack) *Package {
215
215
return loadPackage(arg, stk)
216
216
}
217
217
218
+
// The Go 1.5 vendoring experiment is enabled by setting GO15VENDOREXPERIMENT=1.
219
+
// The variable is obnoxiously long so that years from now when people find it in
220
+
// their profiles and wonder what it does, there is some chance that a web search
221
+
// might answer the question.
222
+
var go15VendorExperiment = os.Getenv("GO15VENDOREXPERIMENT") == "1"
223
+
218
224
// dirToImportPath returns the pseudo-import path we use for a package
219
225
// outside the Go path. It begins with _/ and then contains the full path
220
226
// to the directory. If the package lives in c:\home\gopher\my\pkg then
@@ -239,23 +245,33 @@ func makeImportValid(r rune) rune {
239
245
// but possibly a local import path (an absolute file system path or one beginning
240
246
// with ./ or ../). A local relative path is interpreted relative to srcDir.
241
247
// It returns a *Package describing the package found in that directory.
242
-
func loadImport(path string, srcDir string, stk *importStack, importPos []token.Position) *Package {
248
+
func loadImport(path, srcDir string, parent *Package, stk *importStack, importPos []token.Position) *Package {
243
249
stk.push(path)
244
250
defer stk.pop()
245
251
246
252
// Determine canonical identifier for this package.
247
253
// For a local import the identifier is the pseudo-import path
248
254
// we create from the full directory to the package.
249
255
// Otherwise it is the usual import path.
256
+
// For vendored imports, it is the expanded form.
250
257
importPath := path
258
+
origPath := path
251
259
isLocal := build.IsLocalImport(path)
260
+
var vendorSearch []string
252
261
if isLocal {
253
262
importPath = dirToImportPath(filepath.Join(srcDir, path))
263
+
} else {
264
+
path, vendorSearch = vendoredImportPath(parent, path)
265
+
importPath = path
254
266
}
267
+
255
268
if p := packageCache[importPath]; p != nil {
256
269
if perr := disallowInternal(srcDir, p, stk); perr != p {
257
270
return perr
258
271
}
272
+
if perr := disallowVendor(srcDir, origPath, p, stk); perr != p {
273
+
return perr
274
+
}
259
275
return reusePackage(p, stk)
260
276
}
261
277
@@ -271,11 +287,33 @@ func loadImport(path string, srcDir string, stk *importStack, importPos []token.
271
287
// TODO: After Go 1, decide when to pass build.AllowBinary here.
272
288
// See issue 3268 for mistakes to avoid.
273
289
bp, err := buildContext.Import(path, srcDir, build.ImportComment)
290
+
291
+
// If we got an error from go/build about package not found,
292
+
// it contains the directories from $GOROOT and $GOPATH that
293
+
// were searched. Add to that message the vendor directories
294
+
// that were searched.
295
+
if err != nil && len(vendorSearch) > 0 {
296
+
// NOTE(rsc): The direct text manipulation here is fairly awful,
297
+
// but it avoids defining new go/build API (an exported error type)
298
+
// late in the Go 1.5 release cycle. If this turns out to be a more general
299
+
// problem we could define a real error type when the decision can be
300
+
// considered more carefully.
301
+
text := err.Error()
302
+
if strings.Contains(text, "cannot find package \"") && strings.Contains(text, "\" in any of:\n\t") {
303
+
old := strings.SplitAfter(text, "\n")
304
+
lines := []string{old[0]}
305
+
for _, dir := range vendorSearch {
306
+
lines = append(lines, "\t"+dir+" (vendor tree)\n")
307
+
}
308
+
lines = append(lines, old[1:]...)
309
+
err = errors.New(strings.Join(lines, ""))
310
+
}
311
+
}
274
312
bp.ImportPath = importPath
275
313
if gobin != "" {
276
314
bp.BinDir = gobin
277
315
}
278
-
if err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path {
316
+
if err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path && (!go15VendorExperiment || !strings.Contains(path, "/vendor/")) {
279
317
err = fmt.Errorf("code in directory %s expects import %q", bp.Dir, bp.ImportComment)
280
318
}
281
319
p.load(stk, bp, err)
@@ -288,10 +326,81 @@ func loadImport(path string, srcDir string, stk *importStack, importPos []token.
288
326
if perr := disallowInternal(srcDir, p, stk); perr != p {
289
327
return perr
290
328
}
329
+
if perr := disallowVendor(srcDir, origPath, p, stk); perr != p {
330
+
return perr
331
+
}
291
332
292
333
return p
293
334
}
294
335
336
+
var isDirCache = map[string]bool{}
337
+
338
+
func isDir(path string) bool {
339
+
result, ok := isDirCache[path]
340
+
if ok {
341
+
return result
342
+
}
343
+
344
+
fi, err := os.Stat(path)
345
+
result = err == nil && fi.IsDir()
346
+
isDirCache[path] = result
347
+
return result
348
+
}
349
+
350
+
// vendoredImportPath returns the expansion of path when it appears in parent.
351
+
// If parent is x/y/z, then path might expand to x/y/z/vendor/path, x/y/vendor/path,
352
+
// x/vendor/path, vendor/path, or else stay x/y/z if none of those exist.
353
+
// vendoredImportPath returns the expanded path or, if no expansion is found, the original.
354
+
// If no epxansion is found, vendoredImportPath also returns a list of vendor directories
355
+
// it searched along the way, to help prepare a useful error message should path turn
356
+
// out not to exist.
357
+
func vendoredImportPath(parent *Package, path string) (found string, searched []string) {
358
+
if parent == nil || !go15VendorExperiment {
359
+
return path, nil
360
+
}
361
+
dir := filepath.Clean(parent.Dir)
362
+
root := filepath.Clean(parent.Root)
363
+
if !strings.HasPrefix(dir, root) || len(dir) <= len(root) || dir[len(root)] != filepath.Separator {
364
+
fatalf("invalid vendoredImportPath: dir=%q root=%q separator=%q", dir, root, string(filepath.Separator))
365
+
}
366
+
vpath := "vendor/" + path
367
+
for i := len(dir); i >= len(root); i-- {
368
+
if i < len(dir) && dir[i] != filepath.Separator {
369
+
continue
370
+
}
371
+
// Note: checking for the vendor directory before checking
372
+
// for the vendor/path directory helps us hit the
373
+
// isDir cache more often. It also helps us prepare a more useful
374
+
// list of places we looked, to report when an import is not found.
375
+
if !isDir(filepath.Join(dir[:i], "vendor")) {
376
+
continue
377
+
}
378
+
targ := filepath.Join(dir[:i], vpath)
379
+
if isDir(targ) {
380
+
// We started with parent's dir c:\gopath\src\foo\bar\baz\quux\xyzzy.
381
+
// We know the import path for parent's dir.
382
+
// We chopped off some number of path elements and
383
+
// added vendor\path to produce c:\gopath\src\foo\bar\baz\vendor\path.
384
+
// Now we want to know the import path for that directory.
385
+
// Construct it by chopping the same number of path elements
386
+
// (actually the same number of bytes) from parent's import path
387
+
// and then append /vendor/path.
388
+
chopped := len(dir) - i
389
+
if chopped == len(parent.ImportPath)+1 {
390
+
// We walked up from c:\gopath\src\foo\bar
391
+
// and found c:\gopath\src\vendor\path.
392
+
// We chopped \foo\bar (length 8) but the import path is "foo/bar" (length 7).
393
+
// Use "vendor/path" without any prefix.
394
+
return vpath, nil
395
+
}
396
+
return parent.ImportPath[:len(parent.ImportPath)-chopped] + "/" + vpath, nil
397
+
}
398
+
// Note the existence of a vendor directory in case path is not found anywhere.
399
+
searched = append(searched, targ)
400
+
}
401
+
return path, searched
402
+
}
403
+
295
404
// reusePackage reuses package p to satisfy the import at the top
296
405
// of the import stack stk. If this use causes an import loop,
297
406
// reusePackage updates p's error information to record the loop.
@@ -384,6 +493,101 @@ func findInternal(path string) (index int, ok bool) {
384
493
return 0, false
385
494
}
386
495
496
+
// disallowVendor checks that srcDir is allowed to import p as path.
497
+
// If the import is allowed, disallowVendor returns the original package p.
498
+
// If not, it returns a new package containing just an appropriate error.
499
+
func disallowVendor(srcDir, path string, p *Package, stk *importStack) *Package {
500
+
if !go15VendorExperiment {
501
+
return p
502
+
}
503
+
504
+
// The stack includes p.ImportPath.
505
+
// If that's the only thing on the stack, we started
506
+
// with a name given on the command line, not an
507
+
// import. Anything listed on the command line is fine.
508
+
if len(*stk) == 1 {
509
+
return p
510
+
}
511
+
512
+
if perr := disallowVendorVisibility(srcDir, p, stk); perr != p {
513
+
return perr
514
+
}
515
+
516
+
// Paths like x/vendor/y must be imported as y, never as x/vendor/y.
517
+
if i, ok := findVendor(path); ok {
518
+
perr := *p
519
+
perr.Error = &PackageError{
520
+
ImportStack: stk.copy(),
521
+
Err: "must be imported as " + path[i+len("vendor/"):],
522
+
}
523
+
perr.Incomplete = true
524
+
return &perr
525
+
}
526
+
527
+
return p
528
+
}
529
+
530
+
// disallowVendorVisibility checks that srcDir is allowed to import p.
531
+
// The rules are the same as for /internal/ except that a path ending in /vendor
532
+
// is not subject to the rules, only subdirectories of vendor.
533
+
// This allows people to have packages and commands named vendor,
534
+
// for maximal compatibility with existing source trees.
535
+
func disallowVendorVisibility(srcDir string, p *Package, stk *importStack) *Package {
536
+
// The stack includes p.ImportPath.
537
+
// If that's the only thing on the stack, we started
538
+
// with a name given on the command line, not an
539
+
// import. Anything listed on the command line is fine.
540
+
if len(*stk) == 1 {
541
+
return p
542
+
}
543
+
544
+
// Check for "vendor" element.
545
+
i, ok := findVendor(p.ImportPath)
546
+
if !ok {
547
+
return p
548
+
}
549
+
550
+
// Vendor is present.
551
+
// Map import path back to directory corresponding to parent of vendor.
552
+
if i > 0 {
553
+
i-- // rewind over slash in ".../vendor"
554
+
}
555
+
parent := p.Dir[:i+len(p.Dir)-len(p.ImportPath)]
556
+
if hasPathPrefix(filepath.ToSlash(srcDir), filepath.ToSlash(parent)) {
557
+
return p
558
+
}
559
+
560
+
// Vendor is present, and srcDir is outside parent's tree. Not allowed.
561
+
perr := *p
562
+
perr.Error = &PackageError{
563
+
ImportStack: stk.copy(),
564
+
Err: "use of vendored package not allowed",
565
+
}
566
+
perr.Incomplete = true
567
+
return &perr
568
+
}
569
+
570
+
// findVendor looks for the last non-terminating "vendor" path element in the given import path.
571
+
// If there isn't one, findVendor returns ok=false.
572
+
// Otherwise, findInternal returns ok=true and the index of the "vendor".
573
+
//
574
+
// Note that terminating "vendor" elements don't count: "x/vendor" is its own package,
575
+
// not the vendored copy of an import "" (the empty import path).
576
+
// This will allow people to have packages or commands named vendor.
577
+
// This may help reduce breakage, or it may just be confusing. We'll see.
578
+
func findVendor(path string) (index int, ok bool) {
579
+
// Two cases, depending on internal at start of string or not.
580
+
// The order matters: we must return the index of the final element,
581
+
// because the final one is where the effective import path starts.
582
+
switch {
583
+
case strings.Contains(path, "/vendor/"):
584
+
return strings.LastIndex(path, "/vendor/") + 1, true
585
+
case strings.HasPrefix(path, "vendor/"):
586
+
return 0, true
587
+
}
588
+
return 0, false
589
+
}
590
+
387
591
type targetDir int
388
592
389
593
const (
@@ -630,7 +834,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
630
834
if path == "C" {
631
835
continue
632
836
}
633
-
p1 := loadImport(path, p.Dir, stk, p.build.ImportPos[path])
837
+
p1 := loadImport(path, p.Dir, p, stk, p.build.ImportPos[path])
634
838
if p1.Name == "main" {
635
839
p.Error = &PackageError{
636
840
ImportStack: stk.copy(),
@@ -652,8 +856,11 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
652
856
p.Error.Pos = pos[0].String()
653
857
}
654
858
}
655
-
path = p1.ImportPath
656
-
importPaths[i] = path
859
+
}
860
+
path = p1.ImportPath
861
+
importPaths[i] = path
862
+
if i < len(p.Imports) {
863
+
p.Imports[i] = path
657
864
}
658
865
deps[path] = p1
659
866
imports = append(imports, p1)
@@ -1294,7 +1501,7 @@ func loadPackage(arg string, stk *importStack) *Package {
1294
1501
}
1295
1502
}
1296
1503
1297
-
return loadImport(arg, cwd, stk, nil)
1504
+
return loadImport(arg, cwd, nil, stk, nil)
1298
1505
}
1299
1506
1300
1507
// packages returns the packages named by the
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4