]> git.ipfire.org Git - thirdparty/gcc.git/blame - libgo/go/cmd/go/internal/modfetch/cache.go
libgo: update to final 1.14.2 release
[thirdparty/gcc.git] / libgo / go / cmd / go / internal / modfetch / cache.go
CommitLineData
dd931d9b
ILT
1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package modfetch
6
7import (
8 "bytes"
9 "encoding/json"
d79a22ed 10 "errors"
dd931d9b 11 "fmt"
4f4a855d 12 "io"
dd931d9b
ILT
13 "io/ioutil"
14 "os"
15 "path/filepath"
16 "strings"
17
18 "cmd/go/internal/base"
aa8901e9 19 "cmd/go/internal/cfg"
4f4a855d 20 "cmd/go/internal/lockedfile"
dd931d9b 21 "cmd/go/internal/modfetch/codehost"
dd931d9b 22 "cmd/go/internal/par"
4f4a855d 23 "cmd/go/internal/renameio"
dd931d9b 24
5a8ea165
ILT
25 "golang.org/x/mod/module"
26 "golang.org/x/mod/semver"
27)
dd931d9b
ILT
28
29var PkgMod string // $GOPATH/pkg/mod; set by package modload
30
31func cacheDir(path string) (string, error) {
32 if PkgMod == "" {
33 return "", fmt.Errorf("internal error: modfetch.PkgMod not set")
34 }
5a8ea165 35 enc, err := module.EscapePath(path)
dd931d9b
ILT
36 if err != nil {
37 return "", err
38 }
39 return filepath.Join(PkgMod, "cache/download", enc, "/@v"), nil
40}
41
42func CachePath(m module.Version, suffix string) (string, error) {
43 dir, err := cacheDir(m.Path)
44 if err != nil {
45 return "", err
46 }
47 if !semver.IsValid(m.Version) {
48 return "", fmt.Errorf("non-semver module version %q", m.Version)
49 }
50 if module.CanonicalVersion(m.Version) != m.Version {
51 return "", fmt.Errorf("non-canonical module version %q", m.Version)
52 }
5a8ea165 53 encVer, err := module.EscapeVersion(m.Version)
dd931d9b
ILT
54 if err != nil {
55 return "", err
56 }
57 return filepath.Join(dir, encVer+"."+suffix), nil
58}
59
d79a22ed
ILT
60// DownloadDir returns the directory to which m should have been downloaded.
61// An error will be returned if the module path or version cannot be escaped.
62// An error satisfying errors.Is(err, os.ErrNotExist) will be returned
63// along with the directory if the directory does not exist or if the directory
64// is not completely populated.
dd931d9b
ILT
65func DownloadDir(m module.Version) (string, error) {
66 if PkgMod == "" {
67 return "", fmt.Errorf("internal error: modfetch.PkgMod not set")
68 }
5a8ea165 69 enc, err := module.EscapePath(m.Path)
dd931d9b
ILT
70 if err != nil {
71 return "", err
72 }
73 if !semver.IsValid(m.Version) {
74 return "", fmt.Errorf("non-semver module version %q", m.Version)
75 }
76 if module.CanonicalVersion(m.Version) != m.Version {
77 return "", fmt.Errorf("non-canonical module version %q", m.Version)
78 }
5a8ea165 79 encVer, err := module.EscapeVersion(m.Version)
dd931d9b
ILT
80 if err != nil {
81 return "", err
82 }
d79a22ed
ILT
83
84 dir := filepath.Join(PkgMod, enc+"@"+encVer)
85 if fi, err := os.Stat(dir); os.IsNotExist(err) {
86 return dir, err
87 } else if err != nil {
88 return dir, &DownloadDirPartialError{dir, err}
89 } else if !fi.IsDir() {
90 return dir, &DownloadDirPartialError{dir, errors.New("not a directory")}
91 }
92 partialPath, err := CachePath(m, "partial")
93 if err != nil {
94 return dir, err
95 }
96 if _, err := os.Stat(partialPath); err == nil {
97 return dir, &DownloadDirPartialError{dir, errors.New("not completely extracted")}
98 } else if !os.IsNotExist(err) {
99 return dir, err
100 }
101 return dir, nil
102}
103
104// DownloadDirPartialError is returned by DownloadDir if a module directory
105// exists but was not completely populated.
106//
107// DownloadDirPartialError is equivalent to os.ErrNotExist.
108type DownloadDirPartialError struct {
109 Dir string
110 Err error
dd931d9b
ILT
111}
112
d79a22ed
ILT
113func (e *DownloadDirPartialError) Error() string { return fmt.Sprintf("%s: %v", e.Dir, e.Err) }
114func (e *DownloadDirPartialError) Is(err error) bool { return err == os.ErrNotExist }
115
4f4a855d
ILT
116// lockVersion locks a file within the module cache that guards the downloading
117// and extraction of the zipfile for the given module version.
118func lockVersion(mod module.Version) (unlock func(), err error) {
119 path, err := CachePath(mod, "lock")
120 if err != nil {
121 return nil, err
122 }
123 if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
124 return nil, err
125 }
126 return lockedfile.MutexAt(path).Lock()
127}
128
5a8ea165
ILT
129// SideLock locks a file within the module cache that that previously guarded
130// edits to files outside the cache, such as go.sum and go.mod files in the
131// user's working directory.
132// If err is nil, the caller MUST eventually call the unlock function.
133func SideLock() (unlock func(), err error) {
4f4a855d
ILT
134 if PkgMod == "" {
135 base.Fatalf("go: internal error: modfetch.PkgMod not set")
136 }
5a8ea165 137
4f4a855d
ILT
138 path := filepath.Join(PkgMod, "cache", "lock")
139 if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
5a8ea165 140 return nil, fmt.Errorf("failed to create cache directory: %w", err)
4f4a855d 141 }
5a8ea165
ILT
142
143 return lockedfile.MutexAt(path).Lock()
4f4a855d
ILT
144}
145
dd931d9b
ILT
146// A cachingRepo is a cache around an underlying Repo,
147// avoiding redundant calls to ModulePath, Versions, Stat, Latest, and GoMod (but not Zip).
148// It is also safe for simultaneous use by multiple goroutines
149// (so that it can be returned from Lookup multiple times).
150// It serializes calls to the underlying Repo.
151type cachingRepo struct {
152 path string
153 cache par.Cache // cache for all operations
154 r Repo
155}
156
157func newCachingRepo(r Repo) *cachingRepo {
158 return &cachingRepo{
159 r: r,
160 path: r.ModulePath(),
161 }
162}
163
164func (r *cachingRepo) ModulePath() string {
165 return r.path
166}
167
168func (r *cachingRepo) Versions(prefix string) ([]string, error) {
169 type cached struct {
170 list []string
171 err error
172 }
173 c := r.cache.Do("versions:"+prefix, func() interface{} {
174 list, err := r.r.Versions(prefix)
175 return cached{list, err}
176 }).(cached)
177
178 if c.err != nil {
179 return nil, c.err
180 }
181 return append([]string(nil), c.list...), nil
182}
183
184type cachedInfo struct {
185 info *RevInfo
186 err error
187}
188
189func (r *cachingRepo) Stat(rev string) (*RevInfo, error) {
190 c := r.cache.Do("stat:"+rev, func() interface{} {
191 file, info, err := readDiskStat(r.path, rev)
192 if err == nil {
193 return cachedInfo{info, nil}
194 }
195
dd931d9b
ILT
196 info, err = r.r.Stat(rev)
197 if err == nil {
dd931d9b
ILT
198 // If we resolved, say, 1234abcde to v0.0.0-20180604122334-1234abcdef78,
199 // then save the information under the proper version, for future use.
200 if info.Version != rev {
4f4a855d 201 file, _ = CachePath(module.Version{Path: r.path, Version: info.Version}, "info")
dd931d9b
ILT
202 r.cache.Do("stat:"+info.Version, func() interface{} {
203 return cachedInfo{info, err}
204 })
205 }
4f4a855d
ILT
206
207 if err := writeDiskStat(file, info); err != nil {
208 fmt.Fprintf(os.Stderr, "go: writing stat cache: %v\n", err)
209 }
dd931d9b
ILT
210 }
211 return cachedInfo{info, err}
212 }).(cachedInfo)
213
214 if c.err != nil {
215 return nil, c.err
216 }
217 info := *c.info
218 return &info, nil
219}
220
221func (r *cachingRepo) Latest() (*RevInfo, error) {
222 c := r.cache.Do("latest:", func() interface{} {
dd931d9b
ILT
223 info, err := r.r.Latest()
224
225 // Save info for likely future Stat call.
226 if err == nil {
227 r.cache.Do("stat:"+info.Version, func() interface{} {
228 return cachedInfo{info, err}
229 })
230 if file, _, err := readDiskStat(r.path, info.Version); err != nil {
231 writeDiskStat(file, info)
232 }
233 }
234
235 return cachedInfo{info, err}
236 }).(cachedInfo)
237
238 if c.err != nil {
239 return nil, c.err
240 }
241 info := *c.info
242 return &info, nil
243}
244
aa8901e9 245func (r *cachingRepo) GoMod(version string) ([]byte, error) {
dd931d9b
ILT
246 type cached struct {
247 text []byte
248 err error
249 }
aa8901e9
ILT
250 c := r.cache.Do("gomod:"+version, func() interface{} {
251 file, text, err := readDiskGoMod(r.path, version)
dd931d9b
ILT
252 if err == nil {
253 // Note: readDiskGoMod already called checkGoMod.
254 return cached{text, nil}
255 }
256
aa8901e9 257 text, err = r.r.GoMod(version)
dd931d9b 258 if err == nil {
5a8ea165
ILT
259 if err := checkGoMod(r.path, version, text); err != nil {
260 return cached{text, err}
261 }
dd931d9b
ILT
262 if err := writeDiskGoMod(file, text); err != nil {
263 fmt.Fprintf(os.Stderr, "go: writing go.mod cache: %v\n", err)
264 }
265 }
266 return cached{text, err}
267 }).(cached)
268
269 if c.err != nil {
270 return nil, c.err
271 }
272 return append([]byte(nil), c.text...), nil
273}
274
4f4a855d
ILT
275func (r *cachingRepo) Zip(dst io.Writer, version string) error {
276 return r.r.Zip(dst, version)
dd931d9b
ILT
277}
278
279// Stat is like Lookup(path).Stat(rev) but avoids the
280// repository path resolution in Lookup if the result is
281// already cached on local disk.
aa8901e9 282func Stat(proxy, path, rev string) (*RevInfo, error) {
dd931d9b
ILT
283 _, info, err := readDiskStat(path, rev)
284 if err == nil {
285 return info, nil
286 }
aa8901e9 287 repo, err := Lookup(proxy, path)
dd931d9b
ILT
288 if err != nil {
289 return nil, err
290 }
291 return repo.Stat(rev)
292}
293
294// InfoFile is like Stat but returns the name of the file containing
295// the cached information.
296func InfoFile(path, version string) (string, error) {
297 if !semver.IsValid(version) {
298 return "", fmt.Errorf("invalid version %q", version)
299 }
aa8901e9
ILT
300
301 if file, _, err := readDiskStat(path, version); err == nil {
302 return file, nil
303 }
304
305 err := TryProxies(func(proxy string) error {
306 repo, err := Lookup(proxy, path)
307 if err == nil {
308 _, err = repo.Stat(version)
309 }
310 return err
311 })
312 if err != nil {
dd931d9b
ILT
313 return "", err
314 }
aa8901e9 315
dd931d9b
ILT
316 // Stat should have populated the disk cache for us.
317 file, _, err := readDiskStat(path, version)
318 if err != nil {
319 return "", err
320 }
321 return file, nil
322}
323
324// GoMod is like Lookup(path).GoMod(rev) but avoids the
325// repository path resolution in Lookup if the result is
326// already cached on local disk.
327func GoMod(path, rev string) ([]byte, error) {
328 // Convert commit hash to pseudo-version
329 // to increase cache hit rate.
330 if !semver.IsValid(rev) {
aa8901e9
ILT
331 if _, info, err := readDiskStat(path, rev); err == nil {
332 rev = info.Version
333 } else {
334 err := TryProxies(func(proxy string) error {
335 repo, err := Lookup(proxy, path)
336 if err != nil {
337 return err
338 }
339 info, err := repo.Stat(rev)
340 if err == nil {
341 rev = info.Version
342 }
343 return err
344 })
345 if err != nil {
346 return nil, err
347 }
dd931d9b 348 }
dd931d9b 349 }
aa8901e9 350
dd931d9b
ILT
351 _, data, err := readDiskGoMod(path, rev)
352 if err == nil {
353 return data, nil
354 }
aa8901e9
ILT
355
356 err = TryProxies(func(proxy string) error {
357 repo, err := Lookup(proxy, path)
358 if err == nil {
359 data, err = repo.GoMod(rev)
360 }
361 return err
362 })
363 return data, err
dd931d9b
ILT
364}
365
366// GoModFile is like GoMod but returns the name of the file containing
367// the cached information.
368func GoModFile(path, version string) (string, error) {
369 if !semver.IsValid(version) {
370 return "", fmt.Errorf("invalid version %q", version)
371 }
372 if _, err := GoMod(path, version); err != nil {
373 return "", err
374 }
375 // GoMod should have populated the disk cache for us.
376 file, _, err := readDiskGoMod(path, version)
377 if err != nil {
378 return "", err
379 }
380 return file, nil
381}
382
383// GoModSum returns the go.sum entry for the module version's go.mod file.
384// (That is, it returns the entry listed in go.sum as "path version/go.mod".)
385func GoModSum(path, version string) (string, error) {
386 if !semver.IsValid(version) {
387 return "", fmt.Errorf("invalid version %q", version)
388 }
389 data, err := GoMod(path, version)
390 if err != nil {
391 return "", err
392 }
393 sum, err := goModSum(data)
394 if err != nil {
395 return "", err
396 }
397 return sum, nil
398}
399
400var errNotCached = fmt.Errorf("not in cache")
401
402// readDiskStat reads a cached stat result from disk,
403// returning the name of the cache file and the result.
404// If the read fails, the caller can use
405// writeDiskStat(file, info) to write a new cache entry.
406func readDiskStat(path, rev string) (file string, info *RevInfo, err error) {
407 file, data, err := readDiskCache(path, rev, "info")
408 if err != nil {
aa8901e9
ILT
409 // If the cache already contains a pseudo-version with the given hash, we
410 // would previously return that pseudo-version without checking upstream.
411 // However, that produced an unfortunate side-effect: if the author added a
412 // tag to the repository, 'go get' would not pick up the effect of that new
413 // tag on the existing commits, and 'go' commands that referred to those
414 // commits would use the previous name instead of the new one.
415 //
416 // That's especially problematic if the original pseudo-version starts with
417 // v0.0.0-, as was the case for all pseudo-versions during vgo development,
418 // since a v0.0.0- pseudo-version has lower precedence than pretty much any
419 // tagged version.
420 //
421 // In practice, we're only looking up by hash during initial conversion of a
422 // legacy config and during an explicit 'go get', and a little extra latency
423 // for those operations seems worth the benefit of picking up more accurate
424 // versions.
425 //
426 // Fall back to this resolution scheme only if the GOPROXY setting prohibits
427 // us from resolving upstream tags.
428 if cfg.GOPROXY == "off" {
429 if file, info, err := readDiskStatByHash(path, rev); err == nil {
430 return file, info, nil
431 }
dd931d9b
ILT
432 }
433 return file, nil, err
434 }
435 info = new(RevInfo)
436 if err := json.Unmarshal(data, info); err != nil {
437 return file, nil, errNotCached
438 }
439 // The disk might have stale .info files that have Name and Short fields set.
440 // We want to canonicalize to .info files with those fields omitted.
441 // Remarshal and update the cache file if needed.
442 data2, err := json.Marshal(info)
443 if err == nil && !bytes.Equal(data2, data) {
444 writeDiskCache(file, data)
445 }
446 return file, info, nil
447}
448
449// readDiskStatByHash is a fallback for readDiskStat for the case
450// where rev is a commit hash instead of a proper semantic version.
451// In that case, we look for a cached pseudo-version that matches
452// the commit hash. If we find one, we use it.
453// This matters most for converting legacy package management
454// configs, when we are often looking up commits by full hash.
455// Without this check we'd be doing network I/O to the remote repo
456// just to find out about a commit we already know about
457// (and have cached under its pseudo-version).
458func readDiskStatByHash(path, rev string) (file string, info *RevInfo, err error) {
459 if PkgMod == "" {
460 // Do not download to current directory.
461 return "", nil, errNotCached
462 }
463
464 if !codehost.AllHex(rev) || len(rev) < 12 {
465 return "", nil, errNotCached
466 }
467 rev = rev[:12]
468 cdir, err := cacheDir(path)
469 if err != nil {
470 return "", nil, errNotCached
471 }
472 dir, err := os.Open(cdir)
473 if err != nil {
474 return "", nil, errNotCached
475 }
476 names, err := dir.Readdirnames(-1)
477 dir.Close()
478 if err != nil {
479 return "", nil, errNotCached
480 }
aa8901e9
ILT
481
482 // A given commit hash may map to more than one pseudo-version,
483 // depending on which tags are present on the repository.
484 // Take the highest such version.
485 var maxVersion string
dd931d9b 486 suffix := "-" + rev + ".info"
aa8901e9 487 err = errNotCached
dd931d9b 488 for _, name := range names {
aa8901e9
ILT
489 if strings.HasSuffix(name, suffix) {
490 v := strings.TrimSuffix(name, ".info")
491 if IsPseudoVersion(v) && semver.Max(maxVersion, v) == v {
492 maxVersion = v
493 file, info, err = readDiskStat(path, strings.TrimSuffix(name, ".info"))
494 }
dd931d9b
ILT
495 }
496 }
aa8901e9 497 return file, info, err
dd931d9b
ILT
498}
499
500// oldVgoPrefix is the prefix in the old auto-generated cached go.mod files.
501// We stopped trying to auto-generate the go.mod files. Now we use a trivial
502// go.mod with only a module line, and we've dropped the version prefix
503// entirely. If we see a version prefix, that means we're looking at an old copy
504// and should ignore it.
505var oldVgoPrefix = []byte("//vgo 0.0.")
506
4f4a855d 507// readDiskGoMod reads a cached go.mod file from disk,
dd931d9b
ILT
508// returning the name of the cache file and the result.
509// If the read fails, the caller can use
510// writeDiskGoMod(file, data) to write a new cache entry.
511func readDiskGoMod(path, rev string) (file string, data []byte, err error) {
512 file, data, err = readDiskCache(path, rev, "mod")
513
514 // If the file has an old auto-conversion prefix, pretend it's not there.
515 if bytes.HasPrefix(data, oldVgoPrefix) {
516 err = errNotCached
517 data = nil
518 }
519
520 if err == nil {
5a8ea165
ILT
521 if err := checkGoMod(path, rev, data); err != nil {
522 return "", nil, err
523 }
dd931d9b
ILT
524 }
525
526 return file, data, err
527}
528
529// readDiskCache is the generic "read from a cache file" implementation.
530// It takes the revision and an identifying suffix for the kind of data being cached.
531// It returns the name of the cache file and the content of the file.
532// If the read fails, the caller can use
533// writeDiskCache(file, data) to write a new cache entry.
534func readDiskCache(path, rev, suffix string) (file string, data []byte, err error) {
535 file, err = CachePath(module.Version{Path: path, Version: rev}, suffix)
536 if err != nil {
537 return "", nil, errNotCached
538 }
aa8901e9 539 data, err = renameio.ReadFile(file)
dd931d9b
ILT
540 if err != nil {
541 return file, nil, errNotCached
542 }
543 return file, data, nil
544}
545
546// writeDiskStat writes a stat result cache entry.
547// The file name must have been returned by a previous call to readDiskStat.
548func writeDiskStat(file string, info *RevInfo) error {
549 if file == "" {
550 return nil
551 }
552 js, err := json.Marshal(info)
553 if err != nil {
554 return err
555 }
556 return writeDiskCache(file, js)
557}
558
559// writeDiskGoMod writes a go.mod cache entry.
560// The file name must have been returned by a previous call to readDiskGoMod.
561func writeDiskGoMod(file string, text []byte) error {
562 return writeDiskCache(file, text)
563}
564
565// writeDiskCache is the generic "write to a cache file" implementation.
566// The file must have been returned by a previous call to readDiskCache.
567func writeDiskCache(file string, data []byte) error {
568 if file == "" {
569 return nil
570 }
571 // Make sure directory for file exists.
572 if err := os.MkdirAll(filepath.Dir(file), 0777); err != nil {
573 return err
574 }
4f4a855d 575
aa8901e9 576 if err := renameio.WriteFile(file, data, 0666); err != nil {
dd931d9b
ILT
577 return err
578 }
579
580 if strings.HasSuffix(file, ".mod") {
581 rewriteVersionList(filepath.Dir(file))
582 }
583 return nil
584}
585
586// rewriteVersionList rewrites the version list in dir
587// after a new *.mod file has been written.
588func rewriteVersionList(dir string) {
589 if filepath.Base(dir) != "@v" {
590 base.Fatalf("go: internal error: misuse of rewriteVersionList")
591 }
592
4f4a855d
ILT
593 listFile := filepath.Join(dir, "list")
594
595 // We use a separate lockfile here instead of locking listFile itself because
596 // we want to use Rename to write the file atomically. The list may be read by
597 // a GOPROXY HTTP server, and if we crash midway through a rewrite (or if the
598 // HTTP server ignores our locking and serves the file midway through a
599 // rewrite) it's better to serve a stale list than a truncated one.
600 unlock, err := lockedfile.MutexAt(listFile + ".lock").Lock()
601 if err != nil {
602 base.Fatalf("go: can't lock version list lockfile: %v", err)
603 }
604 defer unlock()
dd931d9b
ILT
605
606 infos, err := ioutil.ReadDir(dir)
607 if err != nil {
608 return
609 }
610 var list []string
611 for _, info := range infos {
612 // We look for *.mod files on the theory that if we can't supply
613 // the .mod file then there's no point in listing that version,
614 // since it's unusable. (We can have *.info without *.mod.)
615 // We don't require *.zip files on the theory that for code only
616 // involved in module graph construction, many *.zip files
617 // will never be requested.
618 name := info.Name()
619 if strings.HasSuffix(name, ".mod") {
620 v := strings.TrimSuffix(name, ".mod")
621 if v != "" && module.CanonicalVersion(v) == v {
622 list = append(list, v)
623 }
624 }
625 }
626 SortVersions(list)
627
628 var buf bytes.Buffer
629 for _, v := range list {
630 buf.WriteString(v)
631 buf.WriteString("\n")
632 }
aa8901e9 633 old, _ := renameio.ReadFile(listFile)
dd931d9b
ILT
634 if bytes.Equal(buf.Bytes(), old) {
635 return
636 }
4f4a855d 637
aa8901e9 638 if err := renameio.WriteFile(listFile, buf.Bytes(), 0666); err != nil {
4f4a855d
ILT
639 base.Fatalf("go: failed to write version list: %v", err)
640 }
dd931d9b 641}