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() }