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