...

Source file src/pkg/go/internal/gcimporter/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 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"
    15		"io/ioutil"
    16		"os"
    17		"path/filepath"
    18		"strings"
    19	)
    20	
    21	// debugging/development support
    22	const debug = false
    23	
    24	var pkgExts = [...]string{".a", ".o"}
    25	
    26	// FindPkg returns the filename and unique package id for an import
    27	// path based on package information provided by build.Import (using
    28	// the build.Default build.Context). A relative srcDir is interpreted
    29	// relative to the current working directory.
    30	// If no file was found, an empty filename is returned.
    31	//
    32	func FindPkg(path, srcDir string) (filename, id string) {
    33		if path == "" {
    34			return
    35		}
    36	
    37		var noext string
    38		switch {
    39		default:
    40			// "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
    41			// Don't require the source files to be present.
    42			if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282
    43				srcDir = abs
    44			}
    45			bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
    46			if bp.PkgObj == "" {
    47				id = path // make sure we have an id to print in error message
    48				return
    49			}
    50			noext = strings.TrimSuffix(bp.PkgObj, ".a")
    51			id = bp.ImportPath
    52	
    53		case build.IsLocalImport(path):
    54			// "./x" -> "/this/directory/x.ext", "/this/directory/x"
    55			noext = filepath.Join(srcDir, path)
    56			id = noext
    57	
    58		case filepath.IsAbs(path):
    59			// for completeness only - go/build.Import
    60			// does not support absolute imports
    61			// "/x" -> "/x.ext", "/x"
    62			noext = path
    63			id = path
    64		}
    65	
    66		if false { // for debugging
    67			if path != id {
    68				fmt.Printf("%s -> %s\n", path, id)
    69			}
    70		}
    71	
    72		// try extensions
    73		for _, ext := range pkgExts {
    74			filename = noext + ext
    75			if f, err := os.Stat(filename); err == nil && !f.IsDir() {
    76				return
    77			}
    78		}
    79	
    80		filename = "" // not found
    81		return
    82	}
    83	
    84	// Import imports a gc-generated package given its import path and srcDir, adds
    85	// the corresponding package object to the packages map, and returns the object.
    86	// The packages map must contain all packages already imported.
    87	//
    88	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) {
    89		var rc io.ReadCloser
    90		var id string
    91		if lookup != nil {
    92			// With custom lookup specified, assume that caller has
    93			// converted path to a canonical import path for use in the map.
    94			if path == "unsafe" {
    95				return types.Unsafe, nil
    96			}
    97			id = path
    98	
    99			// No need to re-import if the package was imported completely before.
   100			if pkg = packages[id]; pkg != nil && pkg.Complete() {
   101				return
   102			}
   103			f, err := lookup(path)
   104			if err != nil {
   105				return nil, err
   106			}
   107			rc = f
   108		} else {
   109			var filename string
   110			filename, id = FindPkg(path, srcDir)
   111			if filename == "" {
   112				if path == "unsafe" {
   113					return types.Unsafe, nil
   114				}
   115				return nil, fmt.Errorf("can't find import: %q", id)
   116			}
   117	
   118			// no need to re-import if the package was imported completely before
   119			if pkg = packages[id]; pkg != nil && pkg.Complete() {
   120				return
   121			}
   122	
   123			// open file
   124			f, err := os.Open(filename)
   125			if err != nil {
   126				return nil, err
   127			}
   128			defer func() {
   129				if err != nil {
   130					// add file name to error
   131					err = fmt.Errorf("%s: %v", filename, err)
   132				}
   133			}()
   134			rc = f
   135		}
   136		defer rc.Close()
   137	
   138		var hdr string
   139		buf := bufio.NewReader(rc)
   140		if hdr, err = FindExportData(buf); err != nil {
   141			return
   142		}
   143	
   144		switch hdr {
   145		case "$$\n":
   146			err = fmt.Errorf("import %q: old export format no longer supported (recompile library)", path)
   147	
   148		case "$$B\n":
   149			var data []byte
   150			data, err = ioutil.ReadAll(buf)
   151			if err != nil {
   152				break
   153			}
   154	
   155			// The indexed export format starts with an 'i'; the older
   156			// binary export format starts with a 'c', 'd', or 'v'
   157			// (from "version"). Select appropriate importer.
   158			if len(data) > 0 && data[0] == 'i' {
   159				_, pkg, err = iImportData(fset, packages, data[1:], id)
   160			} else {
   161				_, pkg, err = BImportData(fset, packages, data, id)
   162			}
   163	
   164		default:
   165			err = fmt.Errorf("unknown export data header: %q", hdr)
   166		}
   167	
   168		return
   169	}
   170	
   171	func deref(typ types.Type) types.Type {
   172		if p, _ := typ.(*types.Pointer); p != nil {
   173			return p.Elem()
   174		}
   175		return typ
   176	}
   177	
   178	type byPath []*types.Package
   179	
   180	func (a byPath) Len() int           { return len(a) }
   181	func (a byPath) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   182	func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() }
   183	

View as plain text