github.com/bir3/gocompiler@v0.3.205/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 "bytes" 11 "fmt" 12 "github.com/bir3/gocompiler/src/go/build" 13 "github.com/bir3/gocompiler/src/go/token" 14 "github.com/bir3/gocompiler/src/go/types" 15 "github.com/bir3/gocompiler/src/internal/pkgbits" 16 "io" 17 "os" 18 "os/exec" 19 "path/filepath" 20 "strings" 21 "sync" 22 ) 23 24 // debugging/development support 25 const debug = false 26 27 var exportMap sync.Map // package dir → func() (string, bool) 28 29 // lookupGorootExport returns the location of the export data 30 // (normally found in the build cache, but located in GOROOT/pkg 31 // in prior Go releases) for the package located in pkgDir. 32 // 33 // (We use the package's directory instead of its import path 34 // mainly to simplify handling of the packages in src/vendor 35 // and cmd/vendor.) 36 func lookupGorootExport(pkgDir string) (string, bool) { 37 f, ok := exportMap.Load(pkgDir) 38 if !ok { 39 var ( 40 listOnce sync.Once 41 exportPath string 42 ) 43 f, _ = exportMap.LoadOrStore(pkgDir, func() (string, bool) { 44 listOnce.Do(func() { 45 cmd := exec.Command(filepath.Join(build.Default.GOROOT, "bin", "go"), "list", "-export", "-f", "{{.Export}}", pkgDir) 46 cmd.Dir = build.Default.GOROOT 47 cmd.Env = append(cmd.Environ(), "GOROOT="+build.Default.GOROOT) 48 var output []byte 49 output, err := cmd.Output() 50 if err != nil { 51 return 52 } 53 54 exports := strings.Split(string(bytes.TrimSpace(output)), "\n") 55 if len(exports) != 1 { 56 return 57 } 58 59 exportPath = exports[0] 60 }) 61 62 return exportPath, exportPath != "" 63 }) 64 } 65 66 return f.(func() (string, bool))() 67 } 68 69 var pkgExts = [...]string{".a", ".o"} // a file from the build cache will have no extension 70 71 // FindPkg returns the filename and unique package id for an import 72 // path based on package information provided by build.Import (using 73 // the build.Default build.Context). A relative srcDir is interpreted 74 // relative to the current working directory. 75 // If no file was found, an empty filename is returned. 76 func FindPkg(path, srcDir string) (filename, id string) { 77 if path == "" { 78 return 79 } 80 81 var noext string 82 switch { 83 default: 84 // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x" 85 // Don't require the source files to be present. 86 if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282 87 srcDir = abs 88 } 89 bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary) 90 if bp.PkgObj == "" { 91 var ok bool 92 if bp.Goroot && bp.Dir != "" { 93 filename, ok = lookupGorootExport(bp.Dir) 94 } 95 if !ok { 96 id = path // make sure we have an id to print in error message 97 return 98 } 99 } else { 100 noext = strings.TrimSuffix(bp.PkgObj, ".a") 101 } 102 id = bp.ImportPath 103 104 case build.IsLocalImport(path): 105 // "./x" -> "/this/directory/x.ext", "/this/directory/x" 106 noext = filepath.Join(srcDir, path) 107 id = noext 108 109 case filepath.IsAbs(path): 110 // for completeness only - go/build.Import 111 // does not support absolute imports 112 // "/x" -> "/x.ext", "/x" 113 noext = path 114 id = path 115 } 116 117 if false { // for debugging 118 if path != id { 119 fmt.Printf("%s -> %s\n", path, id) 120 } 121 } 122 123 if filename != "" { 124 if f, err := os.Stat(filename); err == nil && !f.IsDir() { 125 return 126 } 127 } 128 // try extensions 129 for _, ext := range pkgExts { 130 filename = noext + ext 131 if f, err := os.Stat(filename); err == nil && !f.IsDir() { 132 return 133 } 134 } 135 136 filename = "" // not found 137 return 138 } 139 140 // Import imports a gc-generated package given its import path and srcDir, adds 141 // the corresponding package object to the packages map, and returns the object. 142 // The packages map must contain all packages already imported. 143 func Import(fset *token.FileSet, packages map[string]*types.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types.Package, err error) { 144 var rc io.ReadCloser 145 var id string 146 if lookup != nil { 147 // With custom lookup specified, assume that caller has 148 // converted path to a canonical import path for use in the map. 149 if path == "unsafe" { 150 return types.Unsafe, nil 151 } 152 id = path 153 154 // No need to re-import if the package was imported completely before. 155 if pkg = packages[id]; pkg != nil && pkg.Complete() { 156 return 157 } 158 f, err := lookup(path) 159 if err != nil { 160 return nil, err 161 } 162 rc = f 163 } else { 164 var filename string 165 filename, id = FindPkg(path, srcDir) 166 if filename == "" { 167 if path == "unsafe" { 168 return types.Unsafe, nil 169 } 170 return nil, fmt.Errorf("can't find import: %q", id) 171 } 172 173 // no need to re-import if the package was imported completely before 174 if pkg = packages[id]; pkg != nil && pkg.Complete() { 175 return 176 } 177 178 // open file 179 f, err := os.Open(filename) 180 if err != nil { 181 return nil, err 182 } 183 defer func() { 184 if err != nil { 185 // add file name to error 186 err = fmt.Errorf("%s: %v", filename, err) 187 } 188 }() 189 rc = f 190 } 191 defer rc.Close() 192 193 buf := bufio.NewReader(rc) 194 hdr, size, err := FindExportData(buf) 195 if err != nil { 196 return 197 } 198 199 switch hdr { 200 case "$$\n": 201 err = fmt.Errorf("import %q: old textual export format no longer supported (recompile library)", path) 202 203 case "$$B\n": 204 var exportFormat byte 205 if exportFormat, err = buf.ReadByte(); err != nil { 206 return 207 } 208 209 // The unified export format starts with a 'u'; the indexed export 210 // format starts with an 'i'; and the older binary export format 211 // starts with a 'c', 'd', or 'v' (from "version"). Select 212 // appropriate importer. 213 switch exportFormat { 214 case 'u': 215 var data []byte 216 var r io.Reader = buf 217 if size >= 0 { 218 r = io.LimitReader(r, int64(size)) 219 } 220 if data, err = io.ReadAll(r); err != nil { 221 return 222 } 223 s := string(data) 224 s = s[:strings.LastIndex(s, "\n$$\n")] 225 226 input := pkgbits.NewPkgDecoder(id, s) 227 pkg = readUnifiedPackage(fset, nil, packages, input) 228 case 'i': 229 pkg, err = iImportData(fset, packages, buf, id) 230 default: 231 err = fmt.Errorf("import %q: old binary export format no longer supported (recompile library)", path) 232 } 233 234 default: 235 err = fmt.Errorf("import %q: unknown export data header: %q", path, hdr) 236 } 237 238 return 239 } 240 241 type byPath []*types.Package 242 243 func (a byPath) Len() int { return len(a) } 244 func (a byPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 245 func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() }