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