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