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  }