blob: 2cf944e3b018c05f2ada96a22c5301e2435eaeda [file] [log] [blame]
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// A command for building and maintaining the module cache
// a.out <flags> <command> <args>
// The commands are:
// 'update', which attempts to update an existing index,
// 'query', which looks up things in the index.
// 'clean', which remove obsolete index files.
// If the command is invoked with no arguments, it defaults to 'update'.
package main
import (
"bytes"
"flag"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"golang.org/x/tools/internal/modindex"
)
var verbose = flag.Int("v", 0, "how much information to print")
type cmd struct {
name string
f func(string)
doc string
}
var cmds = []cmd{
{"update", update, "if there is an existing index of GOMODCACHE, update it. Otherwise create one."},
{"clean", clean, "removed unreferenced indexes more than an hour old"},
{"query", query, "not yet implemented"},
}
func goEnv(s string) string {
out, err := exec.Command("go", "env", s).Output()
if err != nil {
return ""
}
out = bytes.TrimSpace(out)
return string(out)
}
func main() {
flag.Parse()
log.SetFlags(log.Lshortfile)
gomodcache := goEnv("GOMODCACHE")
if gomodcache == "" {
log.Fatal("can't find GOMODCACHE")
}
if flag.NArg() == 0 {
update(gomodcache)
return
}
for _, c := range cmds {
if flag.Arg(0) == c.name {
c.f(gomodcache)
return
}
}
flag.Usage()
}
func init() {
var sb strings.Builder
fmt.Fprintf(&sb, "usage:\n")
for _, c := range cmds {
fmt.Fprintf(&sb, "'%s': %s\n", c.name, c.doc)
}
msg := sb.String()
flag.Usage = func() {
fmt.Fprint(os.Stderr, msg)
}
}
func update(dir string) {
if _, err := modindex.Update(dir); err != nil {
log.Print(err)
}
}
func query(dir string) {
panic("implement")
}
func clean(_ string) {
des := modindex.IndexDir
// look at the files starting with 'index'
// the current ones of each version are pointed to by
// index-name-%d files. Any others more than an hour old
// are deleted.
dis, err := os.ReadDir(des)
if err != nil {
log.Fatal(err)
}
cutoff := time.Now().Add(-time.Hour)
var inames []string // older files named index*
curnames := make(map[string]bool) // current versions of index (different CurrentVersion)
for _, de := range dis {
if !strings.HasPrefix(de.Name(), "index") {
continue
}
if strings.HasPrefix(de.Name(), "index-name-") {
buf, err := os.ReadFile(filepath.Join(des, de.Name()))
if err != nil {
log.Print(err)
continue
}
curnames[string(buf)] = true
if *verbose > 1 {
log.Printf("latest index is %s", string(buf))
}
}
info, err := de.Info()
if err != nil {
log.Print(err)
continue
}
if info.ModTime().Before(cutoff) && !strings.HasPrefix(de.Name(), "index-name-") {
// add to the list of files to be removed. index-name-%d files are never removed
inames = append(inames, de.Name())
if *verbose > 0 {
log.Printf("%s:%s", de.Name(), cutoff.Sub(info.ModTime()))
}
}
}
for _, nm := range inames {
if curnames[nm] {
continue
}
err := os.Remove(filepath.Join(des, nm))
if err != nil && *verbose > 0 {
log.Printf("%s not removed (%v)", nm, err)
}
}
}