@@ -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
0 commit comments