1
0
mirror of https://github.com/golang/go synced 2024-10-01 05:18:33 -06:00

go/packages: alter the internal api to go list

This separates the go list specific behavior from the generalised go/packages
loading behaviour, to enable alternate build system back ends.

Change-Id: I07e7649f8f2afc7470a3ee3d0d743cbbcc6f713e
Reviewed-on: https://go-review.googlesource.com/125715
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
Ian Cottrell 2018-07-24 14:54:35 -04:00
parent 65cc56d756
commit b1e4acd68a
4 changed files with 105 additions and 63 deletions

View File

@ -30,7 +30,7 @@ type GoTooOldError struct {
// golistPackages uses the "go list" command to expand the
// pattern words and return metadata for the specified packages.
// dir may be "" and env may be nil, as per os/exec.Command.
func golistPackages(cfg *rawConfig, words ...string) ([]*loaderPackage, error) {
func golistPackages(cfg *rawConfig, words ...string) ([]*rawPackage, error) {
// Fields must match go list;
// see $GOROOT/src/cmd/go/internal/load/pkg.go.
type jsonPackage struct {
@ -71,7 +71,7 @@ func golistPackages(cfg *rawConfig, words ...string) ([]*loaderPackage, error) {
return nil, err
}
// Decode the JSON and convert it to Package form.
var result []*loaderPackage
var result []*rawPackage
for dec := json.NewDecoder(buf); dec.More(); {
p := new(jsonPackage)
if err := dec.Decode(p); err != nil {
@ -150,17 +150,15 @@ func golistPackages(cfg *rawConfig, words ...string) ([]*loaderPackage, error) {
imports[id] = id // identity import
}
pkg := &loaderPackage{
Package: &Package{
ID: id,
Name: p.Name,
GoFiles: absJoin(p.Dir, p.GoFiles, p.CgoFiles),
OtherFiles: absJoin(p.Dir, p.SFiles, p.CFiles),
},
pkgpath: pkgpath,
imports: imports,
export: export,
indirect: p.DepOnly,
pkg := &rawPackage{
ID: id,
Name: p.Name,
GoFiles: absJoin(p.Dir, p.GoFiles, p.CgoFiles),
OtherFiles: absJoin(p.Dir, p.SFiles, p.CFiles),
PkgPath: pkgpath,
Imports: imports,
Export: export,
DepOnly: p.DepOnly,
}
result = append(result, pkg)
}

View File

@ -23,9 +23,10 @@ package packages
import (
"fmt"
"go/build"
"strings"
legacy "golang.org/x/tools/go/loader"
"golang.org/x/tools/imports"
"strings"
)
func loaderFallback(dir string, env []string, patterns []string) ([]*Package, error) {
@ -84,6 +85,13 @@ func loaderFallback(dir string, env []string, patterns []string) ([]*Package, er
}
allpkgs[id] = &loaderPackage{
raw: &rawPackage{
ID: id,
Name: lpkg.Pkg.Name(),
Imports: pkgimports,
GoFiles: goFiles,
DepOnly: !initial[lpkg],
},
Package: &Package{
ID: id,
Name: lpkg.Pkg.Name(),
@ -96,14 +104,13 @@ func loaderFallback(dir string, env []string, patterns []string) ([]*Package, er
IllTyped: !lpkg.TransitivelyErrorFree,
OtherFiles: nil, // Never set for the fallback, because we can't extract from loader.
},
imports: pkgimports,
}
}
// Do a second pass to populate imports.
for _, pkg := range allpkgs {
pkg.Imports = make(map[string]*Package)
for imppath, impid := range pkg.imports {
for imppath, impid := range pkg.raw.Imports {
target, ok := allpkgs[impid]
if !ok {
// return nil, fmt.Errorf("could not load package: %v", impid)

View File

@ -135,10 +135,7 @@ type Config struct {
// Load and returns the Go packages named by the given patterns.
func Load(cfg *Config, patterns ...string) ([]*Package, error) {
l := &loader{}
if cfg != nil {
l.Config = *cfg
}
l := newLoader(cfg)
return l.load(patterns...)
}
@ -209,23 +206,12 @@ type Package struct {
// loaderPackage augments Package with state used during the loading phase
type loaderPackage struct {
raw *rawPackage
*Package
// export holds the path to the export data file
// for this package, if mode == TypeCheck.
// The export data file contains the package's type information
// in a compiler-specific format; see
// golang.org/x/tools/go/{gc,gccgo}exportdata.
// May be the empty string if the build failed.
export string
indirect bool // package is a dependency, not explicitly requested
imports map[string]string // nominal form of Imports graph
importErrors map[string]error // maps each bad import to its error
importErrors map[string]error // maps each bad import to its error
loadOnce sync.Once
color uint8 // for cycle detection
mark, needsrc bool // used in TypeCheck mode only
pkgpath string
}
func (lpkg *Package) String() string { return lpkg.ID }
@ -237,7 +223,11 @@ type loader struct {
exportMu sync.Mutex // enforces mutual exclusion of exportdata operations
}
func (ld *loader) load(patterns ...string) ([]*Package, error) {
func newLoader(cfg *Config) *loader {
ld := &loader{}
if cfg != nil {
ld.Config = *cfg
}
if ld.Context == nil {
ld.Context = context.Background()
}
@ -259,7 +249,10 @@ func (ld *loader) load(patterns ...string) ([]*Package, error) {
}
}
}
return ld
}
func (ld *loader) load(patterns ...string) ([]*Package, error) {
if len(patterns) == 0 {
return nil, fmt.Errorf("no packages to load")
}
@ -274,27 +267,39 @@ func (ld *loader) load(patterns ...string) ([]*Package, error) {
if err != nil {
return nil, err
}
ld.pkgs = make(map[string]*loaderPackage)
return ld.loadFrom(list...)
}
func (ld *loader) loadFrom(list ...*rawPackage) ([]*Package, error) {
ld.pkgs = make(map[string]*loaderPackage, len(list))
var initial []*loaderPackage
// first pass, fixup and build the map and roots
for _, pkg := range list {
ld.pkgs[pkg.ID] = pkg
// Record the set of initial packages
// corresponding to the patterns.
if !pkg.indirect {
initial = append(initial, pkg)
if ld.Mode == LoadSyntax {
pkg.needsrc = true
}
lpkg := &loaderPackage{
raw: pkg,
Package: &Package{
ID: pkg.ID,
Name: pkg.Name,
GoFiles: pkg.GoFiles,
OtherFiles: pkg.OtherFiles,
},
// TODO: should needsrc also be true if pkg.Export == ""
needsrc: ld.Mode >= LoadAllSyntax,
}
if ld.Mode >= LoadAllSyntax {
pkg.needsrc = true
ld.pkgs[lpkg.ID] = lpkg
if !pkg.DepOnly {
initial = append(initial, lpkg)
if ld.Mode == LoadSyntax {
lpkg.needsrc = true
}
}
}
if len(ld.pkgs) == 0 {
return nil, fmt.Errorf("packages not found")
}
if len(initial) == 0 {
return nil, fmt.Errorf("packages had no initial set")
}
// Materialize the import graph.
@ -325,9 +330,8 @@ func (ld *loader) load(patterns ...string) ([]*Package, error) {
}
lpkg.color = grey
stack = append(stack, lpkg) // push
imports := make(map[string]*Package)
for importPath, id := range lpkg.imports {
lpkg.Imports = make(map[string]*Package, len(lpkg.raw.Imports))
for importPath, id := range lpkg.raw.Imports {
var importErr error
imp := ld.pkgs[id]
if imp == nil {
@ -347,10 +351,8 @@ func (ld *loader) load(patterns ...string) ([]*Package, error) {
if visit(imp) {
lpkg.needsrc = true
}
imports[importPath] = imp.Package
lpkg.Imports[importPath] = imp.Package
}
lpkg.imports = nil // no longer needed
lpkg.Imports = imports
stack = stack[:len(stack)-1] // pop
lpkg.color = black
@ -414,7 +416,7 @@ func (ld *loader) loadRecursive(lpkg *loaderPackage) {
// after immediate dependencies are loaded.
// Precondition: ld.mode != Metadata.
func (ld *loader) loadPackage(lpkg *loaderPackage) {
if lpkg.pkgpath == "unsafe" {
if lpkg.raw.PkgPath == "unsafe" {
// Fill in the blanks to avoid surprises.
lpkg.Types = types.Unsafe
lpkg.Fset = ld.Fset
@ -449,7 +451,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
// Call NewPackage directly with explicit name.
// This avoids skew between golist and go/types when the files'
// package declarations are inconsistent.
lpkg.Types = types.NewPackage(lpkg.pkgpath, lpkg.Name)
lpkg.Types = types.NewPackage(lpkg.raw.PkgPath, lpkg.Name)
lpkg.TypesInfo = &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
@ -587,7 +589,7 @@ func (ld *loader) parseFiles(filenames []string) ([]*ast.File, []error) {
// loadFromExportData returns type information for the specified
// package, loading it from an export data file on the first request.
func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error) {
if lpkg.pkgpath == "" {
if lpkg.raw.PkgPath == "" {
log.Fatalf("internal error: Package %s has no PkgPath", lpkg)
}
@ -612,11 +614,11 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error
lpkg.IllTyped = true // fail safe
if lpkg.export == "" {
if lpkg.raw.Export == "" {
// Errors while building export data will have been printed to stderr.
return nil, fmt.Errorf("no export data file")
}
f, err := os.Open(lpkg.export)
f, err := os.Open(lpkg.raw.Export)
if err != nil {
return nil, err
}
@ -630,7 +632,7 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error
// queries.)
r, err := gcexportdata.NewReader(f)
if err != nil {
return nil, fmt.Errorf("reading %s: %v", lpkg.export, err)
return nil, fmt.Errorf("reading %s: %v", lpkg.raw.Export, err)
}
// Build the view.
@ -671,7 +673,7 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error
if !seen[p] {
seen[p] = true
if p.Types != nil {
view[p.pkgpath] = p.Types
view[p.raw.PkgPath] = p.Types
} else {
copyback = append(copyback, p)
}
@ -684,15 +686,15 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error
// Parse the export data.
// (May create/modify packages in view.)
tpkg, err := gcexportdata.Read(r, ld.Fset, view, lpkg.pkgpath)
tpkg, err := gcexportdata.Read(r, ld.Fset, view, lpkg.raw.PkgPath)
if err != nil {
return nil, fmt.Errorf("reading %s: %v", lpkg.export, err)
return nil, fmt.Errorf("reading %s: %v", lpkg.raw.Export, err)
}
// For each newly created types.Package in the view,
// save it in the main graph.
for _, p := range copyback {
p.Types = view[p.pkgpath] // may still be nil
p.Types = view[p.raw.PkgPath] // may still be nil
}
lpkg.Types = tpkg

View File

@ -13,6 +13,41 @@ import (
// This file contains the structs needed at the seam between the packages
// loader and the underlying build tool
// rawPackage is the serialized form of a package
type rawPackage struct {
// ID is a unique identifier for a package.
// This is the same as Package.ID
ID string
// Name is the package name as it appears in the package source code.
// This is the same as Package.name
Name string `json:",omitempty"`
// This is the package path as used in the export data.
// This is used to map entries in the export data back to the package they
// come from.
// This is not currently exposed in Package.
PkgPath string `json:",omitempty"`
// Imports maps import paths appearing in the package's Go source files
// to corresponding package identifiers.
// This is similar to Package.Imports, but maps to the ID rather than the
// package itself.
Imports map[string]string `json:",omitempty"`
// Export is the absolute path to a file containing the export data for the
// package.
// This is not currently exposed in Package.
Export string `json:",omitempty"`
// GoFiles lists the absolute file paths of the package's Go source files.
// This is the same as Package.GoFiles
GoFiles []string `json:",omitempty"`
// OtherFiles lists the absolute file paths of the package's non-Go source
// files.
// This is the same as Package.OtherFiles
OtherFiles []string `json:",omitempty"`
// DepOnly marks a package that is in a list because it was a dependency.
// It is used to find the roots when constructing a graph from a package list.
// This is not exposed in Package.
DepOnly bool `json:",omitempty"`
}
// rawConfig specifies details about what raw package information is needed
// and how the underlying build tool should load package data.
type rawConfig struct {