github.com/hlts2/go@v0.0.0-20170904000733-812b34efaed8/src/go/internal/gcimporter/gcimporter.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package gcimporter implements Import for gc-generated object files. 6 package gcimporter // import "go/internal/gcimporter" 7 8 import ( 9 "bufio" 10 "fmt" 11 "go/build" 12 "go/token" 13 "go/types" 14 "io/ioutil" 15 "os" 16 "path/filepath" 17 "strings" 18 ) 19 20 // debugging/development support 21 const debug = false 22 23 var pkgExts = [...]string{".a", ".o"} 24 25 // FindPkg returns the filename and unique package id for an import 26 // path based on package information provided by build.Import (using 27 // the build.Default build.Context). A relative srcDir is interpreted 28 // relative to the current working directory. 29 // If no file was found, an empty filename is returned. 30 // 31 func FindPkg(path, srcDir string) (filename, id string) { 32 if path == "" { 33 return 34 } 35 36 var noext string 37 switch { 38 default: 39 // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x" 40 // Don't require the source files to be present. 41 if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282 42 srcDir = abs 43 } 44 bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary) 45 if bp.PkgObj == "" { 46 id = path // make sure we have an id to print in error message 47 return 48 } 49 noext = strings.TrimSuffix(bp.PkgObj, ".a") 50 id = bp.ImportPath 51 52 case build.IsLocalImport(path): 53 // "./x" -> "/this/directory/x.ext", "/this/directory/x" 54 noext = filepath.Join(srcDir, path) 55 id = noext 56 57 case filepath.IsAbs(path): 58 // for completeness only - go/build.Import 59 // does not support absolute imports 60 // "/x" -> "/x.ext", "/x" 61 noext = path 62 id = path 63 } 64 65 if false { // for debugging 66 if path != id { 67 fmt.Printf("%s -> %s\n", path, id) 68 } 69 } 70 71 // try extensions 72 for _, ext := range pkgExts { 73 filename = noext + ext 74 if f, err := os.Stat(filename); err == nil && !f.IsDir() { 75 return 76 } 77 } 78 79 filename = "" // not found 80 return 81 } 82 83 // Import imports a gc-generated package given its import path and srcDir, adds 84 // the corresponding package object to the packages map, and returns the object. 85 // The packages map must contain all packages already imported. 86 // 87 func Import(packages map[string]*types.Package, path, srcDir string) (pkg *types.Package, err error) { 88 filename, id := FindPkg(path, srcDir) 89 if filename == "" { 90 if path == "unsafe" { 91 return types.Unsafe, nil 92 } 93 err = fmt.Errorf("can't find import: %q", id) 94 return 95 } 96 97 // no need to re-import if the package was imported completely before 98 if pkg = packages[id]; pkg != nil && pkg.Complete() { 99 return 100 } 101 102 // open file 103 f, err := os.Open(filename) 104 if err != nil { 105 return 106 } 107 defer func() { 108 f.Close() 109 if err != nil { 110 // add file name to error 111 err = fmt.Errorf("%s: %v", filename, err) 112 } 113 }() 114 115 var hdr string 116 buf := bufio.NewReader(f) 117 if hdr, err = FindExportData(buf); err != nil { 118 return 119 } 120 121 switch hdr { 122 case "$$\n": 123 err = fmt.Errorf("import %q: old export format no longer supported (recompile library)", path) 124 case "$$B\n": 125 var data []byte 126 data, err = ioutil.ReadAll(buf) 127 if err == nil { 128 // TODO(gri): allow clients of go/importer to provide a FileSet. 129 // Or, define a new standard go/types/gcexportdata package. 130 fset := token.NewFileSet() 131 _, pkg, err = BImportData(fset, packages, data, id) 132 return 133 } 134 default: 135 err = fmt.Errorf("unknown export data header: %q", hdr) 136 } 137 138 return 139 } 140 141 func deref(typ types.Type) types.Type { 142 if p, _ := typ.(*types.Pointer); p != nil { 143 return p.Elem() 144 } 145 return typ 146 } 147 148 type byPath []*types.Package 149 150 func (a byPath) Len() int { return len(a) } 151 func (a byPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 152 func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() }