Skip to content

Commit 1a954d5

Browse files
committed
go/gcexportdata: a new simpler API for the deprecated go/gcimporter15
Fixes golang/go#15651 Change-Id: I7dc7ba731a22c677e3c01bf13e91ecfff9e46765 Reviewed-on: https://go-review.googlesource.com/30612 Reviewed-by: Robert Griesemer <[email protected]>
1 parent 8b5a8a4 commit 1a954d5

File tree

4 files changed

+276
-0
lines changed

4 files changed

+276
-0
lines changed

go/gcexportdata/example_test.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// Copyright 2016 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 gcexportdata_test
6+
7+
import (
8+
"fmt"
9+
"go/ast"
10+
"go/parser"
11+
"go/token"
12+
"go/types"
13+
"log"
14+
"os"
15+
"path/filepath"
16+
17+
"golang.org/x/tools/go/gcexportdata"
18+
)
19+
20+
// ExampleRead uses gcexportdata.Read to load type information for the
21+
// "fmt" package from the fmt.a file produced by the gc compiler.
22+
func ExampleRead() {
23+
// Find the export data file.
24+
filename, path := gcexportdata.Find("fmt", "")
25+
if filename == "" {
26+
log.Fatalf("can't find export data for fmt")
27+
}
28+
fmt.Printf("Package path: %s\n", path)
29+
fmt.Printf("Export data: %s\n", filepath.Base(filename))
30+
31+
// Open and read the file.
32+
f, err := os.Open(filename)
33+
if err != nil {
34+
log.Fatal(err)
35+
}
36+
defer f.Close()
37+
r, err := gcexportdata.NewReader(f)
38+
if err != nil {
39+
log.Fatalf("reading export data %s: %v", filename, err)
40+
}
41+
42+
// Decode the export data.
43+
fset := token.NewFileSet()
44+
imports := make(map[string]*types.Package)
45+
pkg, err := gcexportdata.Read(r, fset, imports, path)
46+
if err != nil {
47+
log.Fatal(err)
48+
}
49+
50+
// Print package information.
51+
fmt.Printf("Package members: %s...\n", pkg.Scope().Names()[:5])
52+
println := pkg.Scope().Lookup("Println")
53+
posn := fset.Position(println.Pos())
54+
posn.Line = 123 // make example deterministic
55+
fmt.Printf("Println type: %s\n", println.Type())
56+
fmt.Printf("Println location: %s\n", posn)
57+
58+
// Output:
59+
//
60+
// Package path: fmt
61+
// Export data: fmt.a
62+
// Package members: [Errorf Formatter Fprint Fprintf Fprintln]...
63+
// Println type: func(a ...interface{}) (n int, err error)
64+
// Println location: $GOROOT/src/fmt/print.go:123:1
65+
}
66+
67+
// ExampleNewImporter demonstrates usage of NewImporter to provide type
68+
// information for dependencies when type-checking Go source code.
69+
func ExampleNewImporter() {
70+
const src = `package twopi
71+
72+
import "math"
73+
74+
const TwoPi = 2 * math.Pi
75+
`
76+
fset := token.NewFileSet()
77+
f, err := parser.ParseFile(fset, "twopi.go", src, 0)
78+
if err != nil {
79+
log.Fatal(err)
80+
}
81+
82+
packages := make(map[string]*types.Package)
83+
imp := gcexportdata.NewImporter(fset, packages)
84+
conf := types.Config{Importer: imp}
85+
pkg, err := conf.Check("twopi", fset, []*ast.File{f}, nil)
86+
if err != nil {
87+
log.Fatal(err)
88+
}
89+
90+
// object from imported package
91+
pi := packages["math"].Scope().Lookup("Pi")
92+
fmt.Printf("const %s.%s %s = %s // %s\n",
93+
pi.Pkg().Path(),
94+
pi.Name(),
95+
pi.Type(),
96+
pi.(*types.Const).Val(),
97+
fset.Position(pi.Pos()))
98+
99+
// object in source package
100+
twopi := pkg.Scope().Lookup("TwoPi")
101+
fmt.Printf("const %s %s = %s // %s\n",
102+
twopi.Name(),
103+
twopi.Type(),
104+
twopi.(*types.Const).Val(),
105+
fset.Position(twopi.Pos()))
106+
107+
// Output:
108+
//
109+
// const math.Pi untyped float = 3.14159 // $GOROOT/src/math/const.go:11:1
110+
// const TwoPi untyped float = 6.28319 // twopi.go:5:7
111+
}

