| author | |
| committer | |
| log | 15f00d1756f1503da29151cef5213858f6667f4f |
| tree | 2413bbf446f11686fb426d34d79899a94521f2b9 |
| parent | 580135241b9618fdce6f4acca390e41d14e4b937 |
Occasionally, two different Zig processes are waiting on the same
compilation artifacts. For example, if the user runs `zig build` for the
first time in two different terminals with the same version of Zig, one
of them will win the race on compiler_rt and start building it, while
the other process waits. Then they both use the same cache artifacts.
Now, the fact that this is happening is communicated to the user by
displaying it in a progress node during the checking for cache hit.15 files changed, 39 insertions(+), 37 deletions(-)
lib/compiler/Maker.zig+1-1| ... | ... | @@ -1216,7 +1216,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { |
| 1216 | 1216 | defer compile_prog_node.end(); |
| 1217 | 1217 | |
| 1218 | 1218 | switch (options.cache_poison) { |
| 1219 | .pure, .disallowed, .ignored => if (try config_man.hit()) { | |
| 1219 | .pure, .disallowed, .ignored => if (try config_man.hit(compile_prog_node)) { | |
| 1220 | 1220 | const digest = config_man.final(); |
| 1221 | 1221 | break :cp .{ |
| 1222 | 1222 | .{ |
lib/compiler/Maker/Step.zig+6-6| ... | ... | @@ -711,10 +711,10 @@ pub fn handleChildProcessTerm(s: *Step, maker: *Maker, term: std.process.Child.T |
| 711 | 711 | if (!term.success()) return s.fail(maker, "process {f}", .{term}); |
| 712 | 712 | } |
| 713 | 713 | |
| 714 | /// Prefer `cacheHitAndWatch` unless you already added watch inputs | |
| 714 | /// Prefer `cacheHitWatched` unless you already added watch inputs | |
| 715 | 715 | /// separately from using the cache system. |
| 716 | pub fn cacheHit(s: *Step, maker: *Maker, man: *Cache.Manifest) !bool { | |
| 717 | s.result_cached = man.hit() catch |err| return failWithCacheError(s, maker, man, err); | |
| 716 | pub fn cacheHit(s: *Step, maker: *Maker, man: *Cache.Manifest, parent_progress_node: std.Progress.Node) !bool { | |
| 717 | s.result_cached = man.hit(parent_progress_node) catch |err| return failWithCacheError(s, maker, man, err); | |
| 718 | 718 | return s.result_cached; |
| 719 | 719 | } |
| 720 | 720 | |
| ... | ... | @@ -722,8 +722,8 @@ pub fn cacheHit(s: *Step, maker: *Maker, man: *Cache.Manifest) !bool { |
| 722 | 722 | /// the full set of files picked up by the cache manifest. |
| 723 | 723 | /// |
| 724 | 724 | /// Must be accompanied with `writeManifestAndWatch`. |
| 725 | pub fn cacheHitAndWatch(s: *Step, maker: *Maker, man: *Cache.Manifest) !bool { | |
| 726 | const is_hit = man.hit() catch |err| return failWithCacheError(s, maker, man, err); | |
| 725 | pub fn cacheHitWatched(s: *Step, maker: *Maker, man: *Cache.Manifest, parent_progress_node: std.Progress.Node) !bool { | |
| 726 | const is_hit = man.hit(parent_progress_node) catch |err| return failWithCacheError(s, maker, man, err); | |
| 727 | 727 | s.result_cached = is_hit; |
| 728 | 728 | // The above call to hit() populates the manifest with files, so in case of |
| 729 | 729 | // a hit, we need to populate watch inputs. |
| ... | ... | @@ -770,7 +770,7 @@ pub fn writeManifest(s: *Step, maker: *Maker, man: *Cache.Manifest) !void { |
| 770 | 770 | /// Clears previous watch inputs, if any, and then populates watch inputs from |
| 771 | 771 | /// the full set of files picked up by the cache manifest. |
| 772 | 772 | /// |
| 773 | /// Must be accompanied with `cacheHitAndWatch`. | |
| 773 | /// Must be accompanied with `cacheHitWatched`. | |
| 774 | 774 | pub fn writeManifestAndWatch(s: *Step, maker: *Maker, man: *Cache.Manifest) !void { |
| 775 | 775 | try writeManifest(s, maker, man); |
| 776 | 776 | try setWatchInputsFromManifest(s, maker, man); |
lib/compiler/Maker/Step/ConfigHeader.zig+1-2| ... | ... | @@ -27,7 +27,6 @@ pub fn make( |
| 27 | 27 | progress_node: std.Progress.Node, |
| 28 | 28 | ) Step.ExtendedMakeError!void { |
| 29 | 29 | _ = config_header; |
| 30 | _ = progress_node; | |
| 31 | 30 | const graph = maker.graph; |
| 32 | 31 | const step = maker.stepByIndex(step_index); |
| 33 | 32 | const io = graph.io; |
| ... | ... | @@ -111,7 +110,7 @@ pub fn make( |
| 111 | 110 | const output = aw.written(); |
| 112 | 111 | man.hash.addBytes(output); |
| 113 | 112 | |
| 114 | if (try step.cacheHit(maker, &man)) { | |
| 113 | if (try step.cacheHit(maker, &man, progress_node)) { | |
| 115 | 114 | const digest = man.final(); |
| 116 | 115 | maker.generatedPath(conf_ch.generated_dir).* = .{ |
| 117 | 116 | .root_dir = cache_root, |
lib/compiler/Maker/Step/ObjCopy.zig+1-1| ... | ... | @@ -46,7 +46,7 @@ pub fn make( |
| 46 | 46 | |
| 47 | 47 | const basename = opt_basename orelse Io.Dir.path.basename(input_path.sub_path); |
| 48 | 48 | |
| 49 | if (try step.cacheHit(maker, &man)) { | |
| 49 | if (try step.cacheHit(maker, &man, progress_node)) { | |
| 50 | 50 | // Cache hit, skip subprocess execution. |
| 51 | 51 | const digest = man.final(); |
| 52 | 52 | maker.generatedPath(conf_oc.output_file).* = .{ |
lib/compiler/Maker/Step/Options.zig+1-4| ... | ... | @@ -16,9 +16,6 @@ pub fn make( |
| 16 | 16 | ) Step.ExtendedMakeError!void { |
| 17 | 17 | _ = options; |
| 18 | 18 | |
| 19 | // This step completes so quickly that no progress reporting is necessary. | |
| 20 | _ = progress_node; | |
| 21 | ||
| 22 | 19 | const graph = maker.graph; |
| 23 | 20 | const step = maker.stepByIndex(step_index); |
| 24 | 21 | const io = graph.io; |
| ... | ... | @@ -56,7 +53,7 @@ pub fn make( |
| 56 | 53 | |
| 57 | 54 | const basename = "options.zig"; |
| 58 | 55 | |
| 59 | if (try step.cacheHitAndWatch(maker, &man)) { | |
| 56 | if (try step.cacheHitWatched(maker, &man, progress_node)) { | |
| 60 | 57 | const digest = man.final(); |
| 61 | 58 | maker.generatedPath(conf_options.generated_file).* = .{ |
| 62 | 59 | .root_dir = cache_root, |
lib/compiler/Maker/Step/Run.zig+1-1| ... | ... | @@ -254,7 +254,7 @@ pub fn make( |
| 254 | 254 | .check, .zig_test => false, |
| 255 | 255 | }; |
| 256 | 256 | |
| 257 | if (!has_side_effects and try step.cacheHitAndWatch(maker, &man)) { | |
| 257 | if (!has_side_effects and try step.cacheHitWatched(maker, &man, progress_node)) { | |
| 258 | 258 | // Cache hit; skip running command. |
| 259 | 259 | const digest = man.final(); |
| 260 | 260 | try populateGeneratedStdIo(maker, &conf_run, cache_root, &digest); |
lib/compiler/Maker/Step/WriteFile.zig+1-1| ... | ... | @@ -104,7 +104,7 @@ pub fn make( |
| 104 | 104 | } |
| 105 | 105 | } |
| 106 | 106 | |
| 107 | if (try step.cacheHit(maker, &man)) { | |
| 107 | if (try step.cacheHit(maker, &man, progress_node)) { | |
| 108 | 108 | const digest = man.final(); |
| 109 | 109 | maker.generatedPath(conf_wf.generated_directory).* = .{ |
| 110 | 110 | .root_dir = cache_root, |
lib/std/Build/Cache.zig+16-10| ... | ... | @@ -501,7 +501,13 @@ pub const Manifest = struct { |
| 501 | 501 | /// The lock on the manifest file is released when `deinit` is called. As another |
| 502 | 502 | /// option, one may call `toOwnedLock` to obtain a smaller object which can represent |
| 503 | 503 | /// the lock. `deinit` is safe to call whether or not `toOwnedLock` has been called. |
| 504 | pub fn hit(self: *Manifest) HitError!bool { | |
| 504 | pub fn hit(man: *Manifest, parent_progress_node: std.Progress.Node) HitError!bool { | |
| 505 | const node = parent_progress_node.start("Reusing Cache Artifacts", 0); | |
| 506 | defer node.end(); | |
| 507 | return hitInner(man); | |
| 508 | } | |
| 509 | ||
| 510 | pub fn hitInner(self: *Manifest) HitError!bool { | |
| 505 | 511 | assert(self.manifest_file == null); |
| 506 | 512 | |
| 507 | 513 | self.diagnostic = .none; |
| ... | ... | @@ -1380,7 +1386,7 @@ test "cache file and then recall it" { |
| 1380 | 1386 | _ = try ch.addFile(temp_file, null); |
| 1381 | 1387 | |
| 1382 | 1388 | // There should be nothing in the cache |
| 1383 | try testing.expectEqual(false, try ch.hit()); | |
| 1389 | try testing.expectEqual(false, try ch.hit(.none)); | |
| 1384 | 1390 | |
| 1385 | 1391 | digest1 = ch.final(); |
| 1386 | 1392 | try ch.writeManifest(); |
| ... | ... | @@ -1395,7 +1401,7 @@ test "cache file and then recall it" { |
| 1395 | 1401 | _ = try ch.addFile(temp_file, null); |
| 1396 | 1402 | |
| 1397 | 1403 | // Cache hit! We just "built" the same file |
| 1398 | try testing.expect(try ch.hit()); | |
| 1404 | try testing.expect(try ch.hit(.none)); | |
| 1399 | 1405 | digest2 = ch.final(); |
| 1400 | 1406 | |
| 1401 | 1407 | try testing.expectEqual(false, ch.have_exclusive_lock); |
| ... | ... | @@ -1448,7 +1454,7 @@ test "check that changing a file makes cache fail" { |
| 1448 | 1454 | const temp_file_idx = try ch.addFile(temp_file, 100); |
| 1449 | 1455 | |
| 1450 | 1456 | // There should be nothing in the cache |
| 1451 | try testing.expectEqual(false, try ch.hit()); | |
| 1457 | try testing.expectEqual(false, try ch.hit(.none)); | |
| 1452 | 1458 | |
| 1453 | 1459 | try testing.expect(mem.eql(u8, original_temp_file_contents, ch.files.keys()[temp_file_idx].contents.?)); |
| 1454 | 1460 | |
| ... | ... | @@ -1467,7 +1473,7 @@ test "check that changing a file makes cache fail" { |
| 1467 | 1473 | const temp_file_idx = try ch.addFile(temp_file, 100); |
| 1468 | 1474 | |
| 1469 | 1475 | // A file that we depend on has been updated, so the cache should not contain an entry for it |
| 1470 | try testing.expectEqual(false, try ch.hit()); | |
| 1476 | try testing.expectEqual(false, try ch.hit(.none)); | |
| 1471 | 1477 | |
| 1472 | 1478 | // The cache system does not keep the contents of re-hashed input files. |
| 1473 | 1479 | try testing.expect(ch.files.keys()[temp_file_idx].contents == null); |
| ... | ... | @@ -1511,7 +1517,7 @@ test "no file inputs" { |
| 1511 | 1517 | man.hash.addBytes("1234"); |
| 1512 | 1518 | |
| 1513 | 1519 | // There should be nothing in the cache |
| 1514 | try testing.expectEqual(false, try man.hit()); | |
| 1520 | try testing.expectEqual(false, try man.hit(.none)); | |
| 1515 | 1521 | |
| 1516 | 1522 | digest1 = man.final(); |
| 1517 | 1523 | |
| ... | ... | @@ -1523,7 +1529,7 @@ test "no file inputs" { |
| 1523 | 1529 | |
| 1524 | 1530 | man.hash.addBytes("1234"); |
| 1525 | 1531 | |
| 1526 | try testing.expect(try man.hit()); | |
| 1532 | try testing.expect(try man.hit(.none)); | |
| 1527 | 1533 | digest2 = man.final(); |
| 1528 | 1534 | try testing.expectEqual(false, man.have_exclusive_lock); |
| 1529 | 1535 | } |
| ... | ... | @@ -1575,7 +1581,7 @@ test "Manifest with files added after initial hash work" { |
| 1575 | 1581 | _ = try ch.addFile(temp_file1, null); |
| 1576 | 1582 | |
| 1577 | 1583 | // There should be nothing in the cache |
| 1578 | try testing.expectEqual(false, try ch.hit()); | |
| 1584 | try testing.expectEqual(false, try ch.hit(.none)); | |
| 1579 | 1585 | |
| 1580 | 1586 | _ = try ch.addFilePost(temp_file2); |
| 1581 | 1587 | |
| ... | ... | @@ -1589,7 +1595,7 @@ test "Manifest with files added after initial hash work" { |
| 1589 | 1595 | ch.hash.addBytes("1234"); |
| 1590 | 1596 | _ = try ch.addFile(temp_file1, null); |
| 1591 | 1597 | |
| 1592 | try testing.expect(try ch.hit()); | |
| 1598 | try testing.expect(try ch.hit(.none)); | |
| 1593 | 1599 | digest2 = ch.final(); |
| 1594 | 1600 | |
| 1595 | 1601 | try testing.expectEqual(false, ch.have_exclusive_lock); |
| ... | ... | @@ -1613,7 +1619,7 @@ test "Manifest with files added after initial hash work" { |
| 1613 | 1619 | _ = try ch.addFile(temp_file1, null); |
| 1614 | 1620 | |
| 1615 | 1621 | // A file that we depend on has been updated, so the cache should not contain an entry for it |
| 1616 | try testing.expectEqual(false, try ch.hit()); | |
| 1622 | try testing.expectEqual(false, try ch.hit(.none)); | |
| 1617 | 1623 | |
| 1618 | 1624 | _ = try ch.addFilePost(temp_file2); |
| 1619 | 1625 |
src/Compilation.zig+5-5| ... | ... | @@ -2755,7 +2755,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE |
| 2755 | 2755 | man.want_shared_lock = false; |
| 2756 | 2756 | } |
| 2757 | 2757 | |
| 2758 | const is_hit = man.hit() catch |err| switch (err) { | |
| 2758 | const is_hit = man.hit(main_progress_node) catch |err| switch (err) { | |
| 2759 | 2759 | error.CacheCheckFailed => switch (man.diagnostic) { |
| 2760 | 2760 | .none => unreachable, |
| 2761 | 2761 | .manifest_create, .manifest_read, .manifest_lock => |e| return comp.setMiscFailure( |
| ... | ... | @@ -5435,7 +5435,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr |
| 5435 | 5435 | const target = comp.getTarget(); |
| 5436 | 5436 | assert(target.ofmt != .c); |
| 5437 | 5437 | const o_ext = target.ofmt.fileExt(target.cpu.arch); |
| 5438 | const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: { | |
| 5438 | const digest = if (!comp.disable_c_depfile and try man.hit(child_progress_node)) man.final() else blk: { | |
| 5439 | 5439 | var argv: std.array_list.Managed([]const u8) = .init(gpa); |
| 5440 | 5440 | defer argv.deinit(); |
| 5441 | 5441 | |
| ... | ... | @@ -5696,7 +5696,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr |
| 5696 | 5696 | } |
| 5697 | 5697 | |
| 5698 | 5698 | // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. |
| 5699 | if (comp.disable_c_depfile) _ = try man.hit(); | |
| 5699 | if (comp.disable_c_depfile) _ = try man.hit(child_progress_node); | |
| 5700 | 5700 | |
| 5701 | 5701 | // Rename into place. |
| 5702 | 5702 | const digest = man.final(); |
| ... | ... | @@ -5784,7 +5784,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 |
| 5784 | 5784 | const rc_basename = try std.fmt.allocPrint(arena, "{s}.rc", .{src_basename}); |
| 5785 | 5785 | const res_basename = try std.fmt.allocPrint(arena, "{s}.res", .{src_basename}); |
| 5786 | 5786 | |
| 5787 | const digest = if (try man.hit()) man.final() else blk: { | |
| 5787 | const digest = if (try man.hit(child_progress_node)) man.final() else blk: { | |
| 5788 | 5788 | // The digest only depends on the .manifest file, so we can |
| 5789 | 5789 | // get the digest now and write the .res directly to the cache |
| 5790 | 5790 | const digest = man.final(); |
| ... | ... | @@ -5877,7 +5877,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 |
| 5877 | 5877 | |
| 5878 | 5878 | const rc_basename_noext = src_basename[0 .. src_basename.len - fs.path.extension(src_basename).len]; |
| 5879 | 5879 | |
| 5880 | const digest = if (try man.hit()) man.final() else blk: { | |
| 5880 | const digest = if (try man.hit(child_progress_node)) man.final() else blk: { | |
| 5881 | 5881 | var zig_cache_tmp_dir = try comp.dirs.local_cache.handle.createDirPathOpen(io, "tmp", .{}); |
| 5882 | 5882 | defer zig_cache_tmp_dir.close(io); |
| 5883 | 5883 |
src/libs/freebsd.zig+1-1| ... | ... | @@ -462,7 +462,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye |
| 462 | 462 | const full_abilists_path = try comp.dirs.zig_lib.join(arena, &.{abilists_path}); |
| 463 | 463 | const abilists_index = try man.addFile(full_abilists_path, abilists_max_size); |
| 464 | 464 | |
| 465 | if (try man.hit()) { | |
| 465 | if (try man.hit(prog_node)) { | |
| 466 | 466 | const digest = man.final(); |
| 467 | 467 | |
| 468 | 468 | return queueSharedObjects(comp, .{ |
src/libs/glibc.zig+1-1| ... | ... | @@ -703,7 +703,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye |
| 703 | 703 | const full_abilists_path = try comp.dirs.zig_lib.join(arena, &.{abilists_path}); |
| 704 | 704 | const abilists_index = try man.addFile(full_abilists_path, abilists_max_size); |
| 705 | 705 | |
| 706 | if (try man.hit()) { | |
| 706 | if (try man.hit(prog_node)) { | |
| 707 | 707 | const digest = man.final(); |
| 708 | 708 | |
| 709 | 709 | return queueSharedObjects(comp, .{ |
src/libs/mingw.zig+1-1| ... | ... | @@ -255,7 +255,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8, prog_node: std.P |
| 255 | 255 | const final_lib_basename = try std.fmt.allocPrint(gpa, "{s}.lib", .{lib_name}); |
| 256 | 256 | errdefer gpa.free(final_lib_basename); |
| 257 | 257 | |
| 258 | if (try man.hit()) { | |
| 258 | if (try man.hit(prog_node)) { | |
| 259 | 259 | const digest = man.final(); |
| 260 | 260 | const sub_path = try std.fs.path.join(gpa, &.{ "o", &digest, final_lib_basename }); |
| 261 | 261 | errdefer gpa.free(sub_path); |
src/libs/netbsd.zig+1-1| ... | ... | @@ -409,7 +409,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye |
| 409 | 409 | const full_abilists_path = try comp.dirs.zig_lib.join(arena, &.{abilists_path}); |
| 410 | 410 | const abilists_index = try man.addFile(full_abilists_path, abilists_max_size); |
| 411 | 411 | |
| 412 | if (try man.hit()) { | |
| 412 | if (try man.hit(prog_node)) { | |
| 413 | 413 | const digest = man.final(); |
| 414 | 414 | |
| 415 | 415 | return queueSharedObjects(comp, .{ |
src/libs/openbsd.zig+1-1| ... | ... | @@ -332,7 +332,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye |
| 332 | 332 | const full_abilists_path = try comp.dirs.zig_lib.join(arena, &.{abilists_path}); |
| 333 | 333 | const abilists_index = try man.addFile(full_abilists_path, abilists_max_size); |
| 334 | 334 | |
| 335 | if (try man.hit()) { | |
| 335 | if (try man.hit(prog_node)) { | |
| 336 | 336 | const digest = man.final(); |
| 337 | 337 | |
| 338 | 338 | return queueSharedObjects(comp, .{ |
src/main.zig+1-1| ... | ... | @@ -4792,7 +4792,7 @@ fn cmdTranslateC( |
| 4792 | 4792 | Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| |
| 4793 | 4793 | fatal("unable to process {q}: {t}", .{ c_source_file.src_path, err }); |
| 4794 | 4794 | |
| 4795 | const result: Compilation.TranslateCResult = if (try man.hit()) .{ | |
| 4795 | const result: Compilation.TranslateCResult = if (try man.hit(prog_node)) .{ | |
| 4796 | 4796 | .digest = man.finalBin(), |
| 4797 | 4797 | .cache_hit = true, |
| 4798 | 4798 | .errors = std.zig.ErrorBundle.empty, |