github.com/april1989/origin-go-tools@v0.0.32/cmd/godex/godex.go (about) 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 5 package main 6 7 import ( 8 "errors" 9 "flag" 10 "fmt" 11 "go/build" 12 "go/types" 13 "io/ioutil" 14 "os" 15 "path/filepath" 16 "strings" 17 ) 18 19 var ( 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 25 var ( 26 sources []string 27 importers []types.Importer 28 errImportFailed = errors.New("import failed") 29 ) 30 31 func usage() { 32 fmt.Fprintln(os.Stderr, "usage: godex [flags] {path|qualifiedIdent}") 33 flag.PrintDefaults() 34 os.Exit(2) 35 } 36 37 func report(msg string) { 38 fmt.Fprintln(os.Stderr, "error: "+msg) 39 os.Exit(2) 40 } 41 42 func 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 path, name := splitPathIdent(arg) 60 logf("\tprocessing %q: path = %q, name = %s\n", arg, path, name) 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 pkg, err := tryPrefixes(prefixes, path, imp) 69 if err != nil { 70 logf("\t=> ignoring %q: %s\n", path, err) 71 continue 72 } 73 74 // filter objects if needed 75 var filter func(types.Object) bool 76 if name != "" { 77 filter = func(obj types.Object) bool { 78 // TODO(gri) perhaps use regular expression matching here? 79 return obj.Name() == name 80 } 81 } 82 83 // print contents 84 print(os.Stdout, pkg, filter) 85 } 86 } 87 88 func logf(format string, args ...interface{}) { 89 if *verbose { 90 fmt.Fprintf(os.Stderr, format, args...) 91 } 92 } 93 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 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. 112 func tryPrefixes(prefixes chan string, path string, imp types.Importer) (pkg *types.Package, err 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(prefix, path) 122 logf("\ttrying prefix %q\n", prefix) 123 } 124 pkg, err = imp.Import(actual) 125 if err == nil { 126 break 127 } 128 logf("\t=> importing %q failed: %s\n", actual, err) 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. 135 type tryImporters struct{} 136 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) 141 if err == nil { 142 break 143 } 144 logf("\t\t=> %s import failed: %s\n", sources[i], err) 145 } 146 return 147 } 148 149 type protector struct { 150 imp types.Importer 151 } 152 153 func (p *protector) Import(path string) (pkg *types.Package, err 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. 164 func protect(imp types.Importer) types.Importer { 165 return &protector{imp} 166 } 167 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") 172 } 173 sources = append(sources, src) 174 importers = append(importers, protect(imp)) 175 } 176 177 // lookup returns the importer imp for a given source src. 178 func lookup(src string) types.Importer { 179 for i, s := range sources { 180 if s == src { 181 return importers[i] 182 } 183 } 184 return nil 185 } 186 187 func genPrefixes(out chan string, all 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 199 func walkDir(dirname, prefix string, out chan string) { 200 fiList, err := 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(prefix, fi.Name()) 207 out <- prefix 208 walkDir(filepath.Join(dirname, fi.Name()), prefix, out) 209 } 210 } 211 }