@@ -7,9 +7,11 @@ import (
77 "errors"
88 "fmt"
99 "runtime"
10- "sort"
10+ "slices"
11+ "strings"
1112 "sync"
1213
14+ "golang.org/x/exp/maps"
1315 "golang.org/x/tools/go/packages"
1416
1517 "github.com/golangci/golangci-lint/internal/go/cache"
@@ -25,8 +27,13 @@ const (
2527 HashModeNeedAllDeps
2628)
2729
28- // Cache is a per-package data cache. A cached data is invalidated when
29- // package, or it's dependencies change.
30+ var ErrMissing = errors .New ("missing data" )
31+
32+ type hashResults map [HashMode ]string
33+
34+ // Cache is a per-package data cache.
35+ // A cached data is invalidated when package,
36+ // or it's dependencies change.
3037type Cache struct {
3138 lowLevelCache cache.Cache
3239 pkgHashes sync.Map
@@ -45,91 +52,62 @@ func NewCache(sw *timeutils.Stopwatch, log logutils.Log) (*Cache, error) {
4552}
4653
4754func (c * Cache ) Close () {
48- c .sw .TrackStage ("close" , func () {
49- err := c .lowLevelCache .Close ()
50- if err != nil {
51- c .log .Errorf ("cache close: %v" , err )
52- }
53- })
55+ err := c .sw .TrackStageErr ("close" , c .lowLevelCache .Close )
56+ if err != nil {
57+ c .log .Errorf ("cache close: %v" , err )
58+ }
5459}
5560
5661func (c * Cache ) Put (pkg * packages.Package , mode HashMode , key string , data any ) error {
57- var err error
58- buf := & bytes.Buffer {}
59- c .sw .TrackStage ("gob" , func () {
60- err = gob .NewEncoder (buf ).Encode (data )
61- })
62+ buf , err := c .encode (data )
6263 if err != nil {
63- return fmt . Errorf ( "failed to gob encode: %w" , err )
64+ return err
6465 }
6566
66- var aID cache.ActionID
67-
68- c .sw .TrackStage ("key build" , func () {
69- aID , err = c .pkgActionID (pkg , mode )
70- if err == nil {
71- subkey , subkeyErr := cache .Subkey (aID , key )
72- if subkeyErr != nil {
73- err = fmt .Errorf ("failed to build subkey: %w" , subkeyErr )
74- }
75- aID = subkey
76- }
77- })
67+ actionID , err := c .buildKey (pkg , mode , key )
7868 if err != nil {
7969 return fmt .Errorf ("failed to calculate package %s action id: %w" , pkg .Name , err )
8070 }
81- c .ioSem <- struct {}{}
82- c .sw .TrackStage ("cache io" , func () {
83- err = cache .PutBytes (c .lowLevelCache , aID , buf .Bytes ())
84- })
85- <- c .ioSem
71+
72+ err = c .putBytes (actionID , buf )
8673 if err != nil {
8774 return fmt .Errorf ("failed to save data to low-level cache by key %s for package %s: %w" , key , pkg .Name , err )
8875 }
8976
9077 return nil
9178}
9279
93- var ErrMissing = errors .New ("missing data" )
94-
9580func (c * Cache ) Get (pkg * packages.Package , mode HashMode , key string , data any ) error {
96- var aID cache.ActionID
97- var err error
98- c .sw .TrackStage ("key build" , func () {
99- aID , err = c .pkgActionID (pkg , mode )
100- if err == nil {
101- subkey , subkeyErr := cache .Subkey (aID , key )
102- if subkeyErr != nil {
103- err = fmt .Errorf ("failed to build subkey: %w" , subkeyErr )
104- }
105- aID = subkey
106- }
107- })
81+ actionID , err := c .buildKey (pkg , mode , key )
10882 if err != nil {
10983 return fmt .Errorf ("failed to calculate package %s action id: %w" , pkg .Name , err )
11084 }
11185
112- var b []byte
113- c .ioSem <- struct {}{}
114- c .sw .TrackStage ("cache io" , func () {
115- b , _ , err = cache .GetBytes (c .lowLevelCache , aID )
116- })
117- <- c .ioSem
86+ cachedData , err := c .getBytes (actionID )
11887 if err != nil {
11988 if cache .IsErrMissing (err ) {
12089 return ErrMissing
12190 }
12291 return fmt .Errorf ("failed to get data from low-level cache by key %s for package %s: %w" , key , pkg .Name , err )
12392 }
12493
125- c .sw .TrackStage ("gob" , func () {
126- err = gob .NewDecoder (bytes .NewReader (b )).Decode (data )
127- })
128- if err != nil {
129- return fmt .Errorf ("failed to gob decode: %w" , err )
130- }
94+ return c .decode (cachedData , data )
95+ }
13196
132- return nil
97+ func (c * Cache ) buildKey (pkg * packages.Package , mode HashMode , key string ) (cache.ActionID , error ) {
98+ return timeutils .TrackStage (c .sw , "key build" , func () (cache.ActionID , error ) {
99+ actionID , err := c .pkgActionID (pkg , mode )
100+ if err != nil {
101+ return actionID , err
102+ }
103+
104+ subkey , subkeyErr := cache .Subkey (actionID , key )
105+ if subkeyErr != nil {
106+ return actionID , fmt .Errorf ("failed to build subkey: %w" , subkeyErr )
107+ }
108+
109+ return subkey , nil
110+ })
133111}
134112
135113func (c * Cache ) pkgActionID (pkg * packages.Package , mode HashMode ) (cache.ActionID , error ) {
@@ -142,89 +120,172 @@ func (c *Cache) pkgActionID(pkg *packages.Package, mode HashMode) (cache.ActionI
142120 if err != nil {
143121 return cache.ActionID {}, fmt .Errorf ("failed to make a hash: %w" , err )
144122 }
123+
145124 fmt .Fprintf (key , "pkgpath %s\n " , pkg .PkgPath )
146125 fmt .Fprintf (key , "pkghash %s\n " , hash )
147126
148127 return key .Sum (), nil
149128}
150129
151- // packageHash computes a package's hash. The hash is based on all Go
152- // files that make up the package, as well as the hashes of imported
153- // packages.
154130func (c * Cache ) packageHash (pkg * packages.Package , mode HashMode ) (string , error ) {
155- type hashResults map [HashMode ]string
156- hashResI , ok := c .pkgHashes .Load (pkg )
157- if ok {
158- hashRes := hashResI .(hashResults )
159- if _ , ok := hashRes [mode ]; ! ok {
160- return "" , fmt .Errorf ("no mode %d in hash result" , mode )
131+ results , found := c .pkgHashes .Load (pkg )
132+ if found {
133+ hashRes := results .(hashResults )
134+ if result , ok := hashRes [mode ]; ok {
135+ return result , nil
161136 }
162- return hashRes [mode ], nil
137+
138+ return "" , fmt .Errorf ("no mode %d in hash result" , mode )
163139 }
164140
165- hashRes := hashResults {}
141+ hashRes , err := c .computePkgHash (pkg )
142+ if err != nil {
143+ return "" , err
144+ }
145+
146+ result , found := hashRes [mode ]
147+ if ! found {
148+ return "" , fmt .Errorf ("invalid mode %d" , mode )
149+ }
166150
151+ c .pkgHashes .Store (pkg , hashRes )
152+
153+ return result , nil
154+ }
155+
156+ // computePkgHash computes a package's hash.
157+ // The hash is based on all Go files that make up the package,
158+ // as well as the hashes of imported packages.
159+ func (c * Cache ) computePkgHash (pkg * packages.Package ) (hashResults , error ) {
167160 key , err := cache .NewHash ("package hash" )
168161 if err != nil {
169- return "" , fmt .Errorf ("failed to make a hash: %w" , err )
162+ return nil , fmt .Errorf ("failed to make a hash: %w" , err )
170163 }
171164
165+ hashRes := hashResults {}
166+
172167 fmt .Fprintf (key , "pkgpath %s\n " , pkg .PkgPath )
168+
173169 for _ , f := range pkg .CompiledGoFiles {
174- c .ioSem <- struct {}{}
175- h , fErr := cache .FileHash (f )
176- <- c .ioSem
170+ h , fErr := c .fileHash (f )
177171 if fErr != nil {
178- return "" , fmt .Errorf ("failed to calculate file %s hash: %w" , f , fErr )
172+ return nil , fmt .Errorf ("failed to calculate file %s hash: %w" , f , fErr )
179173 }
174+
180175 fmt .Fprintf (key , "file %s %x\n " , f , h )
181176 }
177+
182178 curSum := key .Sum ()
183179 hashRes [HashModeNeedOnlySelf ] = hex .EncodeToString (curSum [:])
184180
185- imps := make ([]* packages.Package , 0 , len (pkg .Imports ))
186- for _ , imp := range pkg .Imports {
187- imps = append (imps , imp )
188- }
189- sort .Slice (imps , func (i , j int ) bool {
190- return imps [i ].PkgPath < imps [j ].PkgPath
181+ imps := maps .Values (pkg .Imports )
182+
183+ slices .SortFunc (imps , func (a , b * packages.Package ) int {
184+ return strings .Compare (a .PkgPath , b .PkgPath )
191185 })
192186
193- calcDepsHash := func (depMode HashMode ) error {
194- for _ , dep := range imps {
195- if dep .PkgPath == "unsafe" {
196- continue
197- }
187+ if err := c .computeDepsHash (HashModeNeedOnlySelf , imps , key ); err != nil {
188+ return nil , err
189+ }
190+
191+ curSum = key .Sum ()
192+ hashRes [HashModeNeedDirectDeps ] = hex .EncodeToString (curSum [:])
193+
194+ if err := c .computeDepsHash (HashModeNeedAllDeps , imps , key ); err != nil {
195+ return nil , err
196+ }
197+
198+ curSum = key .Sum ()
199+ hashRes [HashModeNeedAllDeps ] = hex .EncodeToString (curSum [:])
198200
199- depHash , depErr := c .packageHash (dep , depMode )
200- if depErr != nil {
201- return fmt .Errorf ("failed to calculate hash for dependency %s with mode %d: %w" , dep .Name , depMode , depErr )
202- }
201+ return hashRes , nil
202+ }
203203
204- fmt .Fprintf (key , "import %s %s\n " , dep .PkgPath , depHash )
204+ func (c * Cache ) computeDepsHash (depMode HashMode , imps []* packages.Package , key * cache.Hash ) error {
205+ for _ , dep := range imps {
206+ if dep .PkgPath == "unsafe" {
207+ continue
205208 }
206- return nil
209+
210+ depHash , err := c .packageHash (dep , depMode )
211+ if err != nil {
212+ return fmt .Errorf ("failed to calculate hash for dependency %s with mode %d: %w" , dep .Name , depMode , err )
213+ }
214+
215+ fmt .Fprintf (key , "import %s %s\n " , dep .PkgPath , depHash )
207216 }
208217
209- if err := calcDepsHash (HashModeNeedOnlySelf ); err != nil {
210- return "" , err
218+ return nil
219+ }
220+
221+ func (c * Cache ) putBytes (actionID cache.ActionID , buf * bytes.Buffer ) error {
222+ c .ioSem <- struct {}{}
223+
224+ err := c .sw .TrackStageErr ("cache io" , func () error {
225+ return cache .PutBytes (c .lowLevelCache , actionID , buf .Bytes ())
226+ })
227+
228+ <- c .ioSem
229+
230+ if err != nil {
231+ return err
211232 }
212233
213- curSum = key . Sum ()
214- hashRes [ HashModeNeedDirectDeps ] = hex . EncodeToString ( curSum [:])
234+ return nil
235+ }
215236
216- if err := calcDepsHash (HashModeNeedAllDeps ); err != nil {
217- return "" , err
237+ func (c * Cache ) getBytes (actionID cache.ActionID ) ([]byte , error ) {
238+ c .ioSem <- struct {}{}
239+
240+ cachedData , err := timeutils .TrackStage (c .sw , "cache io" , func () ([]byte , error ) {
241+ b , _ , errGB := cache .GetBytes (c .lowLevelCache , actionID )
242+ return b , errGB
243+ })
244+
245+ <- c .ioSem
246+
247+ if err != nil {
248+ return nil , err
218249 }
219- curSum = key .Sum ()
220- hashRes [HashModeNeedAllDeps ] = hex .EncodeToString (curSum [:])
221250
222- if _ , ok := hashRes [mode ]; ! ok {
223- return "" , fmt .Errorf ("invalid mode %d" , mode )
251+ return cachedData , nil
252+ }
253+
254+ func (c * Cache ) fileHash (f string ) ([cache .HashSize ]byte , error ) {
255+ c .ioSem <- struct {}{}
256+
257+ h , err := cache .FileHash (f )
258+
259+ <- c .ioSem
260+
261+ if err != nil {
262+ return [cache .HashSize ]byte {}, err
224263 }
225264
226- c .pkgHashes .Store (pkg , hashRes )
227- return hashRes [mode ], nil
265+ return h , nil
266+ }
267+
268+ func (c * Cache ) encode (data any ) (* bytes.Buffer , error ) {
269+ buf := & bytes.Buffer {}
270+ err := c .sw .TrackStageErr ("gob" , func () error {
271+ return gob .NewEncoder (buf ).Encode (data )
272+ })
273+ if err != nil {
274+ return nil , fmt .Errorf ("failed to gob encode: %w" , err )
275+ }
276+
277+ return buf , nil
278+ }
279+
280+ func (c * Cache ) decode (b []byte , data any ) error {
281+ err := c .sw .TrackStageErr ("gob" , func () error {
282+ return gob .NewDecoder (bytes .NewReader (b )).Decode (data )
283+ })
284+ if err != nil {
285+ return fmt .Errorf ("failed to gob decode: %w" , err )
286+ }
287+
288+ return nil
228289}
229290
230291func SetSalt (b * bytes.Buffer ) {
0 commit comments