summaryrefslogtreecommitdiff
path: root/vendor/golang.org/x/tools/internal/modindex/index.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/tools/internal/modindex/index.go')
-rw-r--r--vendor/golang.org/x/tools/internal/modindex/index.go233
1 files changed, 127 insertions, 106 deletions
diff --git a/vendor/golang.org/x/tools/internal/modindex/index.go b/vendor/golang.org/x/tools/internal/modindex/index.go
index 9665356c0..c41d1dd90 100644
--- a/vendor/golang.org/x/tools/internal/modindex/index.go
+++ b/vendor/golang.org/x/tools/internal/modindex/index.go
@@ -6,12 +6,10 @@ package modindex
import (
"bufio"
+ "crypto/sha256"
"encoding/csv"
- "errors"
"fmt"
- "hash/crc64"
"io"
- "io/fs"
"log"
"os"
"path/filepath"
@@ -22,7 +20,7 @@ import (
)
/*
-The on-disk index is a text file.
+The on-disk index ("payload") is a text file.
The first 3 lines are header information containing CurrentVersion,
the value of GOMODCACHE, and the validity date of the index.
(This is when the code started building the index.)
@@ -68,34 +66,45 @@ whose types are []byte and interface{}.
// CurrentVersion tells readers about the format of the index.
const CurrentVersion int = 0
-// Index is returned by ReadIndex().
+// Index is returned by [Read].
type Index struct {
- Version int
- Cachedir Abspath // The directory containing the module cache
- Changed time.Time // The index is up to date as of Changed
- Entries []Entry
+ Version int
+ GOMODCACHE string // absolute path of Go module cache dir
+ ValidAt time.Time // moment at which the index was up to date
+ Entries []Entry
+}
+
+func (ix *Index) String() string {
+ return fmt.Sprintf("Index(%s v%d has %d entries at %v)",
+ ix.GOMODCACHE, ix.Version, len(ix.Entries), ix.ValidAt)
}
// An Entry contains information for an import path.
type Entry struct {
- Dir Relpath // directory in modcache
+ Dir string // package directory relative to GOMODCACHE; uses OS path separator
ImportPath string
PkgName string
Version string
- //ModTime STime // is this useful?
- Names []string // exported names and information
+ Names []string // exported names and information
}
// IndexDir is where the module index is stored.
-var IndexDir string
-
-// Set IndexDir
-func init() {
+// Each logical index entry consists of a pair of files:
+//
+// - the "payload" (index-VERSION-XXX), whose name is
+// randomized, holds the actual index; and
+// - the "link" (index-name-VERSION-HASH),
+// whose name is predictable, contains the
+// name of the payload file.
+//
+// Since the link file is small (<512B),
+// reads and writes to it may be assumed atomic.
+var IndexDir string = func() string {
var dir string
- var err error
if testing.Testing() {
dir = os.TempDir()
} else {
+ var err error
dir, err = os.UserCacheDir()
// shouldn't happen, but TempDir is better than
// creating ./go/imports
@@ -103,81 +112,83 @@ func init() {
dir = os.TempDir()
}
}
- dir = filepath.Join(dir, "go", "imports")
- os.MkdirAll(dir, 0777)
- IndexDir = dir
-}
+ dir = filepath.Join(dir, "goimports")
+ if err := os.MkdirAll(dir, 0777); err != nil {
+ log.Printf("failed to create modcache index dir: %v", err)
+ }
+ return dir
+}()
-// ReadIndex reads the latest version of the on-disk index
-// for the cache directory cd.
-// It returns (nil, nil) if there is no index, but returns
-// a non-nil error if the index exists but could not be read.
-func ReadIndex(cachedir string) (*Index, error) {
- cachedir, err := filepath.Abs(cachedir)
+// Read reads the latest version of the on-disk index
+// for the specified Go module cache directory.
+// If there is no index, it returns a nil Index and an fs.ErrNotExist error.
+func Read(gomodcache string) (*Index, error) {
+ gomodcache, err := filepath.Abs(gomodcache)
if err != nil {
return nil, err
}
- cd := Abspath(cachedir)
- dir := IndexDir
- base := indexNameBase(cd)
- iname := filepath.Join(dir, base)
- buf, err := os.ReadFile(iname)
- if err != nil {
- if errors.Is(err, fs.ErrNotExist) {
- return nil, nil
- }
- return nil, fmt.Errorf("cannot read %s: %w", iname, err)
- }
- fname := filepath.Join(dir, string(buf))
- fd, err := os.Open(fname)
+
+ // Read the "link" file for the specified gomodcache directory.
+ // It names the payload file.
+ content, err := os.ReadFile(filepath.Join(IndexDir, linkFileBasename(gomodcache)))
if err != nil {
return nil, err
}
- defer fd.Close()
- r := bufio.NewReader(fd)
- ix, err := readIndexFrom(cd, r)
+ payloadFile := filepath.Join(IndexDir, string(content))
+
+ // Read the index out of the payload file.
+ f, err := os.Open(payloadFile)
if err != nil {
return nil, err
}
- return ix, nil
+ defer f.Close()
+ return readIndexFrom(gomodcache, bufio.NewReader(f))
}
-func readIndexFrom(cd Abspath, bx io.Reader) (*Index, error) {
- b := bufio.NewScanner(bx)
- var ans Index
- // header
- ok := b.Scan()
- if !ok {
- return nil, fmt.Errorf("unexpected scan error")
+func readIndexFrom(gomodcache string, r io.Reader) (*Index, error) {
+ scan := bufio.NewScanner(r)
+
+ // version
+ if !scan.Scan() {
+ return nil, fmt.Errorf("unexpected scan error: %v", scan.Err())
}
- l := b.Text()
- var err error
- ans.Version, err = strconv.Atoi(l)
+ version, err := strconv.Atoi(scan.Text())
if err != nil {
return nil, err
}
- if ans.Version != CurrentVersion {
- return nil, fmt.Errorf("got version %d, expected %d", ans.Version, CurrentVersion)
+ if version != CurrentVersion {
+ return nil, fmt.Errorf("got version %d, expected %d", version, CurrentVersion)
}
- if ok := b.Scan(); !ok {
- return nil, fmt.Errorf("scanner error reading cachedir")
- }
- ans.Cachedir = Abspath(b.Text())
- if ok := b.Scan(); !ok {
- return nil, fmt.Errorf("scanner error reading index creation time")
+
+ // gomodcache
+ if !scan.Scan() {
+ return nil, fmt.Errorf("scanner error reading module cache dir: %v", scan.Err())
}
- // TODO(pjw): need to check that this is the expected cachedir
+ // TODO(pjw): need to check that this is the expected cache dir
// so the tag should be passed in to this function
- ans.Changed, err = time.ParseInLocation(time.DateTime, b.Text(), time.Local)
+ if dir := string(scan.Text()); dir != gomodcache {
+ return nil, fmt.Errorf("index file GOMODCACHE mismatch: got %q, want %q", dir, gomodcache)
+ }
+
+ // changed
+ if !scan.Scan() {
+ return nil, fmt.Errorf("scanner error reading index creation time: %v", scan.Err())
+ }
+ changed, err := time.ParseInLocation(time.DateTime, scan.Text(), time.Local)
if err != nil {
return nil, err
}
- var curEntry *Entry
- for b.Scan() {
- v := b.Text()
+
+ // entries
+ var (
+ curEntry *Entry
+ entries []Entry
+ )
+ for scan.Scan() {
+ v := scan.Text()
if v[0] == ':' {
if curEntry != nil {
- ans.Entries = append(ans.Entries, *curEntry)
+ entries = append(entries, *curEntry)
}
// as directories may contain commas and quotes, they need to be read as csv.
rdr := strings.NewReader(v[1:])
@@ -189,49 +200,56 @@ func readIndexFrom(cd Abspath, bx io.Reader) (*Index, error) {
if len(flds) != 4 {
return nil, fmt.Errorf("header contains %d fields, not 4: %q", len(v), v)
}
- curEntry = &Entry{PkgName: flds[0], ImportPath: flds[1], Dir: toRelpath(cd, flds[2]), Version: flds[3]}
+ curEntry = &Entry{
+ PkgName: flds[0],
+ ImportPath: flds[1],
+ Dir: relative(gomodcache, flds[2]),
+ Version: flds[3],
+ }
continue
}
curEntry.Names = append(curEntry.Names, v)
}
- if curEntry != nil {
- ans.Entries = append(ans.Entries, *curEntry)
+ if err := scan.Err(); err != nil {
+ return nil, fmt.Errorf("scanner failed while reading modindex entry: %v", err)
}
- if err := b.Err(); err != nil {
- return nil, fmt.Errorf("scanner failed %v", err)
+ if curEntry != nil {
+ entries = append(entries, *curEntry)
}
- return &ans, nil
+
+ return &Index{
+ Version: version,
+ GOMODCACHE: gomodcache,
+ ValidAt: changed,
+ Entries: entries,
+ }, nil
}
-// write the index as a text file
-func writeIndex(cachedir Abspath, ix *Index) error {
- ipat := fmt.Sprintf("index-%d-*", CurrentVersion)
- fd, err := os.CreateTemp(IndexDir, ipat)
+// write writes the index file and updates the index directory to refer to it.
+func write(gomodcache string, ix *Index) error {
+ // Write the index into a payload file with a fresh name.
+ f, err := os.CreateTemp(IndexDir, fmt.Sprintf("index-%d-*", CurrentVersion))
if err != nil {
- return err // can this happen?
+ return err // e.g. disk full, or index dir deleted
}
- defer fd.Close()
- if err := writeIndexToFile(ix, fd); err != nil {
+ if err := writeIndexToFile(ix, bufio.NewWriter(f)); err != nil {
+ _ = f.Close() // ignore error
return err
}
- content := fd.Name()
- content = filepath.Base(content)
- base := indexNameBase(cachedir)
- nm := filepath.Join(IndexDir, base)
- err = os.WriteFile(nm, []byte(content), 0666)
- if err != nil {
+ if err := f.Close(); err != nil {
return err
}
- return nil
+
+ // Write the name of the payload file into a link file.
+ indexDirFile := filepath.Join(IndexDir, linkFileBasename(gomodcache))
+ content := []byte(filepath.Base(f.Name()))
+ return os.WriteFile(indexDirFile, content, 0666)
}
-func writeIndexToFile(x *Index, fd *os.File) error {
- cnt := 0
- w := bufio.NewWriter(fd)
+func writeIndexToFile(x *Index, w *bufio.Writer) error {
fmt.Fprintf(w, "%d\n", x.Version)
- fmt.Fprintf(w, "%s\n", x.Cachedir)
- // round the time down
- tm := x.Changed.Add(-time.Second / 2)
+ fmt.Fprintf(w, "%s\n", x.GOMODCACHE)
+ tm := x.ValidAt.Truncate(time.Second) // round the time down
fmt.Fprintf(w, "%s\n", tm.Format(time.DateTime))
for _, e := range x.Entries {
if e.ImportPath == "" {
@@ -239,7 +257,6 @@ func writeIndexToFile(x *Index, fd *os.File) error {
}
// PJW: maybe always write these headers as csv?
if strings.ContainsAny(string(e.Dir), ",\"") {
- log.Printf("DIR: %s", e.Dir)
cw := csv.NewWriter(w)
cw.Write([]string{":" + e.PkgName, e.ImportPath, string(e.Dir), e.Version})
cw.Flush()
@@ -248,19 +265,23 @@ func writeIndexToFile(x *Index, fd *os.File) error {
}
for _, x := range e.Names {
fmt.Fprintf(w, "%s\n", x)
- cnt++
}
}
- if err := w.Flush(); err != nil {
- return err
- }
- return nil
+ return w.Flush()
}
-// return the base name of the file containing the name of the current index
-func indexNameBase(cachedir Abspath) string {
- // crc64 is a way to convert path names into 16 hex digits.
- h := crc64.Checksum([]byte(cachedir), crc64.MakeTable(crc64.ECMA))
- fname := fmt.Sprintf("index-name-%d-%016x", CurrentVersion, h)
- return fname
+// linkFileBasename returns the base name of the link file in the
+// index directory that holds the name of the payload file for the
+// specified (absolute) Go module cache dir.
+func linkFileBasename(gomodcache string) string {
+ // Note: coupled to logic in ./gomodindex/cmd.go. TODO: factor.
+ h := sha256.Sum256([]byte(gomodcache)) // collision-resistant hash
+ return fmt.Sprintf("index-name-%d-%032x", CurrentVersion, h)
+}
+
+func relative(base, file string) string {
+ if rel, err := filepath.Rel(base, file); err == nil {
+ return rel
+ }
+ return file
}