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.
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")
24 // lists of registered sources and corresponding importers
27 importers []types.Importer
28 errImportFailed = errors.New("import failed")
32 fmt.Fprintln(os.Stderr, "usage: godex [flags] {path|qualifiedIdent}")
37 func report(msg string) {
38 fmt.Fprintln(os.Stderr, "error: "+msg)
47 report("no package name, path, or file provided")
50 var imp types.Importer = new(tryImporters)
54 report("source (-s argument) must be one of: " + strings.Join(sources, ", "))
58 for _, arg := range flag.Args() {
59 path, name := splitPathIdent(arg)
60 logf("\tprocessing %q: path = %q, name = %s\n", arg, path, name)
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))
68 pkg, err := tryPrefixes(prefixes, path, imp)
70 logf("\t=> ignoring %q: %s\n", path, err)
74 // filter objects if needed
75 var filter func(types.Object) bool
77 filter = func(obj types.Object) bool {
78 // TODO(gri) perhaps use regular expression matching here?
79 return obj.Name() == name
84 print(os.Stdout, pkg, filter)
88 func logf(format string, args ...interface{}) {
90 fmt.Fprintf(os.Stderr, format, args...)
94 // splitPathIdent splits a path.name argument into its components.
95 // All but the last path element may contain dots.
96 func splitPathIdent(arg string) (path, name string) {
97 if i := strings.LastIndex(arg, "."); i >= 0 {
98 if j := strings.LastIndex(arg, "/"); j < i {
99 // '.' is not part of path
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
112 func tryPrefixes(prefixes chan string, path string, imp types.Importer) (pkg *types.Package, err error) {
113 for prefix := range prefixes {
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")
121 actual = filepath.Join(prefix, path)
122 logf("\ttrying prefix %q\n", prefix)
124 pkg, err = imp.Import(actual)
128 logf("\t=> importing %q failed: %s\n", actual, err)
133 // tryImporters is an importer that tries all registered importers
134 // successively until one of them succeeds or all of them failed.
135 type tryImporters struct{}
137 func (t *tryImporters) Import(path string) (pkg *types.Package, err error) {
138 for i, imp := range importers {
139 logf("\t\ttrying %s import\n", sources[i])
140 pkg, err = imp.Import(path)
144 logf("\t\t=> %s import failed: %s\n", sources[i], err)
149 type protector struct {
153 func (p *protector) Import(path string) (pkg *types.Package, err error) {
155 if recover() != nil {
157 err = errImportFailed
160 return p.imp.Import(path)
163 // protect protects an importer imp from panics and returns the protected importer.
164 func protect(imp types.Importer) types.Importer {
165 return &protector{imp}
168 // register registers an importer imp for a given source src.
169 func register(src string, imp types.Importer) {
170 if lookup(src) != nil {
171 panic(src + " importer already registered")
173 sources = append(sources, src)
174 importers = append(importers, protect(imp))
177 // lookup returns the importer imp for a given source src.
178 func lookup(src string) types.Importer {
179 for i, s := range sources {
187 func genPrefixes(out chan string, all bool) {
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)
199 func walkDir(dirname, prefix string, out chan string) {
200 fiList, err := ioutil.ReadDir(dirname)
204 for _, fi := range fiList {
205 if fi.IsDir() && !strings.HasPrefix(fi.Name(), ".") {
206 prefix := filepath.Join(prefix, fi.Name())
208 walkDir(filepath.Join(dirname, fi.Name()), prefix, out)