@@ -215,6 +215,12 @@ func reloadPackage(arg string, stk *importStack) *Package {
215215 return loadPackage (arg , stk )
216216}
217217
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+
218224// dirToImportPath returns the pseudo-import path we use for a package
219225// outside the Go path. It begins with _/ and then contains the full path
220226// to the directory. If the package lives in c:\home\gopher\my\pkg then
@@ -239,23 +245,33 @@ func makeImportValid(r rune) rune {
239245// but possibly a local import path (an absolute file system path or one beginning
240246// with ./ or ../). A local relative path is interpreted relative to srcDir.
241247// 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 {
243249 stk .push (path )
244250 defer stk .pop ()
245251
246252 // Determine canonical identifier for this package.
247253 // For a local import the identifier is the pseudo-import path
248254 // we create from the full directory to the package.
249255 // Otherwise it is the usual import path.
256+ // For vendored imports, it is the expanded form.
250257 importPath := path
258+ origPath := path
251259 isLocal := build .IsLocalImport (path )
260+ var vendorSearch []string
252261 if isLocal {
253262 importPath = dirToImportPath (filepath .Join (srcDir , path ))
263+ } else {
264+ path , vendorSearch = vendoredImportPath (parent , path )
265+ importPath = path
254266 }
267+
255268 if p := packageCache [importPath ]; p != nil {
256269 if perr := disallowInternal (srcDir , p , stk ); perr != p {
257270 return perr
258271 }
272+ if perr := disallowVendor (srcDir , origPath , p , stk ); perr != p {
273+ return perr
274+ }
259275 return reusePackage (p , stk )
260276 }
261277
@@ -271,11 +287,33 @@ func loadImport(path string, srcDir string, stk *importStack, importPos []token.
271287 // TODO: After Go 1, decide when to pass build.AllowBinary here.
272288 // See issue 3268 for mistakes to avoid.
273289 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+ }
274312 bp .ImportPath = importPath
275313 if gobin != "" {
276314 bp .BinDir = gobin
277315 }
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/" )) {
279317 err = fmt .Errorf ("code in directory %s expects import %q" , bp .Dir , bp .ImportComment )
280318 }
281319 p .load (stk , bp , err )
@@ -288,10 +326,81 @@ func loadImport(path string, srcDir string, stk *importStack, importPos []token.
288326 if perr := disallowInternal (srcDir , p , stk ); perr != p {
289327 return perr
290328 }
329+ if perr := disallowVendor (srcDir , origPath , p , stk ); perr != p {
330+ return perr
331+ }
291332
292333 return p
293334}
294335
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+
295404// reusePackage reuses package p to satisfy the import at the top
296405// of the import stack stk. If this use causes an import loop,
297406// reusePackage updates p's error information to record the loop.
@@ -384,6 +493,101 @@ func findInternal(path string) (index int, ok bool) {
384493 return 0 , false
385494}
386495
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+
387591type targetDir int
388592
389593const (
@@ -630,7 +834,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
630834 if path == "C" {
631835 continue
632836 }
633- p1 := loadImport (path , p .Dir , stk , p .build .ImportPos [path ])
837+ p1 := loadImport (path , p .Dir , p , stk , p .build .ImportPos [path ])
634838 if p1 .Name == "main" {
635839 p .Error = & PackageError {
636840 ImportStack : stk .copy (),
@@ -652,8 +856,11 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
652856 p .Error .Pos = pos [0 ].String ()
653857 }
654858 }
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
657864 }
658865 deps [path ] = p1
659866 imports = append (imports , p1 )
@@ -1294,7 +1501,7 @@ func loadPackage(arg string, stk *importStack) *Package {
12941501 }
12951502 }
12961503
1297- return loadImport (arg , cwd , stk , nil )
1504+ return loadImport (arg , cwd , nil , stk , nil )
12981505}
12991506
13001507// packages returns the packages named by the
0 commit comments