1
2
3
4
5 package main
6
7 import (
8 "errors"
9 "flag"
10 "fmt"
11 "go/build"
12 "go/types"
13 "os"
14 "path/filepath"
15 "strings"
16 )
17
18 var (
19 source = flag.String("s", "", "only consider packages from src, where src is one of the supported compilers")
20 verbose = flag.Bool("v", false, "verbose mode")
21 )
22
23
24 var (
25 sources []string
26 importers []types.Importer
27 errImportFailed = errors.New("import failed")
28 )
29
30 func usage() {
31 fmt.Fprintln(os.Stderr, "usage: godex [flags] {path|qualifiedIdent}")
32 flag.PrintDefaults()
33 os.Exit(2)
34 }
35
36 func report(msg string) {
37 fmt.Fprintln(os.Stderr, "error: "+msg)
38 os.Exit(2)
39 }
40
41 func main() {
42 flag.Usage = usage
43 flag.Parse()
44
45 if flag.NArg() == 0 {
46 report("no package name, path, or file provided")
47 }
48
49 var imp types.Importer = new(tryImporters)
50 if *source != "" {
51 imp = lookup(*source)
52 if imp == nil {
53 report("source (-s argument) must be one of: " + strings.Join(sources, ", "))
54 }
55 }
56
57 for _, arg := range flag.Args() {
58 path, name := splitPathIdent(arg)
59 logf("\tprocessing %q: path = %q, name = %s\n", arg, path, name)
60
61
62
63 prefixes := make(chan string)
64 go genPrefixes(prefixes, !filepath.IsAbs(path) && !build.IsLocalImport(path))
65
66
67 pkg, err := tryPrefixes(prefixes, path, imp)
68 if err != nil {
69 logf("\t=> ignoring %q: %s\n", path, err)
70 continue
71 }
72
73
74 var filter func(types.Object) bool
75 if name != "" {
76 filter = func(obj types.Object) bool {
77
78 return obj.Name() == name
79 }
80 }
81
82
83 print(os.Stdout, pkg, filter)
84 }
85 }
86
87 func logf(format string, args ...interface{}) {
88 if *verbose {
89 fmt.Fprintf(os.Stderr, format, args...)
90 }
91 }
92
93
94
95 func splitPathIdent(arg string) (path, name string) {
96 if i := strings.LastIndex(arg, "."); i >= 0 {
97 if j := strings.LastIndex(arg, "/"); j < i {
98
99 path = arg[:i]
100 name = arg[i+1:]
101 return
102 }
103 }
104 path = arg
105 return
106 }
107
108
109
110
111 func tryPrefixes(prefixes chan string, path string, imp types.Importer) (pkg *types.Package, err error) {
112 for prefix := range prefixes {
113 actual := path
114 if prefix == "" {
115
116
117
118 logf("\ttrying no prefix\n")
119 } else {
120 actual = filepath.Join(prefix, path)
121 logf("\ttrying prefix %q\n", prefix)
122 }
123 pkg, err = imp.Import(actual)
124 if err == nil {
125 break
126 }
127 logf("\t=> importing %q failed: %s\n", actual, err)
128 }
129 return
130 }
131
132
133
134 type tryImporters struct{}
135
136 func (t *tryImporters) Import(path string) (pkg *types.Package, err error) {
137 for i, imp := range importers {
138 logf("\t\ttrying %s import\n", sources[i])
139 pkg, err = imp.Import(path)
140 if err == nil {
141 break
142 }
143 logf("\t\t=> %s import failed: %s\n", sources[i], err)
144 }
145 return
146 }
147
148 type protector struct {
149 imp types.Importer
150 }
151
152 func (p *protector) Import(path string) (pkg *types.Package, err error) {
153 defer func() {
154 if recover() != nil {
155 pkg = nil
156 err = errImportFailed
157 }
158 }()
159 return p.imp.Import(path)
160 }
161
162
163 func protect(imp types.Importer) types.Importer {
164 return &protector{imp}
165 }
166
167
168 func register(src string, imp types.Importer) {
169 if lookup(src) != nil {
170 panic(src + " importer already registered")
171 }
172 sources = append(sources, src)
173 importers = append(importers, protect(imp))
174 }
175
176
177 func lookup(src string) types.Importer {
178 for i, s := range sources {
179 if s == src {
180 return importers[i]
181 }
182 }
183 return nil
184 }
185
186 func genPrefixes(out chan string, all bool) {
187 out <- ""
188 if all {
189 platform := build.Default.GOOS + "_" + build.Default.GOARCH
190 dirnames := append([]string{build.Default.GOROOT}, filepath.SplitList(build.Default.GOPATH)...)
191 for _, dirname := range dirnames {
192 walkDir(filepath.Join(dirname, "pkg", platform), "", out)
193 }
194 }
195 close(out)
196 }
197
198 func walkDir(dirname, prefix string, out chan string) {
199 fiList, err := os.ReadDir(dirname)
200 if err != nil {
201 return
202 }
203 for _, fi := range fiList {
204 if fi.IsDir() && !strings.HasPrefix(fi.Name(), ".") {
205 prefix := filepath.Join(prefix, fi.Name())
206 out <- prefix
207 walkDir(filepath.Join(dirname, fi.Name()), prefix, out)
208 }
209 }
210 }
211
View as plain text