diff --git a/internal/loader/artifact_cache.go b/internal/loader/artifact_cache.go index e920d5a..ff3f753 100644 --- a/internal/loader/artifact_cache.go +++ b/internal/loader/artifact_cache.go @@ -20,10 +20,10 @@ import ( "encoding/hex" "go/token" "go/types" + "io" "os" "path/filepath" "runtime" - "strconv" "golang.org/x/tools/go/gcexportdata" ) @@ -62,7 +62,7 @@ func loaderArtifactPath(env []string, meta *packageMeta, isLocal bool) (string, func loaderArtifactKey(meta *packageMeta, isLocal bool) (string, error) { sum := sha256.New() - sum.Write([]byte("wire-loader-artifact-v3\n")) + sum.Write([]byte("wire-loader-artifact-v4\n")) sum.Write([]byte(runtime.Version())) sum.Write([]byte{'\n'}) sum.Write([]byte(meta.ImportPath)) @@ -73,26 +73,15 @@ func loaderArtifactKey(meta *packageMeta, isLocal bool) (string, error) { sum.Write([]byte(meta.Export)) sum.Write([]byte{'\n'}) if meta.Export != "" { - info, err := os.Stat(meta.Export) + h, err := hashFileContent(meta.Export) if err != nil { return "", err } - sum.Write([]byte(strconv.FormatInt(info.Size(), 10))) - sum.Write([]byte{'\n'}) - sum.Write([]byte(strconv.FormatInt(info.ModTime().UnixNano(), 10))) + sum.Write([]byte(h)) sum.Write([]byte{'\n'}) } else { - for _, name := range metaFiles(meta) { - info, err := os.Stat(name) - if err != nil { - return "", err - } - sum.Write([]byte(name)) - sum.Write([]byte{'\n'}) - sum.Write([]byte(strconv.FormatInt(info.Size(), 10))) - sum.Write([]byte{'\n'}) - sum.Write([]byte(strconv.FormatInt(info.ModTime().UnixNano(), 10))) - sum.Write([]byte{'\n'}) + if err := hashMetaFiles(sum, metaFiles(meta)); err != nil { + return "", err } } if meta.Error != nil { @@ -101,19 +90,35 @@ func loaderArtifactKey(meta *packageMeta, isLocal bool) (string, error) { } return hex.EncodeToString(sum.Sum(nil)), nil } - for _, name := range metaFiles(meta) { - info, err := os.Stat(name) - if err != nil { - return "", err - } + if err := hashMetaFiles(sum, metaFiles(meta)); err != nil { + return "", err + } + return hex.EncodeToString(sum.Sum(nil)), nil +} + +// hashFileContent returns the hex-encoded SHA-256 of the file content. +func hashFileContent(path string) (string, error) { + data, err := os.ReadFile(path) + if err != nil { + return "", err + } + h := sha256.Sum256(data) + return hex.EncodeToString(h[:]), nil +} + +// hashMetaFiles writes content-based hashes for each file into sum. +func hashMetaFiles(sum io.Writer, names []string) error { + for _, name := range names { sum.Write([]byte(name)) sum.Write([]byte{'\n'}) - sum.Write([]byte(strconv.FormatInt(info.Size(), 10))) - sum.Write([]byte{'\n'}) - sum.Write([]byte(strconv.FormatInt(info.ModTime().UnixNano(), 10))) + h, err := hashFileContent(name) + if err != nil { + return err + } + sum.Write([]byte(h)) sum.Write([]byte{'\n'}) } - return hex.EncodeToString(sum.Sum(nil)), nil + return nil } func readLoaderArtifact(path string, fset *token.FileSet, imports map[string]*types.Package, pkgPath string) (*types.Package, error) { diff --git a/internal/loader/discovery_cache.go b/internal/loader/discovery_cache.go index 1151853..6d59a22 100644 --- a/internal/loader/discovery_cache.go +++ b/internal/loader/discovery_cache.go @@ -28,10 +28,11 @@ type discoveryLocalPackage struct { } type discoveryFileMeta struct { - Path string - Size int64 - ModTime int64 - IsDir bool + Path string + Size int64 + ModTime int64 // deprecated: kept for gob compat, not used for matching + ContentHash string // sha256 of file content + IsDir bool } type discoveryDirMeta struct { @@ -44,7 +45,7 @@ type discoveryFileFingerprint struct { Hash string } -const discoveryCacheVersion = 3 +const discoveryCacheVersion = 4 func readDiscoveryCache(req goListRequest) (map[string]*packageMeta, bool) { entry, err := loadDiscoveryCacheEntry(req) @@ -121,10 +122,16 @@ func validateDiscoveryCacheEntry(entry *discoveryCacheEntry) bool { return true } +const discoveryCacheDirEnv = "WIRE_DISCOVERY_CACHE_DIR" + func discoveryCachePath(req goListRequest) (string, error) { - base, err := os.UserCacheDir() - if err != nil { - return "", err + dir := os.Getenv(discoveryCacheDirEnv) + if dir == "" { + base, err := os.UserCacheDir() + if err != nil { + return "", err + } + dir = filepath.Join(base, "wire", "discovery-cache") } sumReq := struct { Version int @@ -147,7 +154,7 @@ func discoveryCachePath(req goListRequest) (string, error) { if err != nil { return "", err } - return filepath.Join(base, "wire", "discovery-cache", key+".gob"), nil + return filepath.Join(dir, key+".gob"), nil } func loadDiscoveryCacheEntry(req goListRequest) (*discoveryCacheEntry, error) { @@ -188,11 +195,19 @@ func statDiscoveryFile(path string) (discoveryFileMeta, bool) { if err != nil { return discoveryFileMeta{}, false } + h := "" + if !info.IsDir() { + var err error + h, err = hashFileContent(path) + if err != nil { + return discoveryFileMeta{}, false + } + } return discoveryFileMeta{ - Path: canonicalLoaderPath(path), - Size: info.Size(), - ModTime: info.ModTime().UnixNano(), - IsDir: info.IsDir(), + Path: canonicalLoaderPath(path), + Size: info.Size(), + ContentHash: h, + IsDir: info.IsDir(), }, true } @@ -201,7 +216,7 @@ func matchesDiscoveryFile(fm discoveryFileMeta) bool { if !ok { return false } - return cur.Size == fm.Size && cur.ModTime == fm.ModTime && cur.IsDir == fm.IsDir + return cur.ContentHash == fm.ContentHash && cur.IsDir == fm.IsDir } func statDiscoveryDir(path string) (discoveryDirMeta, bool) {