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