1
0
mirror of https://github.com/golang/go synced 2024-11-18 15:14:44 -07:00

go.tools/cmd/godex: support for common path prefixes

All of these work now as expected:

        godex code.google.com/p/go.tools/go/types
        godex go.tools/go/types
        godex go/types
        godex types

Also improved logging/verbose mode.

LGTM=adonovan
R=adonovan
CC=golang-codereviews
https://golang.org/cl/80930043
This commit is contained in:
Robert Griesemer 2014-03-26 14:47:52 -07:00
parent 03478d3d3e
commit 8b161c33a9
6 changed files with 124 additions and 53 deletions

View File

@ -9,18 +9,35 @@
// object files. Hence the exported data is truly what a compiler will // object files. Hence the exported data is truly what a compiler will
// see, at the cost of missing commentary. // see, at the cost of missing commentary.
// //
// Usage: godex [flags] {path|qualifiedIdent} // Usage: godex [flags] {path[.name]}
// //
// Each argument must be a package path, or a qualified identifier. // Each argument must be a (possibly partial) package path, optionally
// followed by a dot and the name of a package object:
//
// godex math
// godex math.Sin
// godex math.Sin fmt.Printf
// godex go/types
//
// All but the last path element may contain dots. godex automatically
// tries all possible package path prefixes for non-standard library
// packages if only a partial package path is given. For instance, for
// the path "go/types", godex prepends "code.google.com/p/go.tools".
//
// The prefixes are computed by searching the directories specified by
// the GOPATH environment variable (and by excluding the build os and
// architecture specific directory names from the path). The search
// order is depth-first and alphabetic; for a partial path "foo", a
// package "a/foo" is found before "b/foo".
// //
// The flags are: // The flags are:
// //
// -s=src // -s=""
// only consider packages from src, where src is one of the supported compilers // only consider packages from src, where src is one of the supported compilers
// -v // -v=false
// verbose mode // verbose mode
// //
// The following sources are supported: // The following sources (-s arguments) are supported:
// //
// gc // gc
// gc-generated object files // gc-generated object files
@ -33,6 +50,6 @@
// //
// If no -s argument is provided, godex will try to find a matching source. // If no -s argument is provided, godex will try to find a matching source.
// //
// TODO(gri) expand this documentation
//
package main package main
// BUG(gri) std-library packages should also benefit from auto-generated prefixes.

View File

@ -11,5 +11,5 @@ import (
) )
func init() { func init() {
register("gc", protect(gcimporter.Import)) register("gc", gcimporter.Import)
} }

View File

