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