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