1
2
3
4
5 package packagestest
6
7 import (
8 "context"
9 "fmt"
10 "io/ioutil"
11 "os"
12 "path"
13 "path/filepath"
14 "regexp"
15 "strings"
16
17 "golang.org/x/tools/internal/gocommand"
18 "golang.org/x/tools/internal/packagesinternal"
19 "golang.org/x/tools/internal/proxydir"
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 var Modules = modules{}
46
47 type modules struct{}
48
49 type moduleAtVersion struct {
50 module string
51 version string
52 }
53
54 func (modules) Name() string {
55 return "Modules"
56 }
57
58 func (modules) Filename(exported *Exported, module, fragment string) string {
59 if module == exported.primary {
60 return filepath.Join(primaryDir(exported), fragment)
61 }
62 return filepath.Join(moduleDir(exported, module), fragment)
63 }
64
65 func (modules) Finalize(exported *Exported) error {
66
67
68
69 primaryDir := primaryDir(exported)
70 if err := os.MkdirAll(primaryDir, 0755); err != nil {
71 return err
72 }
73 exported.Config.Dir = primaryDir
74 if exported.written[exported.primary] == nil {
75 exported.written[exported.primary] = make(map[string]string)
76 }
77
78
79
80 versions := make(map[string]moduleAtVersion)
81 for module := range exported.written {
82 if splt := strings.Split(module, "@"); len(splt) > 1 {
83 versions[module] = moduleAtVersion{
84 module: splt[0],
85 version: splt[1],
86 }
87 }
88 }
89
90
91
92 if gomod := exported.written[exported.primary]["go.mod"]; gomod != "" {
93 contents, err := ioutil.ReadFile(gomod)
94 if err != nil {
95 return err
96 }
97 if err := ioutil.WriteFile(gomod+".temp", contents, 0644); err != nil {
98 return err
99 }
100 }
101
102 exported.written[exported.primary]["go.mod"] = filepath.Join(primaryDir, "go.mod")
103 primaryGomod := "module " + exported.primary + "\nrequire (\n"
104 for other := range exported.written {
105 if other == exported.primary {
106 continue
107 }
108 version := moduleVersion(other)
109
110
111 if v, ok := versions[other]; ok {
112 other = v.module
113 version = v.version
114 }
115 primaryGomod += fmt.Sprintf("\t%v %v\n", other, version)
116 }
117 primaryGomod += ")\n"
118 if err := ioutil.WriteFile(filepath.Join(primaryDir, "go.mod"), []byte(primaryGomod), 0644); err != nil {
119 return err
120 }
121
122
123 if err := os.MkdirAll(modCache(exported), 0755); err != nil {
124 return err
125 }
126
127
128 for module, files := range exported.written {
129 if module == exported.primary {
130 continue
131 }
132 dir := moduleDir(exported, module)
133 modfile := filepath.Join(dir, "go.mod")
134
135
136 if v, ok := versions[module]; ok {
137 module = v.module
138 }
139 if err := ioutil.WriteFile(modfile, []byte("module "+module+"\n"), 0644); err != nil {
140 return err
141 }
142 files["go.mod"] = modfile
143 }
144
145
146 modProxyDir := filepath.Join(exported.temp, "modproxy")
147 for module, files := range exported.written {
148 if module == exported.primary {
149 continue
150 }
151 version := moduleVersion(module)
152
153
154 if v, ok := versions[module]; ok {
155 module = v.module
156 version = v.version
157 }
158 if err := writeModuleFiles(modProxyDir, module, version, files); err != nil {
159 return fmt.Errorf("creating module proxy dir for %v: %v", module, err)
160 }
161 }
162
163
164
165 if err := os.Rename(modCache(exported), modCache(exported)+".orig"); err != nil {
166 return err
167 }
168 exported.Config.Env = append(exported.Config.Env,
169 "GO111MODULE=on",
170 "GOPATH="+filepath.Join(exported.temp, "modcache"),
171 "GOMODCACHE=",
172 "GOPROXY="+proxydir.ToURL(modProxyDir),
173 "GOSUMDB=off",
174 )
175 gocmdRunner := &gocommand.Runner{}
176 packagesinternal.SetGoCmdRunner(exported.Config, gocmdRunner)
177
178
179
180 inv := gocommand.Invocation{
181 Verb: "mod",
182 Args: []string{"download", "all"},
183 Env: exported.Config.Env,
184 BuildFlags: exported.Config.BuildFlags,
185 WorkingDir: exported.Config.Dir,
186 }
187 if _, err := gocmdRunner.Run(context.Background(), inv); err != nil {
188 return err
189 }
190 return nil
191 }
192
193 func writeModuleFiles(rootDir, module, ver string, filePaths map[string]string) error {
194 fileData := make(map[string][]byte)
195 for name, path := range filePaths {
196 contents, err := ioutil.ReadFile(path)
197 if err != nil {
198 return err
199 }
200 fileData[name] = contents
201 }
202 return proxydir.WriteModuleVersion(rootDir, module, ver, fileData)
203 }
204
205 func modCache(exported *Exported) string {
206 return filepath.Join(exported.temp, "modcache/pkg/mod")
207 }
208
209 func primaryDir(exported *Exported) string {
210 return filepath.Join(exported.temp, path.Base(exported.primary))
211 }
212
213 func moduleDir(exported *Exported, module string) string {
214 if strings.Contains(module, "@") {
215 return filepath.Join(modCache(exported), module)
216 }
217 return filepath.Join(modCache(exported), path.Dir(module), path.Base(module)+"@"+moduleVersion(module))
218 }
219
220 var versionSuffixRE = regexp.MustCompile(`v\d+`)
221
222 func moduleVersion(module string) string {
223 if versionSuffixRE.MatchString(path.Base(module)) {
224 return path.Base(module) + ".0.0"
225 }
226 return "v1.0.0"
227 }
228
View as plain text