mirror of
https://github.com/golang/go
synced 2024-11-19 01:34:40 -07:00
83f21b9226
R=golang-dev, adonovan CC=golang-dev https://golang.org/cl/9500043
156 lines
4.4 KiB
Go
156 lines
4.4 KiB
Go
package ssa
|
|
|
|
// This file defines an implementation of the types.Importer interface
|
|
// (func) that loads the transitive closure of dependencies of a
|
|
// "main" package.
|
|
|
|
import (
|
|
"errors"
|
|
"go/ast"
|
|
"go/build"
|
|
"go/parser"
|
|
"go/token"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"code.google.com/p/go.tools/go/types"
|
|
)
|
|
|
|
// Prototype of a function that locates, reads and parses a set of
|
|
// source files given an import path.
|
|
//
|
|
// fset is the fileset to which the ASTs should be added.
|
|
// path is the imported path, e.g. "sync/atomic".
|
|
//
|
|
// On success, the function returns files, the set of ASTs produced,
|
|
// or the first error encountered.
|
|
//
|
|
type SourceLoader func(fset *token.FileSet, path string) (files []*ast.File, err error)
|
|
|
|
// doImport loads the typechecker package identified by path
|
|
// Implements the types.Importer prototype.
|
|
//
|
|
func (b *Builder) doImport(imports map[string]*types.Package, path string) (typkg *types.Package, err error) {
|
|
// Package unsafe is handled specially, and has no ssa.Package.
|
|
if path == "unsafe" {
|
|
return types.Unsafe, nil
|
|
}
|
|
|
|
if pkg := b.Prog.Packages[path]; pkg != nil {
|
|
typkg = pkg.Types
|
|
imports[path] = typkg
|
|
return // positive cache hit
|
|
}
|
|
|
|
if err = b.importErrs[path]; err != nil {
|
|
return // negative cache hit
|
|
}
|
|
var files []*ast.File
|
|
var info *TypeInfo
|
|
if b.Context.Mode&UseGCImporter != 0 {
|
|
typkg, err = types.GcImport(imports, path)
|
|
} else {
|
|
files, err = b.Context.Loader(b.Prog.Files, path)
|
|
if err == nil {
|
|
typkg, info, err = b.typecheck(files)
|
|
}
|
|
}
|
|
if err != nil {
|
|
// Cache failure
|
|
b.importErrs[path] = err
|
|
return nil, err
|
|
}
|
|
|
|
// Cache success
|
|
imports[path] = typkg // cache for just this package.
|
|
b.Prog.Packages[path] = b.createPackageImpl(typkg, path, files, info) // cache across all packages
|
|
|
|
return typkg, nil
|
|
}
|
|
|
|
// GorootLoader is an implementation of the SourceLoader function
|
|
// prototype that loads and parses Go source files from the package
|
|
// directory beneath $GOROOT/src/pkg.
|
|
//
|
|
// TODO(adonovan): get rsc and adg (go/build owners) to review this.
|
|
// TODO(adonovan): permit clients to specify a non-default go/build.Context.
|
|
//
|
|
func GorootLoader(fset *token.FileSet, path string) (files []*ast.File, err error) {
|
|
// TODO(adonovan): fix: Do we need cwd? Shouldn't ImportDir(path) / $GOROOT suffice?
|
|
srcDir, err := os.Getwd()
|
|
if err != nil {
|
|
return // serious misconfiguration
|
|
}
|
|
bp, err := build.Import(path, srcDir, 0)
|
|
if err != nil {
|
|
return // import failed
|
|
}
|
|
files, err = ParseFiles(fset, bp.Dir, bp.GoFiles...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return
|
|
}
|
|
|
|
// ParseFiles parses the Go source files files within directory dir
|
|
// and returns their ASTs, or the first parse error if any.
|
|
//
|
|
// This utility function is provided to facilitate implementing a
|
|
// SourceLoader.
|
|
//
|
|
func ParseFiles(fset *token.FileSet, dir string, files ...string) (parsed []*ast.File, err error) {
|
|
for _, file := range files {
|
|
var f *ast.File
|
|
if !filepath.IsAbs(file) {
|
|
file = filepath.Join(dir, file)
|
|
}
|
|
f, err = parser.ParseFile(fset, file, nil, parser.DeclarationErrors)
|
|
if err != nil {
|
|
return // parsing failed
|
|
}
|
|
parsed = append(parsed, f)
|
|
}
|
|
return
|
|
}
|
|
|
|
// CreatePackageFromArgs builds an initial Package from a list of
|
|
// command-line arguments.
|
|
// If args is a list of *.go files, they are parsed and type-checked.
|
|
// If args is a Go package import path, that package is imported.
|
|
// rest is the suffix of args that were not consumed.
|
|
//
|
|
// This utility is provided to facilitate construction of command-line
|
|
// tools with a consistent user interface.
|
|
//
|
|
func CreatePackageFromArgs(builder *Builder, args []string) (pkg *Package, rest []string, err error) {
|
|
var pkgname string
|
|
var files []*ast.File
|
|
|
|
switch {
|
|
case len(args) == 0:
|
|
err = errors.New("No *.go source files nor package name was specified.")
|
|
|
|
case strings.HasSuffix(args[0], ".go"):
|
|
// % tool a.go b.go ...
|
|
// Leading consecutive *.go arguments constitute main package.
|
|
pkgname = "main"
|
|
i := 1
|
|
for ; i < len(args) && strings.HasSuffix(args[i], ".go"); i++ {
|
|
}
|
|
files, err = ParseFiles(builder.Prog.Files, ".", args[:i]...)
|
|
rest = args[i:]
|
|
|
|
default:
|
|
// % tool my/package ...
|
|
// First argument is import path of main package.
|
|
pkgname = args[0]
|
|
rest = args[1:]
|
|
files, err = builder.Context.Loader(builder.Prog.Files, pkgname)
|
|
}
|
|
if err == nil {
|
|
pkg, err = builder.CreatePackage(pkgname, files)
|
|
}
|
|
return
|
|
}
|