@@ -7,9 +7,11 @@ import (
7
7
"errors"
8
8
"fmt"
9
9
"runtime"
10
- "sort"
10
+ "slices"
11
+ "strings"
11
12
"sync"
12
13
14
+ "golang.org/x/exp/maps"
13
15
"golang.org/x/tools/go/packages"
14
16
15
17
"github.com/golangci/golangci-lint/internal/go/cache"
@@ -25,8 +27,13 @@ const (
25
27
HashModeNeedAllDeps
26
28
)
27
29
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.
30
37
type Cache struct {
31
38
lowLevelCache cache.Cache
32
39
pkgHashes sync.Map
@@ -45,91 +52,62 @@ func NewCache(sw *timeutils.Stopwatch, log logutils.Log) (*Cache, error) {
45
52
}
46
53
47
54
func (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
+ }
54
59
}
55
60
56
61
func (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 )
62
63
if err != nil {
63
- return fmt . Errorf ( "failed to gob encode: %w" , err )
64
+ return err
64
65
}
65
66
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 )
78
68
if err != nil {
79
69
return fmt .Errorf ("failed to calculate package %s action id: %w" , pkg .Name , err )
80
70
}
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 )
86
73
if err != nil {
87
74
return fmt .Errorf ("failed to save data to low-level cache by key %s for package %s: %w" , key , pkg .Name , err )
88
75
}
89
76
90
77
return nil
91
78
}
92
79
93
- var ErrMissing = errors .New ("missing data" )
94
-
95
80
func (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 )
108
82
if err != nil {
109
83
return fmt .Errorf ("failed to calculate package %s action id: %w" , pkg .Name , err )
110
84
}
111
85
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 )
118
87
if err != nil {
119
88
if cache .IsErrMissing (err ) {
120
89
return ErrMissing
121
90
}
122
91
return fmt .Errorf ("failed to get data from low-level cache by key %s for package %s: %w" , key , pkg .Name , err )
123
92
}
124
93
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
+ }
131
96
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
+ })
133
111
}
134
112
135
113
func (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
142
120
if err != nil {
143
121
return cache.ActionID {}, fmt .Errorf ("failed to make a hash: %w" , err )
144
122
}
123
+
145
124
fmt .Fprintf (key , "pkgpath %s\n " , pkg .PkgPath )
146
125
fmt .Fprintf (key , "pkghash %s\n " , hash )
147
126
148
127
return key .Sum (), nil
149
128
}
150
129
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.
154
130
func (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
161
136
}
162
- return hashRes [mode ], nil
137
+
138
+ return "" , fmt .Errorf ("no mode %d in hash result" , mode )
163
139
}
164
140
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
+ }
166
150
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 ) {
167
160
key , err := cache .NewHash ("package hash" )
168
161
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 )
170
163
}
171
164
165
+ hashRes := hashResults {}
166
+
172
167
fmt .Fprintf (key , "pkgpath %s\n " , pkg .PkgPath )
168
+
173
169
for _ , f := range pkg .CompiledGoFiles {
174
- c .ioSem <- struct {}{}
175
- h , fErr := cache .FileHash (f )
176
- <- c .ioSem
170
+ h , fErr := c .fileHash (f )
177
171
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 )
179
173
}
174
+
180
175
fmt .Fprintf (key , "file %s %x\n " , f , h )
181
176
}
177
+
182
178
curSum := key .Sum ()
183
179
hashRes [HashModeNeedOnlySelf ] = hex .EncodeToString (curSum [:])
184
180
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 )
191
185
})
192
186
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 [:])
198
200
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
+ }
203
203
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
205
208
}
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 )
207
216
}
208
217
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
211
232
}
212
233
213
- curSum = key . Sum ()
214
- hashRes [ HashModeNeedDirectDeps ] = hex . EncodeToString ( curSum [:])
234
+ return nil
235
+ }
215
236
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
218
249
}
219
- curSum = key .Sum ()
220
- hashRes [HashModeNeedAllDeps ] = hex .EncodeToString (curSum [:])
221
250
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
224
263
}
225
264
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
228
289
}
229
290
230
291
func SetSalt (b * bytes.Buffer ) {
0 commit comments