@ -21,10 +21,10 @@ func init() {
// importer for default gccgo // importer for default gccgo
var inst gccgoimporter.GccgoInstallation var inst gccgoimporter.GccgoInstallation
inst.InitFromDriver("gccgo") inst.InitFromDriver("gccgo")
register("gccgo", protect(inst.GetImporter(nil))) register("gccgo", inst.GetImporter(nil))
// importer for gccgo using condensed export format (experimental) // importer for gccgo using condensed export format (experimental)
register("gccgo-new", protect(gccgoNewImporter)) register("gccgo-new", gccgoNewImporter)
} }
func gccgoNewImporter(packages map[string]*types.Package, path string) (*types.Package, error) { func gccgoNewImporter(packages map[string]*types.Package, path string) (*types.Package, error) {

View File

@ -8,6 +8,8 @@ import (
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
"go/build"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -16,17 +18,20 @@ import (
) )
var ( var (
source = flag.String("s", "", "only consider packages from this source") source = flag.String("s", "", "only consider packages from src, where src is one of the supported compilers")
verbose = flag.Bool("v", false, "verbose mode") verbose = flag.Bool("v", false, "verbose mode")
) )
// lists of registered sources and corresponding importers
var ( var (
sources []string // sources of export data corresponding to importers sources []string
importers []types.Importer // importers for corresponding sources importers []types.Importer
importFailed = errors.New("import failed") importFailed = errors.New("import failed")
packages = make(map[string]*types.Package)
) )
// map of imported packages
var packages = make(map[string]*types.Package)
func usage() { func usage() {
fmt.Fprintln(os.Stderr, "usage: godex [flags] {path|qualifiedIdent}") fmt.Fprintln(os.Stderr, "usage: godex [flags] {path|qualifiedIdent}")
flag.PrintDefaults() flag.PrintDefaults()
@ -46,7 +51,7 @@ func main() {
report("no package name, path, or file provided") report("no package name, path, or file provided")
} }
imp := tryImport imp := tryImports
if *source != "" { if *source != "" {
imp = lookup(*source) imp = lookup(*source)
if imp == nil { if imp == nil {
@ -56,14 +61,17 @@ func main() {
for _, arg := range flag.Args() { for _, arg := range flag.Args() {
path, name := splitPathIdent(arg) path, name := splitPathIdent(arg)
if *verbose { logf("\tprocessing %q: path = %q, name = %s\n", arg, path, name)
fmt.Fprintf(os.Stderr, "(processing %q: path = %q, name = %s)\n", arg, path, name)
} // generate possible package path prefixes
// (at the moment we do this for each argument - should probably cache this)
prefixes := make(chan string)
go genPrefixes(prefixes)
// import package // import package
pkg, err := imp(packages, path) pkg, err := tryPrefixes(packages, prefixes, path, imp)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "ignoring %q: %s\n", path, err) logf("\t=> ignoring %q: %s\n", path, err)
continue continue
} }
@ -82,6 +90,57 @@ func main() {
} }
} }
func logf(format string, args ...interface{}) {
if *verbose {
fmt.Fprintf(os.Stderr, format, args...)
}
}
// splitPathIdent splits a path.name argument into its components.
// All but the last path element may contain dots.
func splitPathIdent(arg string) (path, name string) {
if i := strings.LastIndex(arg, "."); i >= 0 {
if j := strings.LastIndex(arg, "/"); j < i {
// '.' is not part of path
path = arg[:i]
name = arg[i+1:]
return
}
}
path = arg
return
}
// tryPrefixes tries to import the package given by (the possibly partial) path using the given importer imp
// by prepending all possible prefixes to path. It returns with the first package that it could import, or
// with an error.
func tryPrefixes(packages map[string]*types.Package, prefixes chan string, path string, imp types.Importer) (pkg *types.Package, err error) {
for prefix := range prefixes {
logf("\ttrying prefix %q\n", prefix)
prepath := filepath.Join(prefix, path)
pkg, err = imp(packages, prepath)
if err == nil {
break
}
logf("\t=> importing %q failed: %s\n", prepath, err)
}
return
}
// tryImports is an importer that tries all registered importers
// successively until one of them succeeds or all of them failed.
func tryImports(packages map[string]*types.Package, path string) (pkg *types.Package, err error) {
for i, imp := range importers {
logf("\t\ttrying %s import\n", sources[i])
pkg, err = imp(packages, path)
if err == nil {
break
}
logf("\t\t=> %s import failed: %s\n", sources[i], err)
}
return
}
// protect protects an importer imp from panics and returns the protected importer. // protect protects an importer imp from panics and returns the protected importer.
func protect(imp types.Importer) types.Importer { func protect(imp types.Importer) types.Importer {
return func(packages map[string]*types.Package, path string) (pkg *types.Package, err error) { return func(packages map[string]*types.Package, path string) (pkg *types.Package, err error) {
@ -95,44 +154,16 @@ func protect(imp types.Importer) types.Importer {
} }
} }
func tryImport(packages map[string]*types.Package, path string) (pkg *types.Package, err error) { // register registers an importer imp for a given source src.
for i, imp := range importers {
if *verbose {
fmt.Fprintf(os.Stderr, "(trying source: %s)\n", sources[i])
}
pkg, err = imp(packages, path)
if err == nil {
break
}
}
return
}
// splitPathIdent splits a path.name argument into its components.
// All but the last path element may contain dots.
// TODO(gri) document this also in doc.go.
func splitPathIdent(arg string) (path, name string) {
const sep = string(filepath.Separator)
if i := strings.LastIndex(arg, "."); i >= 0 {
if j := strings.LastIndex(arg, sep); j < i {
// '.' is not part of path
path = arg[:i]
name = arg[i+1:]
return
}
}
path = arg
return
}
func register(src string, imp types.Importer) { func register(src string, imp types.Importer) {
if lookup(src) != nil { if lookup(src) != nil {
panic(src + " importer already registered") panic(src + " importer already registered")
} }
sources = append(sources, src) sources = append(sources, src)
importers = append(importers, imp) importers = append(importers, protect(imp))
} }
// lookup returns the importer imp for a given source src.
func lookup(src string) types.Importer { func lookup(src string) types.Importer {
for i, s := range sources { for i, s := range sources {
if s == src { if s == src {
@ -141,3 +172,26 @@ func lookup(src string) types.Importer {
} }
return nil return nil
} }
func genPrefixes(out chan string) {
out <- "" // try no prefix
platform := build.Default.GOOS + "_" + build.Default.GOARCH
for _, dirname := range filepath.SplitList(build.Default.GOPATH) {
walkDir(filepath.Join(dirname, "pkg", platform), "", out)
}
close(out)
}
func walkDir(dirname, prefix string, out chan string) {
fiList, err := ioutil.ReadDir(dirname)
if err != nil {
return
}
for _, fi := range fiList {
if fi.IsDir() && !strings.HasPrefix(fi.Name(), ".") {
prefix := filepath.Join(prefix, fi.Name())
out <- prefix
walkDir(filepath.Join(dirname, fi.Name()), prefix, out)
}
}
}

View File

@ -85,7 +85,7 @@ func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) boo
} }
} }
p.printf("package %s\n\n", pkg.Name()) p.printf("package %s // %q\n\n", pkg.Name(), pkg.Path())
if len(consts) > 0 { if len(consts) > 0 {
p.print("const (\n") p.print("const (\n")

View File

@ -11,7 +11,7 @@ import (
) )
func init() { func init() {
register("source", protect(sourceImporter)) register("source", sourceImporter)
} }
func sourceImporter(packages map[string]*types.Package, path string) (*types.Package, error) { func sourceImporter(packages map[string]*types.Package, path string) (*types.Package, error) {