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  }