1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
package main
import (
"fmt"
"go/build"
"io"
"os/exec"
"strings"
"regexp"
"path/filepath"
. "github.com/dave/jennifer/jen"
)
func hints(w io.Writer, pkg, name, goListPath, filter string, standard, novendor bool) error {
// notest
file := NewFile(pkg)
file.HeaderComment("This file is generated - do not edit.")
file.Line()
packages, err := getPackages(goListPath, filter, standard, novendor)
if err != nil {
return err
}
/*
// <name> contains package name hints
var <name> = map[string]string{
...
}
*/
file.Commentf("%s contains package name hints", name)
file.Var().Id(name).Op("=").Map(String()).String().Values(DictFunc(func(d Dict) {
for path, name := range packages {
d[Lit(path)] = Lit(name)
}
}))
return file.Render(w)
}
func getPackages(goListPath, filter string, standard, novendor bool) (map[string]string, error) {
// notest
r, err := regexp.Compile(filter)
if err != nil {
return nil, err
}
cmd := exec.Command("go", "list", "-e", "-f", "{{ .Standard }} {{ .ImportPath }} {{ .Name }}", goListPath)
cmd.Env = []string{
fmt.Sprintf("GOPATH=%s", build.Default.GOPATH),
fmt.Sprintf("GOROOT=%s", build.Default.GOROOT),
}
if standard {
cmd.Dir = filepath.Join(build.Default.GOROOT, "src")
} else {
cmd.Dir = filepath.Join(build.Default.GOPATH, "src")
}
b, err := cmd.Output()
if err != nil {
if x, ok := err.(*exec.ExitError); ok {
return nil, fmt.Errorf("go list command returned an error - %s: %s", err.Error(), string(x.Stderr))
}
return nil, fmt.Errorf("go list command returned an error: %s", err.Error())
}
all := strings.Split(strings.TrimSpace(string(b)), "\n")
packages := map[string]string{}
for _, j := range all {
parts := strings.Split(j, " ")
isStandard := parts[0] == "true"
if isStandard != standard {
continue
}
path := parts[1]
name := parts[2]
if novendor && hasVendor(path) {
continue
}
if name == "main" {
continue
}
if !r.MatchString(path) {
continue
}
path = unvendorPath(path)
if packages[path] != "" {
continue
}
packages[path] = name
}
return packages, nil
}
func unvendorPath(path string) string {
// notest
i, ok := findVendor(path)
if !ok {
return path
}
return path[i+len("vendor/"):]
}
// FindVendor looks for the last non-terminating "vendor" path element in the given import path.
// If there isn't one, FindVendor returns ok=false.
// Otherwise, FindVendor returns ok=true and the index of the "vendor".
// Copied from cmd/go/internal/load
func findVendor(path string) (index int, ok bool) {
// notest
// Two cases, depending on internal at start of string or not.
// The order matters: we must return the index of the final element,
// because the final one is where the effective import path starts.
switch {
case strings.Contains(path, "/vendor/"):
return strings.LastIndex(path, "/vendor/") + 1, true
case strings.HasPrefix(path, "vendor/"):
return 0, true
}
return 0, false
}
func hasVendor(path string) bool {
// notest
_, v := findVendor(path)
return v
}
|