mirror of
https://github.com/golang/go
synced 2024-11-18 17:54:57 -07:00
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 <gri@golang.org>
This commit is contained in:
parent
8b5a8a45ae
commit
1a954d519b
111
go/gcexportdata/example_test.go
Normal file
111
go/gcexportdata/example_test.go
Normal file
@ -0,0 +1,111 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gcexportdata_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/tools/go/gcexportdata"
|
||||
)
|
||||
|
||||
// ExampleRead uses gcexportdata.Read to load type information for the
|
||||
// "fmt" package from the fmt.a file produced by the gc compiler.
|
||||
func ExampleRead() {
|
||||
// Find the export data file.
|
||||
filename, path := gcexportdata.Find("fmt", "")
|
||||
if filename == "" {
|
||||
log.Fatalf("can't find export data for fmt")
|
||||
}
|
||||
fmt.Printf("Package path: %s\n", path)
|
||||
fmt.Printf("Export data: %s\n", filepath.Base(filename))
|
||||
|
||||
// Open and read the file.
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
r, err := gcexportdata.NewReader(f)
|
||||
if err != nil {
|
||||
log.Fatalf("reading export data %s: %v", filename, err)
|
||||
}
|
||||
|
||||
// Decode the export data.
|
||||
fset := token.NewFileSet()
|
||||
imports := make(map[string]*types.Package)
|
||||
pkg, err := gcexportdata.Read(r, fset, imports, path)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Print package information.
|
||||
fmt.Printf("Package members: %s...\n", pkg.Scope().Names()[:5])
|
||||
println := pkg.Scope().Lookup("Println")
|
||||
posn := fset.Position(println.Pos())
|
||||
posn.Line = 123 // make example deterministic
|
||||
fmt.Printf("Println type: %s\n", println.Type())
|
||||
fmt.Printf("Println location: %s\n", posn)
|
||||
|
||||
// Output:
|
||||
//
|
||||
// Package path: fmt
|
||||
// Export data: fmt.a
|
||||
// Package members: [Errorf Formatter Fprint Fprintf Fprintln]...
|
||||
// Println type: func(a ...interface{}) (n int, err error)
|
||||
// Println location: $GOROOT/src/fmt/print.go:123:1
|
||||
}
|
||||
|
||||
// ExampleNewImporter demonstrates usage of NewImporter to provide type
|
||||
// information for dependencies when type-checking Go source code.
|
||||
func ExampleNewImporter() {
|
||||
const src = `package twopi
|
||||
|
||||
import "math"
|
||||
|
||||
const TwoPi = 2 * math.Pi
|
||||
`
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, "twopi.go", src, 0)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
packages := make(map[string]*types.Package)
|
||||
imp := gcexportdata.NewImporter(fset, packages)
|
||||
conf := types.Config{Importer: imp}
|
||||
pkg, err := conf.Check("twopi", fset, []*ast.File{f}, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// object from imported package
|
||||
pi := packages["math"].Scope().Lookup("Pi")
|
||||
fmt.Printf("const %s.%s %s = %s // %s\n",
|
||||
pi.Pkg().Path(),
|
||||
pi.Name(),
|
||||
pi.Type(),
|
||||
pi.(*types.Const).Val(),
|
||||
fset.Position(pi.Pos()))
|
||||
|
||||
// object in source package
|
||||
twopi := pkg.Scope().Lookup("TwoPi")
|
||||
fmt.Printf("const %s %s = %s // %s\n",
|
||||
twopi.Name(),
|
||||
twopi.Type(),
|
||||
twopi.(*types.Const).Val(),
|
||||
fset.Position(twopi.Pos()))
|
||||
|
||||
// Output:
|
||||
//
|
||||
// const math.Pi untyped float = 3.14159 // $GOROOT/src/math/const.go:11:1
|
||||
// const TwoPi untyped float = 6.28319 // twopi.go:5:7
|
||||
}
|
88
go/gcexportdata/gcexportdata.go
Normal file
88
go/gcexportdata/gcexportdata.go
Normal file
@ -0,0 +1,88 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package gcexportdata provides functions for locating, reading, and
|
||||
// writing export data files containing type information produced by the
|
||||
// gc compiler. This package supports go1.7 export data format and all
|
||||
// later versions.
|
||||
//
|
||||
// This package replaces the deprecated golang.org/x/tools/go/gcimporter15
|
||||
// package, which will be deleted in October 2017.
|
||||
//
|
||||
// Although it might seem convenient for this package to live alongside
|
||||
// go/types in the standard library, this would cause version skew
|
||||
// problems for developer tools that use it, since they must be able to
|
||||
// consume the outputs of the gc compiler both before and after a Go
|
||||
// update such as from Go 1.7 to Go 1.8. Because this package lives in
|
||||
// golang.org/x/tools, sites can update their version of this repo some
|
||||
// time before the Go 1.8 release and rebuild and redeploy their
|
||||
// developer tools, which will then be able to consume both Go 1.7 and
|
||||
// Go 1.8 export data files, so they will work before and after the
|
||||
// Go update. (See discussion at https://github.com/golang/go/issues/15651.)
|
||||
//
|
||||
package gcexportdata // import "golang.org/x/tools/go/gcexportdata"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
gcimporter "golang.org/x/tools/go/gcimporter15"
|
||||
)
|
||||
|
||||
// Find returns the name of an object (.o) or archive (.a) file
|
||||
// containing type information for the specified import path,
|
||||
// using the workspace layout conventions of go/build.
|
||||
// If no file was found, an empty filename is returned.
|
||||
//
|
||||
// A relative srcDir is interpreted relative to the current working directory.
|
||||
//
|
||||
// Find also returns the package's resolved (canonical) import path,
|
||||
// reflecting the effects of srcDir and vendoring on importPath.
|
||||
func Find(importPath string, srcDir string) (filename, path string) {
|
||||
return gcimporter.FindPkg(importPath, srcDir)
|
||||
}
|
||||
|
||||
// NewReader returns a reader for the export data section of an object
|
||||
// (.o) or archive (.a) file read from r. The new reader may provide
|
||||
// additional trailing data beyond the end of the export data.
|
||||
func NewReader(r io.Reader) (io.Reader, error) {
|
||||
buf := bufio.NewReader(r)
|
||||
_, err := gcimporter.FindExportData(buf)
|
||||
// If we ever switch to a zip-like archive format with the ToC
|
||||
// at the end, we can return the correct portion of export data,
|
||||
// but for now we must return the entire rest of the file.
|
||||
return buf, err
|
||||
}
|
||||
|
||||
// Read reads export data from in, decodes it, and returns type
|
||||
// information for the package.
|
||||
// The package name is specified by path.
|
||||
// File position information is added to fset.
|
||||
//
|
||||
// Read may inspect and add to the imports map to ensure that references
|
||||
// within the export data to other packages are consistent. The caller
|
||||
// must ensure that imports[path] does not exist, or exists but is
|
||||
// incomplete (see types.Package.Complete), and Read inserts the
|
||||
// resulting package into this map entry.
|
||||
//
|
||||
// On return, the state of the reader is undefined.
|
||||
func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) {
|
||||
data, err := ioutil.ReadAll(in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading export data for %q: %v", path, err)
|
||||
}
|
||||
_, pkg, err := gcimporter.BImportData(fset, imports, data, path)
|
||||
return pkg, err
|
||||
}
|
||||
|
||||
// Write writes encoded type information for the specified package to out.
|
||||
// The FileSet provides file position information for named objects.
|
||||
func Write(out io.Writer, fset *token.FileSet, pkg *types.Package) error {
|
||||
_, err := out.Write(gcimporter.BExportData(fset, pkg))
|
||||
return err
|
||||
}
|
73
go/gcexportdata/importer.go
Normal file
73
go/gcexportdata/importer.go
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gcexportdata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"os"
|
||||
)
|
||||
|
||||
// NewImporter returns a new instance of the types.Importer interface
|
||||
// that reads type information from export data files written by gc.
|
||||
// The Importer also satisfies types.ImporterFrom.
|
||||
//
|
||||
// Export data files are located using "go build" workspace conventions
|
||||
// and the build.Default context.
|
||||
//
|
||||
// Use this importer instead of go/importer.For("gc", ...) to avoid the
|
||||
// version-skew problems described in the documentation of this package,
|
||||
// or to control the FileSet or access the imports map populated during
|
||||
// package loading.
|
||||
//
|
||||
func NewImporter(fset *token.FileSet, imports map[string]*types.Package) types.ImporterFrom {
|
||||
return importer{fset, imports}
|
||||
}
|
||||
|
||||
type importer struct {
|
||||
fset *token.FileSet
|
||||
imports map[string]*types.Package
|
||||
}
|
||||
|
||||
func (imp importer) Import(importPath string) (*types.Package, error) {
|
||||
return imp.ImportFrom(importPath, "", 0)
|
||||
}
|
||||
|
||||
func (imp importer) ImportFrom(importPath, srcDir string, mode types.ImportMode) (_ *types.Package, err error) {
|
||||
filename, path := Find(importPath, srcDir)
|
||||
if filename == "" {
|
||||
if importPath == "unsafe" {
|
||||
// Even for unsafe, call Find first in case
|
||||
// the package was vendored.
|
||||
return types.Unsafe, nil
|
||||
}
|
||||
return nil, fmt.Errorf("can't find import: %s", importPath)
|
||||
}
|
||||
|
||||
if pkg, ok := imp.imports[path]; ok && pkg.Complete() {
|
||||
return pkg, nil // cache hit
|
||||
}
|
||||
|
||||
// open file
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
f.Close()
|
||||
if err != nil {
|
||||
// add file name to error
|
||||
err = fmt.Errorf("reading export data: %s: %v", filename, err)
|
||||
}
|
||||
}()
|
||||
|
||||
r, err := NewReader(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return Read(r, imp.fset, imp.imports, path)
|
||||
}
|
@ -15,6 +15,10 @@
|
||||
// standard library's go/importer package, specifically customizable
|
||||
// package data lookup. This package should be deleted once that
|
||||
// functionality becomes available in the standard library.
|
||||
//
|
||||
// Deprecated: this package will be deleted in October 2017.
|
||||
// New code should use golang.org/x/tools/go/gcexportdata.
|
||||
//
|
||||
package gcimporter // import "golang.org/x/tools/go/gcimporter15"
|
||||
|
||||
import (
|
||||
|
Loading…
Reference in New Issue
Block a user