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.
5 // This file is a modified copy of $GOROOT/src/go/internal/gcimporter/gcimporter.go,
6 // but it also contains the original source-based importer code for Go1.6.
7 // Once we stop supporting 1.6, we can remove that code.
9 // Package gcimporter provides various functions for reading
10 // gc-generated object files that can be used to implement the
11 // Importer interface defined by the Go 1.5 standard library package.
27 var pkgExts = [...]string{".a", ".o"}
29 // FindPkg returns the filename and unique package id for an import
30 // path based on package information provided by build.Import (using
31 // the build.Default build.Context). A relative srcDir is interpreted
32 // relative to the current working directory.
33 // If no file was found, an empty filename is returned.
35 func FindPkg(path, srcDir string) (filename, id string) {
43 // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
44 // Don't require the source files to be present.
45 if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282
48 bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
50 id = path // make sure we have an id to print in error message
53 noext = strings.TrimSuffix(bp.PkgObj, ".a")
56 case build.IsLocalImport(path):
57 // "./x" -> "/this/directory/x.ext", "/this/directory/x"
58 noext = filepath.Join(srcDir, path)
61 case filepath.IsAbs(path):
62 // for completeness only - go/build.Import
63 // does not support absolute imports
64 // "/x" -> "/x.ext", "/x"
69 if false { // for debugging
71 fmt.Printf("%s -> %s\n", path, id)
76 for _, ext := range pkgExts {
77 filename = noext + ext
78 if f, err := os.Stat(filename); err == nil && !f.IsDir() {
83 filename = "" // not found
87 // Import imports a gc-generated package given its import path and srcDir, adds
88 // the corresponding package object to the packages map, and returns the object.
89 // The packages map must contain all packages already imported.
91 func Import(packages map[string]*types.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types.Package, err error) {
93 var filename, id string
95 // With custom lookup specified, assume that caller has
96 // converted path to a canonical import path for use in the map.
98 return types.Unsafe, nil
102 // No need to re-import if the package was imported completely before.
103 if pkg = packages[id]; pkg != nil && pkg.Complete() {
106 f, err := lookup(path)
112 filename, id = FindPkg(path, srcDir)
114 if path == "unsafe" {
115 return types.Unsafe, nil
117 return nil, fmt.Errorf("can't find import: %q", id)
120 // no need to re-import if the package was imported completely before
121 if pkg = packages[id]; pkg != nil && pkg.Complete() {
126 f, err := os.Open(filename)
132 // add file name to error
133 err = fmt.Errorf("%s: %v", filename, err)
141 buf := bufio.NewReader(rc)
142 if hdr, _, err = findExportData(buf); err != nil {
149 data, err = ioutil.ReadAll(buf)
154 // TODO(gri): allow clients of go/importer to provide a FileSet.
155 // Or, define a new standard go/types/gcexportdata package.
156 fset := token.NewFileSet()
158 // The indexed export format starts with an 'i'.
159 if len(data) == 0 || data[0] != 'i' {
160 return nil, fmt.Errorf("unknown export data format %c", data[0])
162 _, pkg, err = IImportData(fset, packages, data[1:], id)
165 err = fmt.Errorf("unknown export data header: %q", hdr)
171 type byPath []*types.Package
173 func (a byPath) Len() int { return len(a) }
174 func (a byPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
175 func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() }