1
2
3
4
5 package packagestest
6
7 import (
8 "bytes"
9 "context"
10 "fmt"
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/proxydir"
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 var Modules = modules{}
45
46 type modules struct{}
47
48 type moduleAtVersion struct {
49 module string
50 version string
51 }
52
53 func (modules) Name() string {
54 return "Modules"
55 }
56
57 func (modules) Filename(exported *Exported, module, fragment string) string {
58 if module == exported.primary {
59 return filepath.Join(primaryDir(exported), fragment)
60 }
61 return filepath.Join(moduleDir(exported, module), fragment)
62 }
63
64 func (modules) Finalize(exported *Exported) error {
65
66
67
68 primaryDir := primaryDir(exported)
69 if err := os.MkdirAll(primaryDir, 0755); err != nil {
70 return err
71 }
72 exported.Config.Dir = primaryDir
73 if exported.written[exported.primary] == nil {
74 exported.written[exported.primary] = make(map[string]string)
75 }
76
77
78
79 versions := make(map[string]moduleAtVersion)
80 for module := range exported.written {
81 if splt := strings.Split(module, "@"); len(splt) > 1 {
82 versions[module] = moduleAtVersion{
83 module: splt[0],
84 version: splt[1],
85 }
86 }
87 }
88
89
90
91 if gomod := exported.written[exported.primary]["go.mod"]; gomod != "" {
92 contents, err := os.ReadFile(gomod)
93 if err != nil {
94 return err
95 }
96 if err := os.WriteFile(gomod+".temp", contents, 0644); err != nil {
97 return err
98 }
99 }
100
101 exported.written[exported.primary]["go.mod"] = filepath.Join(primaryDir, "go.mod")
102 var primaryGomod bytes.Buffer
103 fmt.Fprintf(&primaryGomod, "module %s\nrequire (\n", exported.primary)
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 fmt.Fprintf(&primaryGomod, "\t%v %v\n", other, version)
116 }
117 fmt.Fprintf(&primaryGomod, ")\n")
118 if err := os.WriteFile(filepath.Join(primaryDir, "go.mod"), primaryGomod.Bytes(), 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 := os.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
176
177
178 inv := gocommand.Invocation{
179 Verb: "mod",
180 Args: []string{"download", "all"},
181 Env: exported.Config.Env,
182 BuildFlags: exported.Config.BuildFlags,
183 WorkingDir: exported.Config.Dir,
184 }
185 _, err := new(gocommand.Runner).Run(context.Background(), inv)
186 return err
187 }
188
189 func writeModuleFiles(rootDir, module, ver string, filePaths map[string]string) error {
190 fileData := make(map[string][]byte)
191 for name, path := range filePaths {
192 contents, err := os.ReadFile(path)
193 if err != nil {
194 return err
195 }
196 fileData[name] = contents
197 }
198 return proxydir.WriteModuleVersion(rootDir, module, ver, fileData)
199 }
200
201 func modCache(exported *Exported) string {
202 return filepath.Join(exported.temp, "modcache/pkg/mod")
203 }
204
205 func primaryDir(exported *Exported) string {
206 return filepath.Join(exported.temp, path.Base(exported.primary))
207 }
208
209 func moduleDir(exported *Exported, module string) string {
210 if strings.Contains(module, "@") {
211 return filepath.Join(modCache(exported), module)
212 }
213 return filepath.Join(modCache(exported), path.Dir(module), path.Base(module)+"@"+moduleVersion(module))
214 }
215
216 var versionSuffixRE = regexp.MustCompile(`v\d+`)
217
218 func moduleVersion(module string) string {
219 if versionSuffixRE.MatchString(path.Base(module)) {
220 return path.Base(module) + ".0.0"
221 }
222 return "v1.0.0"
223 }
224
View as plain text