/
pkgpathprefix.go
132 lines (119 loc) · 2.99 KB
/
pkgpathprefix.go
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
package testjson
import (
"bytes"
"go/build"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
)
// RelativePackagePath attempts to remove a common prefix from a package path.
// The common prefix is determined either by looking at the GOPATH or reading
// the package value from go.mod file.
// If the pkgpath does not match the common prefix it will be returned
// unmodified.
// If the pkgpath matches the common prefix exactly then '.' will be returned.
func RelativePackagePath(pkgpath string) string {
if pkgpath == pkgPathPrefix {
return "."
}
return strings.TrimPrefix(pkgpath, pkgPathPrefix+"/")
}
func getPkgPathPrefix() string {
cwd, _ := os.Getwd()
if isGoModuleEnabled() {
prefix := getPkgPathPrefixFromGoModule(cwd)
if prefix != "" {
return prefix
}
}
return getPkgPathPrefixGoPath(cwd)
}
func isGoModuleEnabled() bool {
version := runtime.Version()
if strings.HasPrefix(version, "go1.10") {
return false
}
// Go modules may not be enabled if env var is unset, or set to auto, however
// we can always fall back to using GOPATH as the prefix if a go.mod is not
// found.
return os.Getenv("GO111MODULE") != "off"
}
// TODO: might not work on windows
func getPkgPathPrefixGoPath(cwd string) string {
gopaths := strings.Split(build.Default.GOPATH, string(filepath.ListSeparator))
for _, gopath := range gopaths {
gosrcpath := gopath + "/src/"
if strings.HasPrefix(cwd, gosrcpath) {
return strings.TrimPrefix(cwd, gosrcpath)
}
}
return ""
}
func getPkgPathPrefixFromGoModule(cwd string) string {
filename := goModuleFilePath(cwd)
if filename == "" {
return ""
}
raw, err := ioutil.ReadFile(filename)
if err != nil {
// TODO: log.Warn
return ""
}
return pkgPathFromGoModuleFile(raw)
}
var (
slashSlash = []byte("//")
moduleStr = []byte("module")
)
// Copy of ModulePath from golang.org/src/cmd/go/internal/modfile/read.go
func pkgPathFromGoModuleFile(mod []byte) string {
for len(mod) > 0 {
line := mod
mod = nil
if i := bytes.IndexByte(line, '\n'); i >= 0 {
line, mod = line[:i], line[i+1:]
}
if i := bytes.Index(line, slashSlash); i >= 0 {
line = line[:i]
}
line = bytes.TrimSpace(line)
if !bytes.HasPrefix(line, moduleStr) {
continue
}
line = line[len(moduleStr):]
n := len(line)
line = bytes.TrimSpace(line)
if len(line) == n || len(line) == 0 {
continue
}
if line[0] == '"' || line[0] == '`' {
p, err := strconv.Unquote(string(line))
if err != nil {
return "" // malformed quoted string or multi-line module path
}
return p
}
return string(line)
}
return "" // missing module path
}
// A rough re-implementation of FindModuleRoot from
// golang.org/src/cmd/go/internal/modload/init.go
func goModuleFilePath(cwd string) string {
dir := filepath.Clean(cwd)
for {
path := filepath.Join(dir, "go.mod")
if _, err := os.Stat(path); err == nil {
return path
}
parent := filepath.Dir(dir)
if parent == dir {
return ""
}
dir = parent
}
}
var pkgPathPrefix = getPkgPathPrefix()