Source file src/cmd/compile/internal/importer/gcimporter.go

     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  	"cmd/compile/internal/types2"
    11  	"fmt"
    12  	"go/build"
    13  	"io"
    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  			id = path // make sure we have an id to print in error message
    47  			return
    48  		}
    49  		noext = strings.TrimSuffix(bp.PkgObj, ".a")
    50  		id = bp.ImportPath
    51  
    52  	case build.IsLocalImport(path):
    53  		// "./x" -> "/this/directory/x.ext", "/this/directory/x"
    54  		noext = filepath.Join(srcDir, path)
    55  		id = noext
    56  
    57  	case filepath.IsAbs(path):
    58  		// for completeness only - go/build.Import
    59  		// does not support absolute imports
    60  		// "/x" -> "/x.ext", "/x"
    61  		noext = path
    62  		id = path
    63  	}
    64  
    65  	if false { // for debugging
    66  		if path != id {
    67  			fmt.Printf("%s -> %s\n", path, id)
    68  		}
    69  	}
    70  
    71  	// try extensions
    72  	for _, ext := range pkgExts {
    73  		filename = noext + ext
    74  		if f, err := os.Stat(filename); err == nil && !f.IsDir() {
    75  			return
    76  		}
    77  	}
    78  
    79  	filename = "" // not found
    80  	return
    81  }
    82  
    83  // Import imports a gc-generated package given its import path and srcDir, adds
    84  // the corresponding package object to the packages map, and returns the object.
    85  // The packages map must contain all packages already imported.
    86  //
    87  func Import(packages map[string]*types2.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types2.Package, err error) {
    88  	var rc io.ReadCloser
    89  	var id string
    90  	if lookup != nil {
    91  		// With custom lookup specified, assume that caller has
    92  		// converted path to a canonical import path for use in the map.
    93  		if path == "unsafe" {
    94  			return types2.Unsafe, nil
    95  		}
    96  		id = path
    97  
    98  		// No need to re-import if the package was imported completely before.
    99  		if pkg = packages[id]; pkg != nil && pkg.Complete() {
   100  			return
   101  		}
   102  		f, err := lookup(path)
   103  		if err != nil {
   104  			return nil, err
   105  		}
   106  		rc = f
   107  	} else {
   108  		var filename string
   109  		filename, id = FindPkg(path, srcDir)
   110  		if filename == "" {
   111  			if path == "unsafe" {
   112  				return types2.Unsafe, nil
   113  			}
   114  			return nil, fmt.Errorf("can't find import: %q", id)
   115  		}
   116  
   117  		// no need to re-import if the package was imported completely before
   118  		if pkg = packages[id]; pkg != nil && pkg.Complete() {
   119  			return
   120  		}
   121  
   122  		// open file
   123  		f, err := os.Open(filename)
   124  		if err != nil {
   125  			return nil, err
   126  		}
   127  		defer func() {
   128  			if err != nil {
   129  				// add file name to error
   130  				err = fmt.Errorf("%s: %v", filename, err)
   131  			}
   132  		}()
   133  		rc = f
   134  	}
   135  	defer rc.Close()
   136  
   137  	var hdr string
   138  	buf := bufio.NewReader(rc)
   139  	if hdr, err = FindExportData(buf); err != nil {
   140  		return
   141  	}
   142  
   143  	switch hdr {
   144  	case "$$\n":
   145  		err = fmt.Errorf("import %q: old textual export format no longer supported (recompile library)", path)
   146  
   147  	case "$$B\n":
   148  		var data []byte
   149  		data, err = ioutil.ReadAll(buf)
   150  		if err != nil {
   151  			break
   152  		}
   153  
   154  		// The indexed export format starts with an 'i'; the older
   155  		// binary export format starts with a 'c', 'd', or 'v'
   156  		// (from "version"). Select appropriate importer.
   157  		if len(data) > 0 && data[0] == 'i' {
   158  			pkg, err = ImportData(packages, string(data[1:]), id)
   159  		} else {
   160  			err = fmt.Errorf("import %q: old binary export format no longer supported (recompile library)", path)
   161  		}
   162  
   163  	default:
   164  		err = fmt.Errorf("import %q: unknown export data header: %q", path, hdr)
   165  	}
   166  
   167  	return
   168  }
   169  
   170  type byPath []*types2.Package
   171  
   172  func (a byPath) Len() int           { return len(a) }
   173  func (a byPath) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   174  func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() }
   175  

View as plain text