OLD | NEW |
1 package builder | 1 package builder |
2 | 2 |
3 import ( | 3 import ( |
4 "errors" | 4 "errors" |
5 "fmt" | 5 "fmt" |
6 "io/ioutil" | 6 "io/ioutil" |
7 "os" | 7 "os" |
8 "path" | 8 "path" |
9 "path/filepath" | 9 "path/filepath" |
10 "regexp" | 10 "regexp" |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
71 // | 71 // |
72 // fiddleRoot - The root directory where fiddle stores its files. See DESIGN.
md. | 72 // fiddleRoot - The root directory where fiddle stores its files. See DESIGN.
md. |
73 // depotTools - The directory where depot_tools is checked out. | 73 // depotTools - The directory where depot_tools is checked out. |
74 // repo - A vcs to pull hash info from. | 74 // repo - A vcs to pull hash info from. |
75 func New(fiddleRoot, depotTools string, repo vcsinfo.VCS) *Builder { | 75 func New(fiddleRoot, depotTools string, repo vcsinfo.VCS) *Builder { |
76 b := &Builder{ | 76 b := &Builder{ |
77 fiddleRoot: fiddleRoot, | 77 fiddleRoot: fiddleRoot, |
78 depotTools: depotTools, | 78 depotTools: depotTools, |
79 repo: repo, | 79 repo: repo, |
80 } | 80 } |
| 81 _, _ = b.AvailableBuilds() // Called for side-effect of loading hashes. |
81 go b.startDecimation() | 82 go b.startDecimation() |
82 b.updateCurrent() | 83 b.updateCurrent() |
83 | 84 |
84 return b | 85 return b |
85 } | 86 } |
86 | 87 |
87 // branch is used to sort the chrome branches in the Skia repo. | 88 // branch is used to sort the chrome branches in the Skia repo. |
88 type branch struct { | 89 type branch struct { |
89 N int | 90 N int |
90 Name string | 91 Name string |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
152 } else { | 153 } else { |
153 if githash, err = buildskia.GetSkiaHash(nil); err != nil { | 154 if githash, err = buildskia.GetSkiaHash(nil); err != nil { |
154 return nil, fmt.Errorf("Failed to retrieve Skia LKGR: %s
", err) | 155 return nil, fmt.Errorf("Failed to retrieve Skia LKGR: %s
", err) |
155 } | 156 } |
156 } | 157 } |
157 checkout := path.Join(versions, githash) | 158 checkout := path.Join(versions, githash) |
158 | 159 |
159 fi, err := os.Stat(checkout) | 160 fi, err := os.Stat(checkout) |
160 // If the file is present and a directory then only proceed if 'force' i
s true. | 161 // If the file is present and a directory then only proceed if 'force' i
s true. |
161 if err == nil && fi.IsDir() == true && !force { | 162 if err == nil && fi.IsDir() == true && !force { |
| 163 glog.Infof("Dir already exists: %s", checkout) |
162 return nil, AlreadyExistsErr | 164 return nil, AlreadyExistsErr |
163 } | 165 } |
164 | 166 |
165 ret, err := buildskia.DownloadSkia("", githash, checkout, b.depotTools,
false, deps) | 167 ret, err := buildskia.DownloadSkia("", githash, checkout, b.depotTools,
false, deps) |
166 if err != nil { | 168 if err != nil { |
167 return nil, fmt.Errorf("Failed to fetch: %s", err) | 169 return nil, fmt.Errorf("Failed to fetch: %s", err) |
168 } | 170 } |
169 | 171 |
170 if err := buildLib(checkout, b.depotTools); err != nil { | 172 if err := buildLib(checkout, b.depotTools); err != nil { |
171 return nil, err | 173 return nil, err |
(...skipping 10 matching lines...) Expand all Loading... |
182 _, err = fmt.Fprintf(fb, "%s\n", githash) | 184 _, err = fmt.Fprintf(fb, "%s\n", githash) |
183 if err != nil { | 185 if err != nil { |
184 return nil, fmt.Errorf("Failed to write %s: %s", GOOD_BUILDS_FIL
ENAME, err) | 186 return nil, fmt.Errorf("Failed to write %s: %s", GOOD_BUILDS_FIL
ENAME, err) |
185 } | 187 } |
186 return ret, nil | 188 return ret, nil |
187 } | 189 } |
188 | 190 |
189 // updateCurrent updates the value of b.current with the new gitinfo for the mos
t recent build. | 191 // updateCurrent updates the value of b.current with the new gitinfo for the mos
t recent build. |
190 // | 192 // |
191 // Or a mildly informative stand-in if somehow the update fails. | 193 // Or a mildly informative stand-in if somehow the update fails. |
| 194 // |
| 195 // updateCurrent presumes the caller already has a lock on the mutex. |
192 func (b *Builder) updateCurrent() { | 196 func (b *Builder) updateCurrent() { |
193 allBuilds, err := b.AvailableBuilds() | |
194 b.mutex.Lock() | |
195 defer b.mutex.Unlock() | |
196 fallback := &vcsinfo.LongCommit{ShortCommit: &vcsinfo.ShortCommit{Hash:
"unknown"}} | 197 fallback := &vcsinfo.LongCommit{ShortCommit: &vcsinfo.ShortCommit{Hash:
"unknown"}} |
197 » if err != nil { | 198 » if len(b.hashes) == 0 { |
198 » » glog.Errorf("Failed to get list of available builds: %s", err) | 199 » » glog.Errorf("There are no hashes.") |
199 if b.current == nil { | 200 if b.current == nil { |
200 b.current = fallback | 201 b.current = fallback |
201 } | 202 } |
202 return | 203 return |
203 } | 204 } |
204 » details, err := b.repo.Details(allBuilds[0], true) | 205 » details, err := b.repo.Details(b.hashes[len(b.hashes)-1], true) |
205 if err != nil { | 206 if err != nil { |
206 glog.Errorf("Unable to retrieve build info: %s", err) | 207 glog.Errorf("Unable to retrieve build info: %s", err) |
207 if b.current == nil { | 208 if b.current == nil { |
208 b.current = fallback | 209 b.current = fallback |
209 } | 210 } |
210 return | 211 return |
211 } | 212 } |
212 b.current = details | 213 b.current = details |
213 } | 214 } |
214 | 215 |
215 // AvailableBuilds returns a list of git hashes, all the versions of Skia that | 216 // AvailableBuilds returns a list of git hashes, all the versions of Skia that |
216 // can be built against. This returns the list with the newest builds first. | 217 // can be built against. This returns the list with the newest builds last. |
217 // The list will always be of length > 1, otherwise and error is returned. | 218 // The list will always be of length > 1, otherwise and error is returned. |
218 func (b *Builder) AvailableBuilds() ([]string, error) { | 219 func (b *Builder) AvailableBuilds() ([]string, error) { |
219 b.mutex.Lock() | 220 b.mutex.Lock() |
220 defer b.mutex.Unlock() | 221 defer b.mutex.Unlock() |
221 if len(b.hashes) > 0 { | 222 if len(b.hashes) > 0 { |
222 return b.hashes, nil | 223 return b.hashes, nil |
223 } | 224 } |
224 fi, err := os.Open(filepath.Join(b.fiddleRoot, GOOD_BUILDS_FILENAME)) | 225 fi, err := os.Open(filepath.Join(b.fiddleRoot, GOOD_BUILDS_FILENAME)) |
225 if err != nil { | 226 if err != nil { |
226 return nil, fmt.Errorf("Failed to open %s for reading: %s", GOOD
_BUILDS_FILENAME, err) | 227 return nil, fmt.Errorf("Failed to open %s for reading: %s", GOOD
_BUILDS_FILENAME, err) |
227 } | 228 } |
228 defer util.Close(fi) | 229 defer util.Close(fi) |
229 buf, err := ioutil.ReadAll(fi) | 230 buf, err := ioutil.ReadAll(fi) |
230 if err != nil { | 231 if err != nil { |
231 return nil, fmt.Errorf("Failed to read: %s", err) | 232 return nil, fmt.Errorf("Failed to read: %s", err) |
232 } | 233 } |
233 hashes := strings.Split(string(buf), "\n") | 234 hashes := strings.Split(string(buf), "\n") |
234 » revHashes := []string{} | 235 » realHashes := []string{} |
235 » for i := len(hashes) - 1; i >= 0; i-- { | 236 » for _, h := range hashes { |
236 » » h := hashes[i] | |
237 if h != "" { | 237 if h != "" { |
238 » » » revHashes = append(revHashes, h) | 238 » » » realHashes = append(realHashes, h) |
239 } | 239 } |
240 } | 240 } |
241 » b.hashes = revHashes | 241 » b.hashes = realHashes |
242 if len(b.hashes) == 0 { | 242 if len(b.hashes) == 0 { |
243 return nil, fmt.Errorf("List of available builds is empty.") | 243 return nil, fmt.Errorf("List of available builds is empty.") |
244 } | 244 } |
245 » return revHashes, nil | 245 » return realHashes, nil |
246 } | 246 } |
247 | 247 |
248 func (b *Builder) Current() *vcsinfo.LongCommit { | 248 func (b *Builder) Current() *vcsinfo.LongCommit { |
249 b.mutex.Lock() | 249 b.mutex.Lock() |
250 defer b.mutex.Unlock() | 250 defer b.mutex.Unlock() |
251 return b.current | 251 return b.current |
252 } | 252 } |
253 | 253 |
254 // BuildLatestSkiaChromeBranch builds the most recent branch of Skia for Chrome | 254 // BuildLatestSkiaChromeBranch builds the most recent branch of Skia for Chrome |
255 // in the given fiddleRoot directory. | 255 // in the given fiddleRoot directory. |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
314 return branchName, res, nil | 314 return branchName, res, nil |
315 } | 315 } |
316 | 316 |
317 func (b *Builder) writeNewGoodBuilds(hashes []string) error { | 317 func (b *Builder) writeNewGoodBuilds(hashes []string) error { |
318 if len(hashes) < 1 { | 318 if len(hashes) < 1 { |
319 return fmt.Errorf("At least one good build must be kept around."
) | 319 return fmt.Errorf("At least one good build must be kept around."
) |
320 } | 320 } |
321 b.mutex.Lock() | 321 b.mutex.Lock() |
322 defer b.mutex.Unlock() | 322 defer b.mutex.Unlock() |
323 | 323 |
324 revHashes := []string{} | |
325 for i := len(hashes) - 1; i >= 0; i-- { | |
326 h := hashes[i] | |
327 if h != "" { | |
328 revHashes = append(revHashes, h) | |
329 } | |
330 } | |
331 b.hashes = hashes | 324 b.hashes = hashes |
332 fb, err := os.Create(filepath.Join(b.fiddleRoot, GOOD_BUILDS_FILENAME)) | 325 fb, err := os.Create(filepath.Join(b.fiddleRoot, GOOD_BUILDS_FILENAME)) |
333 if err != nil { | 326 if err != nil { |
334 return fmt.Errorf("Failed to open %s for writing: %s", GOOD_BUIL
DS_FILENAME, err) | 327 return fmt.Errorf("Failed to open %s for writing: %s", GOOD_BUIL
DS_FILENAME, err) |
335 } | 328 } |
336 defer util.Close(fb) | 329 defer util.Close(fb) |
337 » if _, err := fb.Write([]byte(strings.Join(revHashes, "\n") + "\n")); err
!= nil { | 330 » if _, err := fb.Write([]byte(strings.Join(hashes, "\n") + "\n")); err !=
nil { |
338 return fmt.Errorf("Failed to write %s: %s", GOOD_BUILDS_FILENAME
, err) | 331 return fmt.Errorf("Failed to write %s: %s", GOOD_BUILDS_FILENAME
, err) |
339 } | 332 } |
340 return nil | 333 return nil |
341 } | 334 } |
342 | 335 |
343 func (b *Builder) startDecimation() { | 336 func (b *Builder) startDecimation() { |
344 decimateLiveness := metrics2.NewLiveness("decimate") | 337 decimateLiveness := metrics2.NewLiveness("decimate") |
345 decimateFailures := metrics2.GetCounter("decimate-failed", nil) | 338 decimateFailures := metrics2.GetCounter("decimate-failed", nil) |
346 for _ = range time.Tick(DECIMATION_PERIOD) { | 339 for _ = range time.Tick(DECIMATION_PERIOD) { |
347 hashes, err := b.AvailableBuilds() | 340 hashes, err := b.AvailableBuilds() |
348 if err != nil { | 341 if err != nil { |
349 glog.Errorf("Failed to get available builds while decima
ting: %s", err) | 342 glog.Errorf("Failed to get available builds while decima
ting: %s", err) |
350 decimateFailures.Inc(1) | 343 decimateFailures.Inc(1) |
351 continue | 344 continue |
352 } | 345 } |
353 keep, remove, err := decimate(hashes, b.repo, PRESERVE_COUNT) | 346 keep, remove, err := decimate(hashes, b.repo, PRESERVE_COUNT) |
354 if err != nil { | 347 if err != nil { |
355 glog.Errorf("Failed to calc removals while decimating: %
s", err) | 348 glog.Errorf("Failed to calc removals while decimating: %
s", err) |
356 decimateFailures.Inc(1) | 349 decimateFailures.Inc(1) |
357 continue | 350 continue |
358 } | 351 } |
| 352 glog.Infof("Decimate: Keep %v Remove %v", keep, remove) |
359 for _, hash := range remove { | 353 for _, hash := range remove { |
360 glog.Infof("Decimate: Beginning %s", hash) | 354 glog.Infof("Decimate: Beginning %s", hash) |
361 if err := os.RemoveAll(filepath.Join(b.fiddleRoot, "vers
ions", hash)); err != nil { | 355 if err := os.RemoveAll(filepath.Join(b.fiddleRoot, "vers
ions", hash)); err != nil { |
362 glog.Errorf("Failed to remove directory for %s:
%s", hash, err) | 356 glog.Errorf("Failed to remove directory for %s:
%s", hash, err) |
363 continue | 357 continue |
364 } | 358 } |
365 glog.Infof("Decimate: Finished %s", hash) | 359 glog.Infof("Decimate: Finished %s", hash) |
366 } | 360 } |
367 if err := b.writeNewGoodBuilds(keep); err != nil { | 361 if err := b.writeNewGoodBuilds(keep); err != nil { |
368 continue | 362 continue |
369 } | 363 } |
370 decimateFailures.Reset() | 364 decimateFailures.Reset() |
371 decimateLiveness.Reset() | 365 decimateLiveness.Reset() |
372 } | 366 } |
373 } | 367 } |
374 | 368 |
375 // decimate returns a list of hashes to keep, the list to remove, | 369 // decimate returns a list of hashes to keep, the list to remove, |
376 // and an error if one occurred. | 370 // and an error if one occurred. |
377 // | 371 // |
378 // The algorithm is: | 372 // The algorithm is: |
379 // Preserve all hashes that are spaced one month apart. | 373 // Preserve all hashes that are spaced one month apart. |
380 // Then if there are more than 'limit' remaining hashes | 374 // Then if there are more than 'limit' remaining hashes |
381 // remove every other one to bring the count down to 'limit'/2. | 375 // remove every other one to bring the count down to 'limit'/2. |
382 // | 376 // |
383 func decimate(hashes []string, vcs vcsinfo.VCS, limit int) ([]string, []string,
error) { | 377 func decimate(hashes []string, vcs vcsinfo.VCS, limit int) ([]string, []string,
error) { |
384 keep := []string{} | 378 keep := []string{} |
385 remove := []string{} | 379 remove := []string{} |
386 | 380 |
387 » // The hashes are in reverse chronological order, so we start at the end | 381 » // The hashes are stored with the oldest first, newest last. |
388 » // and work back until we start to find hashes that are less than | 382 » // So we start at the front and work forward until we start to find hash
es that are less than |
389 » // PRESERVE_DURATION apart. Once we find that spot set oldiesBegin | 383 » // PRESERVE_DURATION apart. Once we find that spot set oldiesEnd |
390 // to that index. | 384 // to that index. |
391 » oldiesBegin := len(hashes) | 385 » oldiesEnd := 0 |
392 » c, err := vcs.Details(hashes[len(hashes)-1], true) | 386 » c, err := vcs.Details(hashes[0], true) |
393 if err != nil { | 387 if err != nil { |
394 return nil, nil, fmt.Errorf("Failed to get hash details: %s", er
r) | 388 return nil, nil, fmt.Errorf("Failed to get hash details: %s", er
r) |
395 } | 389 } |
396 » lastTS := c.Timestamp | 390 » lastTS := time.Time{} |
397 » for i := len(hashes) - 2; i > 0; i-- { | 391 » for i, h := range hashes { |
398 » » c, err := vcs.Details(hashes[i], true) | 392 » » c, err = vcs.Details(h, true) |
399 if err != nil { | 393 if err != nil { |
400 return nil, nil, fmt.Errorf("Failed to get hash details:
%s", err) | 394 return nil, nil, fmt.Errorf("Failed to get hash details:
%s", err) |
401 } | 395 } |
| 396 fmt.Printf("%v", c.Timestamp.Sub(lastTS)) |
402 if c.Timestamp.Sub(lastTS) < PRESERVE_DURATION { | 397 if c.Timestamp.Sub(lastTS) < PRESERVE_DURATION { |
403 glog.Infof("Decimation: Time %v between %q and %q", c.Ti
mestamp.Sub(lastTS), hashes[i], hashes[i+1]) | |
404 break | 398 break |
405 } | 399 } |
406 lastTS = c.Timestamp | 400 lastTS = c.Timestamp |
407 » » oldiesBegin = i | 401 » » oldiesEnd = i |
408 } | 402 } |
409 | 403 |
410 // Now that we know where the old hashes that we want to preserve are, w
e | 404 // Now that we know where the old hashes that we want to preserve are, w
e |
411 // will chop them off and ignore them for the rest of the decimation pro
cess. | 405 // will chop them off and ignore them for the rest of the decimation pro
cess. |
412 » oldies := hashes[oldiesBegin:] | 406 » oldies := hashes[:oldiesEnd] |
413 » hashes = hashes[:oldiesBegin] | 407 » hashes = hashes[oldiesEnd:] |
| 408 » fmt.Println(oldies, hashes) |
414 | 409 |
415 // Only do decimation if we have enough fresh hashes. | 410 // Only do decimation if we have enough fresh hashes. |
416 if len(hashes) < limit { | 411 if len(hashes) < limit { |
417 » » return append(hashes, oldies...), remove, nil | 412 » » return append(oldies, hashes...), remove, nil |
418 } | 413 } |
419 » for i, h := range hashes { | 414 » last := hashes[len(hashes)-1] |
| 415 » for i, h := range hashes[:len(hashes)-1] { |
420 if i%2 == 0 { | 416 if i%2 == 0 { |
421 keep = append(keep, h) | 417 keep = append(keep, h) |
422 } else { | 418 } else { |
423 remove = append(remove, h) | 419 remove = append(remove, h) |
424 } | 420 } |
425 } | 421 } |
| 422 keep = append(keep, last) |
426 // Once done with decimation add the oldies back into the list of hashes
to keep. | 423 // Once done with decimation add the oldies back into the list of hashes
to keep. |
427 » return append(keep, oldies...), remove, nil | 424 » return append(oldies, keep...), remove, nil |
428 } | 425 } |
OLD | NEW |