-
Notifications
You must be signed in to change notification settings - Fork 23
/
mod.go
152 lines (142 loc) · 4.46 KB
/
mod.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package main
import (
"io"
"os"
"path/filepath"
"strings"
"github.com/rogpeppe/go-internal/dirhash"
"golang.org/x/tools/go/vcs"
"gopkg.in/errgo.v2/fmt/errors"
)
// hashDir is like dirhash.HashDir except that it ignores the
// gohack hash file in the top level directory, and auto-generated
// go.mod files.
func hashDir(dir string, modulePath string) (string, error) {
files, err := dirhash.DirFiles(dir, "")
if err != nil {
return "", err
}
j := 0
for _, f := range files {
if f == hashFile {
continue
} else if f == "go.mod" {
ok, err := isAutoGoMod(filepath.Join(dir, f), modulePath)
if err != nil {
return "", errors.Wrap(err)
}
if ok {
continue
}
}
files[j] = f
j++
}
files = files[:j]
return dirhash.Hash1(files, func(name string) (io.ReadCloser, error) {
return os.Open(filepath.Join(dir, name))
})
}
type moduleVCSInfo struct {
// module holds the module information as printed by go list.
module *listModule
// alreadyExists holds whether the replacement directory already exists.
alreadyExists bool
// dir holds the absolute path to the replacement directory.
dir string
// replDir holds the path to use for the module in the go.mod replace directive.
replDir string
// root holds information on the VCS root of the module.
root *vcs.RepoRoot
// vcs holds the implementation of the VCS used by the module.
vcs VCS
// VCSInfo holds information on the VCS tree in the replacement
// directory. It is only filled in when alreadyExists is true.
VCSInfo
}
// getVCSInfoForModule returns VCS information about the module
// by inspecting the module path and the module's checked out
// directory.
func getVCSInfoForModule(m *listModule) (*moduleVCSInfo, error) {
// TODO if module directory already exists, could look in it to see if there's
// a single VCS directory and use that if so, to avoid hitting the network
// for vanity imports.
root, err := vcs.RepoRootForImportPath(m.Path, *printCommands)
if err != nil {
return nil, errors.Note(err, nil, "cannot find module root")
}
v, ok := kindToVCS[root.VCS.Cmd]
if !ok {
return nil, errors.Newf("unknown VCS kind %q", root.VCS.Cmd)
}
dir, replDir, err := moduleDir(m.Path)
if err != nil {
return nil, errors.Notef(err, nil, "failed to determine target directory for %v", m.Path)
}
dirInfo, err := os.Stat(dir)
if err != nil && !os.IsNotExist(err) {
return nil, errors.Wrap(err)
}
if err == nil && !dirInfo.IsDir() {
return nil, errors.Newf("%q is not a directory", dir)
}
info := &moduleVCSInfo{
module: m,
root: root,
alreadyExists: err == nil,
dir: dir,
replDir: replDir,
vcs: v,
}
if !info.alreadyExists {
return info, nil
}
// Remove the go.mod file if it was autogenerated so that the
// normal VCS cleanliness detection works OK.
removedGoMod, err := removeAutoGoMod(info)
if err != nil {
return nil, errors.Wrap(err)
}
info.VCSInfo, err = info.vcs.Info(dir)
if err != nil {
return nil, errors.Notef(err, nil, "cannot get VCS info from %q", dir)
}
if removedGoMod {
// We removed the autogenerated go.mod file so add it back again.
if err := ensureGoModFile(info.module.Path, info.dir); err != nil {
return nil, errors.Wrap(err)
}
}
return info, nil
}
// moduleDir returns the path to the directory to be used for storing the
// module with the given path, as well as the filepath to be used in a replace
// directive. If $GOHACK is set then it will be used. A relative $GOHACK will
// be interpreted relative to main module directory.
func moduleDir(module string) (path string, replPath string, err error) {
modfp := filepath.FromSlash(module)
d := os.Getenv("GOHACK")
if d == "" {
uhd, err := UserHomeDir()
if err != nil {
return "", "", errors.Notef(err, nil, "failed to determine user home dir")
}
path = filepath.Join(uhd, "gohack", modfp)
return path, path, nil
}
if filepath.IsAbs(d) {
path = filepath.Join(d, modfp)
return path, path, nil
}
replPath = filepath.Join(d, modfp)
if !strings.HasPrefix(replPath, ".."+string(os.PathSeparator)) {
// We know replPath is relative, but filepath.Join strips any leading
// "./" prefix, and we need that in the replace directive because
// otherwise the path will be treated as a module path rather than a
// relative file path, so add it back.
replPath = "." + string(os.PathSeparator) + replPath
}
mainModDir := filepath.Dir(mainModFile.Syntax.Name)
path = filepath.Join(mainModDir, replPath)
return path, replPath, err
}