go/gcexportdata/gcexportdata.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright 2016 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 gcexportdata provides functions for locating, reading, and
6+
// writing export data files containing type information produced by the
7+
// gc compiler. This package supports go1.7 export data format and all
8+
// later versions.
9+
//
10+
// This package replaces the deprecated golang.org/x/tools/go/gcimporter15
11+
// package, which will be deleted in October 2017.
12+
//
13+
// Although it might seem convenient for this package to live alongside
14+
// go/types in the standard library, this would cause version skew
15+
// problems for developer tools that use it, since they must be able to
16+
// consume the outputs of the gc compiler both before and after a Go
17+
// update such as from Go 1.7 to Go 1.8. Because this package lives in
18+
// golang.org/x/tools, sites can update their version of this repo some
19+
// time before the Go 1.8 release and rebuild and redeploy their
20+
// developer tools, which will then be able to consume both Go 1.7 and
21+
// Go 1.8 export data files, so they will work before and after the
22+
// Go update. (See discussion at https://github.com/golang/go/issues/15651.)
23+
//
24+
package gcexportdata // import "golang.org/x/tools/go/gcexportdata"
25+
26+
import (
27+
"bufio"
28+
"fmt"
29+
"go/token"
30+
"go/types"
31+
"io"
32+
"io/ioutil"
33+
34+
gcimporter "golang.org/x/tools/go/gcimporter15"
35+
)
36+
37+
// Find returns the name of an object (.o) or archive (.a) file
38+
// containing type information for the specified import path,
39+
// using the workspace layout conventions of go/build.
40+
// If no file was found, an empty filename is returned.
41+
//
42+
// A relative srcDir is interpreted relative to the current working directory.
43+
//
44+
// Find also returns the package's resolved (canonical) import path,
45+
// reflecting the effects of srcDir and vendoring on importPath.
46+
func Find(importPath string, srcDir string) (filename, path string) {
47+
return gcimporter.FindPkg(importPath, srcDir)
48+
}
49+
50+
// NewReader returns a reader for the export data section of an object
51+
// (.o) or archive (.a) file read from r. The new reader may provide
52+
// additional trailing data beyond the end of the export data.
53+
func NewReader(r io.Reader) (io.Reader, error) {
54+
buf := bufio.NewReader(r)
55+
_, err := gcimporter.FindExportData(buf)
56+
// If we ever switch to a zip-like archive format with the ToC
57+
// at the end, we can return the correct portion of export data,
58+
// but for now we must return the entire rest of the file.
59+
return buf, err
60+
}
61+
62+
// Read reads export data from in, decodes it, and returns type
63+
// information for the package.
64+
// The package name is specified by path.
65+
// File position information is added to fset.
66+
//
67+
// Read may inspect and add to the imports map to ensure that references
68+
// within the export data to other packages are consistent. The caller
69+
// must ensure that imports[path] does not exist, or exists but is
70+
// incomplete (see types.Package.Complete), and Read inserts the
71+
// resulting package into this map entry.
72+
//
73+
// On return, the state of the reader is undefined.
74+
func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) {
75+
data, err := ioutil.ReadAll(in)
76+
if err != nil {
77+
return nil, fmt.Errorf("reading export data for %q: %v", path, err)
78+
}
79+
_, pkg, err := gcimporter.BImportData(fset, imports, data, path)
80+
return pkg, err
81+
}
82+
83+
// Write writes encoded type information for the specified package to out.
84+
// The FileSet provides file position information for named objects.
85+
func Write(out io.Writer, fset *token.FileSet, pkg *types.Package) error {
86+
_, err := out.Write(gcimporter.BExportData(fset, pkg))
87+
return err
88+
}

go/gcexportdata/importer.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2016 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 gcexportdata
6+
7+
import (
8+
"fmt"
9+
"go/token"
10+
"go/types"
11+
"os"
12+
)
13+
14+
// NewImporter returns a new instance of the types.Importer interface
15+
// that reads type information from export data files written by gc.
16+
// The Importer also satisfies types.ImporterFrom.
17+
//
18+
// Export data files are located using "go build" workspace conventions
19+
// and the build.Default context.
20+
//
21+
// Use this importer instead of go/importer.For("gc", ...) to avoid the
22+
// version-skew problems described in the documentation of this package,
23+
// or to control the FileSet or access the imports map populated during
24+
// package loading.
25+
//
26+
func NewImporter(fset *token.FileSet, imports map[string]*types.Package) types.ImporterFrom {
27+
return importer{fset, imports}
28+
}
29+
30+
type importer struct {
31+
fset *token.FileSet
32+
imports map[string]*types.Package
33+
}
34+
35+
func (imp importer) Import(importPath string) (*types.Package, error) {
36+
return imp.ImportFrom(importPath, "", 0)
37+
}
38+
39+
func (imp importer) ImportFrom(importPath, srcDir string, mode types.ImportMode) (_ *types.Package, err error) {
40+
filename, path := Find(importPath, srcDir)
41+
if filename == "" {
42+
if importPath == "unsafe" {
43+
// Even for unsafe, call Find first in case
44+
// the package was vendored.
45+
return types.Unsafe, nil
46+
}
47+
return nil, fmt.Errorf("can't find import: %s", importPath)
48+
}
49+
50+
if pkg, ok := imp.imports[path]; ok && pkg.Complete() {
51+
return pkg, nil // cache hit
52+
}
53+
54+
// open file
55+
f, err := os.Open(filename)
56+
if err != nil {
57+
return nil, err
58+
}
59+
defer func() {
60+
f.Close()
61+
if err != nil {
62+
// add file name to error
63+
err = fmt.Errorf("reading export data: %s: %v", filename, err)
64+
}
65+
}()
66+
67+
r, err := NewReader(f)
68+
if err != nil {
69+
return nil, err
70+
}
71+
72+
return Read(r, imp.fset, imp.imports, path)
73+
}

go/gcimporter15/gcimporter.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
// standard library's go/importer package, specifically customizable
1616
// package data lookup. This package should be deleted once that
1717
// functionality becomes available in the standard library.
18+
//
19+
// Deprecated: this package will be deleted in October 2017.
20+
// New code should use golang.org/x/tools/go/gcexportdata.
21+
//
1822
package gcimporter // import "golang.org/x/tools/go/gcimporter15"
1923

2024
import (

0 commit comments

Comments
 (0)