@@ -7,6 +7,7 @@ package cache
7
7
import (
8
8
"bufio"
9
9
"cmd/go/internal/base"
10
+ "cmd/go/internal/cacheprog"
10
11
"cmd/internal/quoted"
11
12
"context"
12
13
"crypto/sha256"
@@ -38,7 +39,7 @@ type ProgCache struct {
38
39
39
40
// can are the commands that the child process declared that it supports.
40
41
// This is effectively the versioning mechanism.
41
- can map [ProgCmd ]bool
42
+ can map [cacheprog. ProgCmd ]bool
42
43
43
44
// fuzzDirCache is another Cache implementation to use for the FuzzDir
44
45
// method. In practice this is the default GOCACHE disk-based
@@ -55,142 +56,14 @@ type ProgCache struct {
55
56
56
57
mu sync.Mutex // guards following fields
57
58
nextID int64
58
- inFlight map [int64 ]chan <- * ProgResponse
59
+ inFlight map [int64 ]chan <- * cacheprog. ProgResponse
59
60
outputFile map [OutputID ]string // object => abs path on disk
60
61
61
62
// writeMu serializes writing to the child process.
62
63
// It must never be held at the same time as mu.
63
64
writeMu sync.Mutex
64
65
}
65
66
66
- // The following types define the protocol for a GOCACHEPROG program.
67
- //
68
- // By default, the go command manages a build cache stored in the file system
69
- // itself. GOCACHEPROG can be set to the name of a command (with optional
70
- // space-separated flags) that implements the go command build cache externally.
71
- // This permits defining a different cache policy.
72
- //
73
- // The go command will start the GOCACHEPROG as a subprocess and communicate
74
- // with it via JSON messages over stdin/stdout. The subprocess's stderr will be
75
- // connected to the go command's stderr.
76
- //
77
- // The subprocess should immediately send a [ProgResponse] with its capabilities.
78
- // After that, the go command will send a stream of [ProgRequest] messages and the
79
- // subprocess should reply to each [ProgRequest] with a [ProgResponse] message.
80
-
81
- // ProgCmd is a command that can be issued to a child process.
82
- //
83
- // If the interface needs to grow, the go command can add new commands or new
84
- // versioned commands like "get2" in the future. The initial [ProgResponse] from
85
- // the child process indicates which commands it supports.
86
- type ProgCmd string
87
-
88
- const (
89
- // cmdPut tells the cache program to store an object in the cache.
90
- //
91
- // [ProgRequest.ActionID] is the cache key of this object. The cache should
92
- // store [ProgRequest.OutputID] and [ProgRequest.Body] under this key for a
93
- // later "get" request. It must also store the Body in a file in the local
94
- // file system and return the path to that file in [ProgResponse.DiskPath],
95
- // which must exist at least until a "close" request.
96
- cmdPut = ProgCmd ("put" )
97
-
98
- // cmdGet tells the cache program to retrieve an object from the cache.
99
- //
100
- // [ProgRequest.ActionID] specifies the key of the object to get. If the
101
- // cache does not contain this object, it should set [ProgResponse.Miss] to
102
- // true. Otherwise, it should populate the fields of [ProgResponse],
103
- // including setting [ProgResponse.OutputID] to the OutputID of the original
104
- // "put" request and [ProgResponse.DiskPath] to the path of a local file
105
- // containing the Body of the original "put" request. That file must
106
- // continue to exist at least until a "close" request.
107
- cmdGet = ProgCmd ("get" )
108
-
109
- // cmdClose requests that the cache program exit gracefully.
110
- //
111
- // The cache program should reply to this request and then exit
112
- // (thus closing its stdout).
113
- cmdClose = ProgCmd ("close" )
114
- )
115
-
116
- // ProgRequest is the JSON-encoded message that's sent from the go command to
117
- // the GOCACHEPROG child process over stdin. Each JSON object is on its own
118
- // line. A ProgRequest of Type "put" with BodySize > 0 will be followed by a
119
- // line containing a base64-encoded JSON string literal of the body.
120
- type ProgRequest struct {
121
- // ID is a unique number per process across all requests.
122
- // It must be echoed in the ProgResponse from the child.
123
- ID int64
124
-
125
- // Command is the type of request.
126
- // The go command will only send commands that were declared
127
- // as supported by the child.
128
- Command ProgCmd
129
-
130
- // ActionID is the cache key for "put" and "get" requests.
131
- ActionID []byte `json:",omitempty"` // or nil if not used
132
-
133
- // OutputID is stored with the body for "put" requests.
134
- //
135
- // Prior to Go 1.24, when GOCACHEPROG was still an experiment, this was
136
- // accidentally named ObjectID. It was renamed to OutputID in Go 1.24.
137
- OutputID []byte `json:",omitempty"` // or nil if not used
138
-
139
- // Body is the body for "put" requests. It's sent after the JSON object
140
- // as a base64-encoded JSON string when BodySize is non-zero.
141
- // It's sent as a separate JSON value instead of being a struct field
142
- // send in this JSON object so large values can be streamed in both directions.
143
- // The base64 string body of a ProgRequest will always be written
144
- // immediately after the JSON object and a newline.
145
- Body io.Reader `json:"-"`
146
-
147
- // BodySize is the number of bytes of Body. If zero, the body isn't written.
148
- BodySize int64 `json:",omitempty"`
149
-
150
- // ObjectID is the accidental spelling of OutputID that was used prior to Go
151
- // 1.24.
152
- //
153
- // Deprecated: use OutputID. This field is only populated temporarily for
154
- // backwards compatibility with Go 1.23 and earlier when
155
- // GOEXPERIMENT=gocacheprog is set. It will be removed in Go 1.25.
156
- ObjectID []byte `json:",omitempty"`
157
- }
158
-
159
- // ProgResponse is the JSON response from the child process to the go command.
160
- //
161
- // With the exception of the first protocol message that the child writes to its
162
- // stdout with ID==0 and KnownCommands populated, these are only sent in
163
- // response to a ProgRequest from the go command.
164
- //
165
- // ProgResponses can be sent in any order. The ID must match the request they're
166
- // replying to.
167
- type ProgResponse struct {
168
- ID int64 // that corresponds to ProgRequest; they can be answered out of order
169
- Err string `json:",omitempty"` // if non-empty, the error
170
-
171
- // KnownCommands is included in the first message that cache helper program
172
- // writes to stdout on startup (with ID==0). It includes the
173
- // ProgRequest.Command types that are supported by the program.
174
- //
175
- // This lets the go command extend the protocol gracefully over time (adding
176
- // "get2", etc), or fail gracefully when needed. It also lets the go command
177
- // verify the program wants to be a cache helper.
178
- KnownCommands []ProgCmd `json:",omitempty"`
179
-
180
- // For "get" requests.
181
-
182
- Miss bool `json:",omitempty"` // cache miss
183
- OutputID []byte `json:",omitempty"` // the ObjectID stored with the body
184
- Size int64 `json:",omitempty"` // body size in bytes
185
- Time * time.Time `json:",omitempty"` // when the object was put in the cache (optional; used for cache expiration)
186
-
187
- // For "get" and "put" requests.
188
-
189
- // DiskPath is the absolute path on disk of the body corresponding to a
190
- // "get" (on cache hit) or "put" request's ActionID.
191
- DiskPath string `json:",omitempty"`
192
- }
193
-
194
67
// startCacheProg starts the prog binary (with optional space-separated flags)
195
68
// and returns a Cache implementation that talks to it.
196
69
//
@@ -238,14 +111,14 @@ func startCacheProg(progAndArgs string, fuzzDirCache Cache) Cache {
238
111
stdout : out ,
239
112
stdin : in ,
240
113
bw : bufio .NewWriter (in ),
241
- inFlight : make (map [int64 ]chan <- * ProgResponse ),
114
+ inFlight : make (map [int64 ]chan <- * cacheprog. ProgResponse ),
242
115
outputFile : make (map [OutputID ]string ),
243
116
readLoopDone : make (chan struct {}),
244
117
}
245
118
246
119
// Register our interest in the initial protocol message from the child to
247
120
// us, saying what it can do.
248
- capResc := make (chan * ProgResponse , 1 )
121
+ capResc := make (chan * cacheprog. ProgResponse , 1 )
249
122
pc .inFlight [0 ] = capResc
250
123
251
124
pc .jenc = json .NewEncoder (pc .bw )
@@ -260,7 +133,7 @@ func startCacheProg(progAndArgs string, fuzzDirCache Cache) Cache {
260
133
case <- timer .C :
261
134
log .Printf ("# still waiting for GOCACHEPROG %v ..." , prog )
262
135
case capRes := <- capResc :
263
- can := map [ProgCmd ]bool {}
136
+ can := map [cacheprog. ProgCmd ]bool {}
264
137
for _ , cmd := range capRes .KnownCommands {
265
138
can [cmd ] = true
266
139
}
@@ -277,7 +150,7 @@ func (c *ProgCache) readLoop(readLoopDone chan<- struct{}) {
277
150
defer close (readLoopDone )
278
151
jd := json .NewDecoder (c .stdout )
279
152
for {
280
- res := new (ProgResponse )
153
+ res := new (cacheprog. ProgResponse )
281
154
if err := jd .Decode (res ); err != nil {
282
155
if c .closing .Load () {
283
156
return // quietly
@@ -302,8 +175,8 @@ func (c *ProgCache) readLoop(readLoopDone chan<- struct{}) {
302
175
}
303
176
}
304
177
305
- func (c * ProgCache ) send (ctx context.Context , req * ProgRequest ) (* ProgResponse , error ) {
306
- resc := make (chan * ProgResponse , 1 )
178
+ func (c * ProgCache ) send (ctx context.Context , req * cacheprog. ProgRequest ) (* cacheprog. ProgResponse , error ) {
179
+ resc := make (chan * cacheprog. ProgResponse , 1 )
307
180
if err := c .writeToChild (req , resc ); err != nil {
308
181
return nil , err
309
182
}
@@ -318,7 +191,7 @@ func (c *ProgCache) send(ctx context.Context, req *ProgRequest) (*ProgResponse,
318
191
}
319
192
}
320
193
321
- func (c * ProgCache ) writeToChild (req * ProgRequest , resc chan <- * ProgResponse ) (err error ) {
194
+ func (c * ProgCache ) writeToChild (req * cacheprog. ProgRequest , resc chan <- * cacheprog. ProgResponse ) (err error ) {
322
195
c .mu .Lock ()
323
196
c .nextID ++
324
197
req .ID = c .nextID
@@ -369,7 +242,7 @@ func (c *ProgCache) writeToChild(req *ProgRequest, resc chan<- *ProgResponse) (e
369
242
}
370
243
371
244
func (c * ProgCache ) Get (a ActionID ) (Entry , error ) {
372
- if ! c .can [cmdGet ] {
245
+ if ! c .can [cacheprog . CmdGet ] {
373
246
// They can't do a "get". Maybe they're a write-only cache.
374
247
//
375
248
// TODO(bradfitz,bcmills): figure out the proper error type here. Maybe
@@ -379,8 +252,8 @@ func (c *ProgCache) Get(a ActionID) (Entry, error) {
379
252
// error types on the Cache interface.
380
253
return Entry {}, & entryNotFoundError {}
381
254
}
382
- res , err := c .send (c .ctx , & ProgRequest {
383
- Command : cmdGet ,
255
+ res , err := c .send (c .ctx , & cacheprog. ProgRequest {
256
+ Command : cacheprog . CmdGet ,
384
257
ActionID : a [:],
385
258
})
386
259
if err != nil {
@@ -436,7 +309,7 @@ func (c *ProgCache) Put(a ActionID, file io.ReadSeeker) (_ OutputID, size int64,
436
309
return OutputID {}, 0 , err
437
310
}
438
311
439
- if ! c .can [cmdPut ] {
312
+ if ! c .can [cacheprog . CmdPut ] {
440
313
// Child is a read-only cache. Do nothing.
441
314
return out , size , nil
442
315
}
@@ -448,8 +321,8 @@ func (c *ProgCache) Put(a ActionID, file io.ReadSeeker) (_ OutputID, size int64,
448
321
deprecatedValue = out [:]
449
322
}
450
323
451
- res , err := c .send (c .ctx , & ProgRequest {
452
- Command : cmdPut ,
324
+ res , err := c .send (c .ctx , & cacheprog. ProgRequest {
325
+ Command : cacheprog . CmdPut ,
453
326
ActionID : a [:],
454
327
OutputID : out [:],
455
328
ObjectID : deprecatedValue , // TODO(bradfitz): remove in Go 1.25
@@ -473,8 +346,8 @@ func (c *ProgCache) Close() error {
473
346
// First write a "close" message to the child so it can exit nicely
474
347
// and clean up if it wants. Only after that exchange do we cancel
475
348
// the context that kills the process.
476
- if c .can [cmdClose ] {
477
- _ , err = c .send (c .ctx , & ProgRequest {Command : cmdClose })
349
+ if c .can [cacheprog . CmdClose ] {
350
+ _ , err = c .send (c .ctx , & cacheprog. ProgRequest {Command : cacheprog . CmdClose })
478
351
}
479
352
// Cancel the context, which will close the helper's stdin.
480
353
c .ctxCancel ()
0 commit comments