github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/go/loader/hash.go (about) 1 package loader 2 3 import ( 4 "fmt" 5 "runtime" 6 "sort" 7 "strings" 8 9 "github.com/amarpal/go-tools/go/buildid" 10 "github.com/amarpal/go-tools/lintcmd/cache" 11 ) 12 13 // computeHash computes a package's hash. The hash is based on all Go 14 // files that make up the package, as well as the hashes of imported 15 // packages. 16 func computeHash(c *cache.Cache, pkg *PackageSpec) (cache.ActionID, error) { 17 key := c.NewHash("package " + pkg.PkgPath) 18 fmt.Fprintf(key, "goos %s goarch %s\n", runtime.GOOS, runtime.GOARCH) 19 fmt.Fprintf(key, "import %q\n", pkg.PkgPath) 20 21 // Compute the hashes of all files making up the package. As an 22 // optimization, we use the build ID that Go already computed for 23 // us, because it is virtually identical to hashed all 24 // CompiledGoFiles. 25 success := false 26 if pkg.ExportFile != "" { 27 id, err := getBuildid(pkg.ExportFile) 28 if err == nil { 29 if idx := strings.IndexRune(id, '/'); idx > -1 { 30 fmt.Fprintf(key, "files %s\n", id[:idx]) 31 success = true 32 } 33 } 34 } 35 if !success { 36 for _, f := range pkg.CompiledGoFiles { 37 h, err := cache.FileHash(f) 38 if err != nil { 39 return cache.ActionID{}, err 40 } 41 fmt.Fprintf(key, "file %s %x\n", f, h) 42 } 43 } 44 45 imps := make([]*PackageSpec, 0, len(pkg.Imports)) 46 for _, v := range pkg.Imports { 47 imps = append(imps, v) 48 } 49 sort.Slice(imps, func(i, j int) bool { 50 return imps[i].PkgPath < imps[j].PkgPath 51 }) 52 53 for _, dep := range imps { 54 if dep.ExportFile == "" { 55 fmt.Fprintf(key, "import %s \n", dep.PkgPath) 56 } else { 57 id, err := getBuildid(dep.ExportFile) 58 if err == nil { 59 fmt.Fprintf(key, "import %s %s\n", dep.PkgPath, id) 60 } else { 61 fh, err := cache.FileHash(dep.ExportFile) 62 if err != nil { 63 return cache.ActionID{}, err 64 } 65 fmt.Fprintf(key, "import %s %x\n", dep.PkgPath, fh) 66 } 67 } 68 } 69 return key.Sum(), nil 70 } 71 72 var buildidCache = map[string]string{} 73 74 func getBuildid(f string) (string, error) { 75 if h, ok := buildidCache[f]; ok { 76 return h, nil 77 } 78 h, err := buildid.ReadFile(f) 79 if err != nil { 80 return "", err 81 } 82 buildidCache[f] = h 83 return h, nil 84 }