github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/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  	"fmt"
    11  	"go/build"
    12  	"go/token"
    13  	"go/types"
    14  	"io/ioutil"
    15  	"os"
    16  	"path/filepath"
    17  	"strings"
    18  )
    19  
    20  // debugging/development support
    21  const debug = false
    22  
    23  var pkgExts = [...]string{".a", ".o"}
    24  
    25  // FindPkg returns the filename and unique package id for an import
    26  // path based on package information provided by build.Import (using
    27  // the build.Default build.Context). A relative srcDir is interpreted
    28  // relative to the current working directory.
    29  // If no file was found, an empty filename is returned.
    30  //
    31  func FindPkg(path, srcDir string) (filename, id string) {
    32  	if path == "" {
    33  		return
    34  	}
    35  
    36  	var noext string
    37  	switch {
    38  	default:
    39  		// "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
    40  		// Don't require the source files to be present.
    41  		if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282
    42  			srcDir = abs
    43  		}
    44  		bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
    45  		if bp.PkgObj == "" {
    46  			return
    47  		}
    48  		noext = strings.TrimSuffix(bp.PkgObj, ".a")
    49  		id = bp.ImportPath
    50  
    51  	case build.IsLocalImport(path):
    52  		// "./x" -> "/this/directory/x.ext", "/this/directory/x"
    53  		noext = filepath.Join(srcDir, path)
    54  		id = noext
    55  
    56  	case filepath.IsAbs(path):
    57  		// for completeness only - go/build.Import
    58  		// does not support absolute imports
    59  		// "/x" -> "/x.ext", "/x"
    60  		noext = path
    61  		id = path
    62  	}
    63  
    64  	if false { // for debugging
    65  		if path != id {
    66  			fmt.Printf("%s -> %s\n", path, id)
    67  		}
    68  	}
    69  
    70  	// try extensions
    71  	for _, ext := range pkgExts {
    72  		filename = noext + ext
    73  		if f, err := os.Stat(filename); err == nil && !f.IsDir() {
    74  			return
    75  		}
    76  	}
    77  
    78  	filename = "" // not found
    79  	return
    80  }
    81  
    82  // Import imports a gc-generated package given its import path and srcDir, adds
    83  // the corresponding package object to the packages map, and returns the object.
    84  // The packages map must contain all packages already imported.
    85  //
    86  func Import(packages map[string]*types.Package, path, srcDir string) (pkg *types.Package, err error) {
    87  	filename, id := FindPkg(path, srcDir)
    88  	if filename == "" {
    89  		if path == "unsafe" {
    90  			return types.Unsafe, nil
    91  		}
    92  		err = fmt.Errorf("can't find import: %s", id)
    93  		return
    94  	}
    95  
    96  	// no need to re-import if the package was imported completely before
    97  	if pkg = packages[id]; pkg != nil && pkg.Complete() {
    98  		return
    99  	}
   100  
   101  	// open file
   102  	f, err := os.Open(filename)
   103  	if err != nil {
   104  		return
   105  	}
   106  	defer func() {
   107  		f.Close()
   108  		if err != nil {
   109  			// add file name to error
   110  			err = fmt.Errorf("%s: %v", filename, err)
   111  		}
   112  	}()
   113  
   114  	var hdr string
   115  	buf := bufio.NewReader(f)
   116  	if hdr, err = FindExportData(buf); err != nil {
   117  		return
   118  	}
   119  
   120  	switch hdr {
   121  	case "$$\n":
   122  		err = fmt.Errorf("import %q: old export format no longer supported (recompile library)", path)
   123  	case "$$B\n":
   124  		var data []byte
   125  		data, err = ioutil.ReadAll(buf)
   126  		if err == nil {
   127  			// TODO(gri): allow clients of go/importer to provide a FileSet.
   128  			// Or, define a new standard go/types/gcexportdata package.
   129  			fset := token.NewFileSet()
   130  			_, pkg, err = BImportData(fset, packages, data, id)
   131  			return
   132  		}
   133  	default:
   134  		err = fmt.Errorf("unknown export data header: %q", hdr)
   135  	}
   136  
   137  	return
   138  }
   139  
   140  func deref(typ types.Type) types.Type {
   141  	if p, _ := typ.(*types.Pointer); p != nil {
   142  		return p.Elem()
   143  	}
   144  	return typ
   145  }
   146  
   147  type byPath []*types.Package
   148  
   149  func (a byPath) Len() int           { return len(a) }
   150  func (a byPath) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   151  func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() }