GoPLS Viewer

Home|gopls/cmd/godex/godex.go
1// Copyright 2014 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
5package main
6
7import (
8    "errors"
9    "flag"
10    "fmt"
11    "go/build"
12    "go/types"
13    "io/ioutil"
14    "os"
15    "path/filepath"
16    "strings"
17)
18
19var (
20    source  = flag.String("s""""only consider packages from src, where src is one of the supported compilers")
21    verbose = flag.Bool("v"false"verbose mode")
22)
23
24// lists of registered sources and corresponding importers
25var (
26    sources         []string
27    importers       []types.Importer
28    errImportFailed = errors.New("import failed")
29)
30
31func usage() {
32    fmt.Fprintln(os.Stderr"usage: godex [flags] {path|qualifiedIdent}")
33    flag.PrintDefaults()
34    os.Exit(2)
35}
36
37func report(msg string) {
38    fmt.Fprintln(os.Stderr"error: "+msg)
39    os.Exit(2)
40}
41
42func main() {
43    flag.Usage = usage
44    flag.Parse()
45
46    if flag.NArg() == 0 {
47        report("no package name, path, or file provided")
48    }
49
50    var imp types.Importer = new(tryImporters)
51    if *source != "" {
52        imp = lookup(*source)
53        if imp == nil {
54            report("source (-s argument) must be one of: " + strings.Join(sources", "))
55        }
56    }
57
58    for _arg := range flag.Args() {
59        pathname := splitPathIdent(arg)
60        logf("\tprocessing %q: path = %q, name = %s\n"argpathname)
61
62        // generate possible package path prefixes
63        // (at the moment we do this for each argument - should probably cache the generated prefixes)
64        prefixes := make(chan string)
65        go genPrefixes(prefixes, !filepath.IsAbs(path) && !build.IsLocalImport(path))
66
67        // import package
68        pkgerr := tryPrefixes(prefixespathimp)
69        if err != nil {
70            logf("\t=> ignoring %q: %s\n"patherr)
71            continue
72        }
73
74        // filter objects if needed
75        var filter func(types.Objectbool
76        if name != "" {
77            filter = func(obj types.Objectbool {
78                // TODO(gri) perhaps use regular expression matching here?
79                return obj.Name() == name
80            }
81        }
82
83        // print contents
84        print(os.Stdoutpkgfilter)
85    }
86}
87
88func logf(format stringargs ...interface{}) {
89    if *verbose {
90        fmt.Fprintf(os.Stderrformatargs...)
91    }
92}
93
94// splitPathIdent splits a path.name argument into its components.
95// All but the last path element may contain dots.
96func splitPathIdent(arg string) (pathname string) {
97    if i := strings.LastIndex(arg"."); i >= 0 {
98        if j := strings.LastIndex(arg"/"); j < i {
99            // '.' is not part of path
100            path = arg[:i]
101            name = arg[i+1:]
102            return
103        }
104    }
105    path = arg
106    return
107}
108
109// tryPrefixes tries to import the package given by (the possibly partial) path using the given importer imp
110// by prepending all possible prefixes to path. It returns with the first package that it could import, or
111// with an error.
112func tryPrefixes(prefixes chan stringpath stringimp types.Importer) (pkg *types.Packageerr error) {
113    for prefix := range prefixes {
114        actual := path
115        if prefix == "" {
116            // don't use filepath.Join as it will sanitize the path and remove
117            // a leading dot and then the path is not recognized as a relative
118            // package path by the importers anymore
119            logf("\ttrying no prefix\n")
120        } else {
121            actual = filepath.Join(prefixpath)
122            logf("\ttrying prefix %q\n"prefix)
123        }
124        pkgerr = imp.Import(actual)
125        if err == nil {
126            break
127        }
128        logf("\t=> importing %q failed: %s\n"actualerr)
129    }
130    return
131}
132
133// tryImporters is an importer that tries all registered importers
134// successively until one of them succeeds or all of them failed.
135type tryImporters struct{}
136
137func (t *tryImportersImport(path string) (pkg *types.Packageerr error) {
138    for iimp := range importers {
139        logf("\t\ttrying %s import\n"sources[i])
140        pkgerr = imp.Import(path)
141        if err == nil {
142            break
143        }
144        logf("\t\t=> %s import failed: %s\n"sources[i], err)
145    }
146    return
147}
148
149type protector struct {
150    imp types.Importer
151}
152
153func (p *protectorImport(path string) (pkg *types.Packageerr error) {
154    defer func() {
155        if recover() != nil {
156            pkg = nil
157            err = errImportFailed
158        }
159    }()
160    return p.imp.Import(path)
161}
162
163// protect protects an importer imp from panics and returns the protected importer.
164func protect(imp types.Importertypes.Importer {
165    return &protector{imp}
166}
167
168// register registers an importer imp for a given source src.
169func register(src stringimp types.Importer) {
170    if lookup(src) != nil {
171        panic(src + " importer already registered")
172    }
173    sources = append(sourcessrc)
174    importers = append(importersprotect(imp))
175}
176
177// lookup returns the importer imp for a given source src.
178func lookup(src stringtypes.Importer {
179    for is := range sources {
180        if s == src {
181            return importers[i]
182        }
183    }
184    return nil
185}
186
187func genPrefixes(out chan stringall bool) {
188    out <- ""
189    if all {
190        platform := build.Default.GOOS + "_" + build.Default.GOARCH
191        dirnames := append([]string{build.Default.GOROOT}, filepath.SplitList(build.Default.GOPATH)...)
192        for _dirname := range dirnames {
193            walkDir(filepath.Join(dirname"pkg"platform), ""out)
194        }
195    }
196    close(out)
197}
198
199func walkDir(dirnameprefix stringout chan string) {
200    fiListerr := ioutil.ReadDir(dirname)
201    if err != nil {
202        return
203    }
204    for _fi := range fiList {
205        if fi.IsDir() && !strings.HasPrefix(fi.Name(), ".") {
206            prefix := filepath.Join(prefixfi.Name())
207            out <- prefix
208            walkDir(filepath.Join(dirnamefi.Name()), prefixout)
209        }
210    }
211}
212
MembersX
ioutil
usage
main.RangeStmt_1156.BlockStmt.name
lookup.RangeStmt_4532.s
walkDir.RangeStmt_5094.fi
protect
genPrefixes.all
os
main.RangeStmt_1156.BlockStmt.path
splitPathIdent.path
splitPathIdent.i
tryPrefixes.RangeStmt_2810.prefix
tryImporters.Import.RangeStmt_3559.imp
errors
tryImporters.Import.t
register.imp
lookup.RangeStmt_4532.i
lookup
genPrefixes.out
build
report
main
main.RangeStmt_1156.BlockStmt.pkg
tryPrefixes.err
protector.Import.pkg
tryPrefixes.path
tryImporters.Import.path
protector.Import.path
protect.imp
lookup.src
main.RangeStmt_1156.BlockStmt.prefixes
tryPrefixes.pkg
register
tryImporters.Import.err
flag
tryImporters.Import
walkDir.err
fmt
main.imp
main.RangeStmt_1156.BlockStmt.err
tryImporters.Import.RangeStmt_3559.i
protector.Import.p
splitPathIdent.name
tryImporters
protector.Import
protector.Import.err
walkDir.fiList
tryPrefixes.RangeStmt_2810.BlockStmt.actual
genPrefixes.BlockStmt.dirnames
walkDir.RangeStmt_5094.BlockStmt.BlockStmt.prefix
sources
main.RangeStmt_1156.arg
logf.format
protector
protector.imp
register.src
logf
splitPathIdent
tryImporters.Import.pkg
tryPrefixes.imp
genPrefixes.BlockStmt.RangeStmt_4850.dirname
filepath
strings
splitPathIdent.arg
splitPathIdent.BlockStmt.j
tryPrefixes
tryPrefixes.prefixes
walkDir.dirname
report.msg
genPrefixes
walkDir.out
importers
logf.args
walkDir
walkDir.prefix
Members
X