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