| ... | @@ -294,7 +294,7 @@ pub const Manifest = struct { | ... | @@ -294,7 +294,7 @@ pub const Manifest = struct { |
| 294 | files: Files = .empty, | 294 | files: Files = .empty, |
| 295 | /// Indexes line up with `files`, but only up until `hit` is called. Uses | 295 | /// Indexes line up with `files`, but only up until `hit` is called. Uses |
| 296 | /// `Cache.gpa`. | 296 | /// `Cache.gpa`. |
| 297 | input_files: std.ArrayList(InputFile) = .empty, | 297 | input_paths: std.ArrayList(InputPath) = .empty, |
| 298 | diagnostic: Diagnostic = .none, | 298 | diagnostic: Diagnostic = .none, |
| 299 | /// Keeps track of the last time we performed a file system write to observe | 299 | /// Keeps track of the last time we performed a file system write to observe |
| 300 | /// what time the file system thinks it is, according to its own granularity. | 300 | /// what time the file system thinks it is, according to its own granularity. |
| ... | @@ -304,11 +304,11 @@ pub const Manifest = struct { | ... | @@ -304,11 +304,11 @@ pub const Manifest = struct { |
| 304 | /// final terminating byte can be added without allocation. Uses | 304 | /// final terminating byte can be added without allocation. Uses |
| 305 | /// `Cache.gpa`. | 305 | /// `Cache.gpa`. |
| 306 | contents: std.ArrayList(u8) = .empty, | 306 | contents: std.ArrayList(u8) = .empty, |
| 307 | /// All contents from all `input_files` whose contents were requested, | 307 | /// All contents from all `input_paths` whose contents were requested, |
| 308 | /// concatenated. Total byte size will be less than `max_input_content_len` | 308 | /// concatenated. Total byte size will be less than `max_input_content_len` |
| 309 | /// otherwise an error is returned. | 309 | /// otherwise an error is returned. |
| 310 | /// | 310 | /// |
| 311 | /// Data is invalidated when `addFilePost` is called. | 311 | /// Data is invalidated when `addPathPost` is called. |
| 312 | all_input_content: std.ArrayList(u8) = .empty, | 312 | all_input_content: std.ArrayList(u8) = .empty, |
| 313 | max_input_content_len: usize = std.math.maxInt(u32), | 313 | max_input_content_len: usize = std.math.maxInt(u32), |
| 314 | | 314 | |
| ... | @@ -328,23 +328,24 @@ pub const Manifest = struct { | ... | @@ -328,23 +328,24 @@ pub const Manifest = struct { |
| 328 | InvalidFormat, | 328 | InvalidFormat, |
| 329 | } || Allocator.Error || Io.Cancelable; | 329 | } || Allocator.Error || Io.Cancelable; |
| 330 | | 330 | |
| 331 | fn fail(c: *Check, m: *Manifest, diagnostic: Diagnostic) void { | 331 | fn fail(c: *Check, m: *Manifest, diagnostic: Diagnostic) error{CacheCheckFailed} { |
| 332 | if (!@atomicRmw(bool, &c.diagnostic_lock, .Xchg, true, .unordered)) { | 332 | if (!@atomicRmw(bool, &c.diagnostic_lock, .Xchg, true, .monotonic)) { |
| 333 | m.diagnostic = diagnostic; | 333 | m.diagnostic = diagnostic; |
| 334 | } | 334 | } |
| | 335 | return error.CacheCheckFailed; |
| 335 | } | 336 | } |
| 336 | }; | 337 | }; |
| 337 | | 338 | |
| 338 | pub const Files = std.array_hash_map.Custom(File.Offset, void, File.HashContext, false); | 339 | pub const Files = std.array_hash_map.Custom(File.Offset, void, File.HashContext, false); |
| 339 | | 340 | |
| 340 | /// Source files whose prefix and relative path are included when computing | 341 | /// Source files and directories whose prefix and relative path are |
| 341 | /// the cache manifest digest. It's the information needed to lazily hash | 342 | /// included when computing the cache manifest digest. It's the information |
| 342 | /// the input files only when a cache miss occurs. | 343 | /// needed to lazily hash the input files only when a cache miss occurs. |
| 343 | /// | 344 | /// |
| 344 | /// `File.prefix`, `File.path`, and `File.mode` will be always populated, | 345 | /// `File.prefix`, `File.path`, and `File.mode` will be always populated, |
| 345 | /// but the other fields of `File` will be populated depending on the | 346 | /// but the other fields of `File` will be populated depending on the |
| 346 | /// fields of `InputFile`. | 347 | /// fields of `InputPath`. |
| 347 | pub const InputFile = struct { | 348 | pub const InputPath = struct { |
| 348 | request_handle: bool, | 349 | request_handle: bool, |
| 349 | have_handle: bool, | 350 | have_handle: bool, |
| 350 | /// Determines whether `File.size`, `File.inode`, and `File.mtime` are populated. | 351 | /// Determines whether `File.size`, `File.inode`, and `File.mtime` are populated. |
| ... | @@ -360,7 +361,7 @@ pub const Manifest = struct { | ... | @@ -360,7 +361,7 @@ pub const Manifest = struct { |
| 360 | /// `have_handle` determines whether this is populated. | 361 | /// `have_handle` determines whether this is populated. |
| 361 | handle: Io.File, | 362 | handle: Io.File, |
| 362 | | 363 | |
| 363 | /// Index into `Manifest.input_files`. | 364 | /// Index into `Manifest.input_paths`. |
| 364 | pub const Index = enum(u32) { | 365 | pub const Index = enum(u32) { |
| 365 | _, | 366 | _, |
| 366 | }; | 367 | }; |
| ... | @@ -406,13 +407,13 @@ pub const Manifest = struct { | ... | @@ -406,13 +407,13 @@ pub const Manifest = struct { |
| 406 | pub const Offset = enum(u32) { | 407 | pub const Offset = enum(u32) { |
| 407 | _, | 408 | _, |
| 408 | | 409 | |
| 409 | pub fn get(offset: Offset, m: *const Manifest) *File { | 410 | pub fn get(offset: Offset, contents: []u8) *File { |
| 410 | return @ptrCast(m.contents.items[@backingInt(offset)..][0..@sizeOf(File)]); | 411 | return @ptrCast(@alignCast(contents.items[@backingInt(offset)..][0..@sizeOf(File)])); |
| 411 | } | 412 | } |
| 412 | | 413 | |
| 413 | pub fn getFallible(offset: Offset, m: *const Manifest) error{EndOfStream}!*File { | 414 | pub fn getFallible(offset: Offset, contents: []u8) error{InvalidFormat}!*File { |
| 414 | if (@backingInt(offset) + @sizeOf(File) >= m.contents.len) return error.EndOfStream; | 415 | if (@backingInt(offset) + @sizeOf(File) >= contents.items.len) return error.InvalidFormat; |
| 415 | return get(offset, m); | 416 | return get(offset, contents); |
| 416 | } | 417 | } |
| 417 | }; | 418 | }; |
| 418 | | 419 | |
| ... | @@ -432,25 +433,6 @@ pub const Manifest = struct { | ... | @@ -432,25 +433,6 @@ pub const Manifest = struct { |
| 432 | } | 433 | } |
| 433 | }; | 434 | }; |
| 434 | | 435 | |
| 435 | pub fn path(file: *const File) [:0]const u8 { | | |
| 436 | return pathFallible(file) catch unreachable; | | |
| 437 | } | | |
| 438 | | | |
| 439 | pub fn pathFallible(file: *const File) error{EndOfStream}![:0]const u8 { | | |
| 440 | const ptr: [*]u8 = &file.path_start; | | |
| 441 | const len = mem.findScalar(u8, ptr, 0) orelse return error.EndOfStream; | | |
| 442 | return ptr[0..len :0]; | | |
| 443 | } | | |
| 444 | | | |
| 445 | fn manifestDigestHash(file: *const File, hasher: *Hasher) void { | | |
| 446 | const path_ptr: [*]u8 = &file.path_start; | | |
| 447 | const path_len = mem.findScalar(u8, path_ptr, 0).?; | | |
| 448 | comptime assert(@offsetOf(File, "path_start") - @offsetOf(File, "flags") == 1); | | |
| 449 | // Includes flags and sentinel. | | |
| 450 | const hash_string = (path_ptr - 1)[0 .. path_len + 2]; | | |
| 451 | hasher.update(hash_string); | | |
| 452 | } | | |
| 453 | | | |
| 454 | fn setStat(file: *File, m: *Manifest, stat: Stat) Io.Cancelable!void { | 436 | fn setStat(file: *File, m: *Manifest, stat: Stat) Io.Cancelable!void { |
| 455 | file.size = stat.size; | 437 | file.size = stat.size; |
| 456 | file.inode = stat.inode; | 438 | file.inode = stat.inode; |
| ... | @@ -490,6 +472,16 @@ pub const Manifest = struct { | ... | @@ -490,6 +472,16 @@ pub const Manifest = struct { |
| 490 | pub const FileOp = struct { | 472 | pub const FileOp = struct { |
| 491 | file_offset: File.Offset, | 473 | file_offset: File.Offset, |
| 492 | err: anyerror, | 474 | err: anyerror, |
| | 475 | |
| | 476 | /// Returned `Path` references `Manifest.contents`. |
| | 477 | pub fn path(fo: FileOp, manifest: *const Manifest) Path { |
| | 478 | const contents = manifest.contents.items; |
| | 479 | const prefix = fo.file_offset.get(contents).flags.prefix; |
| | 480 | return .{ |
| | 481 | .root_dir = manifest.cache.prefixes()[prefix], |
| | 482 | .sub_path = filePath(contents, fo.file_offset), |
| | 483 | }; |
| | 484 | } |
| 493 | }; | 485 | }; |
| 494 | }; | 486 | }; |
| 495 | | 487 | |
| ... | @@ -499,42 +491,49 @@ pub const Manifest = struct { | ... | @@ -499,42 +491,49 @@ pub const Manifest = struct { |
| 499 | mtime: Io.Timestamp, | 491 | mtime: Io.Timestamp, |
| 500 | }; | 492 | }; |
| 501 | | 493 | |
| 502 | pub const AddInputFileOptions = struct { | 494 | pub const PathHandle = union(enum) { |
| 503 | /// If `is_directory` is true, this handle must be opened with | 495 | file: ?Io.File, |
| 504 | /// iteration capability. | 496 | /// If provided, this handle must be opened with iteration capability. |
| 505 | handle: ?Io.File = null, | 497 | dir: ?Io.Dir, |
| | 498 | }; |
| | 499 | |
| | 500 | pub const AddInputPathOptions = struct { |
| | 501 | handle: PathHandle = .{ .file = null }, |
| 506 | stat: ?Stat = null, | 502 | stat: ?Stat = null, |
| 507 | request_handle: bool = false, | 503 | request_handle: bool = false, |
| 508 | /// Can request file or directory contents depending on `is_directory`. | 504 | /// Can request file or directory contents depending on `handle`. |
| 509 | request_contents: bool = false, | 505 | request_contents: bool = false, |
| 510 | /// Contents of a directory are considered to be the sorted list of | | |
| 511 | /// file names of direct entries, separated by null byte. Each file name | | |
| 512 | /// is prefixed by `Io.File.Kind` byte, +1 so that the zero tag is | | |
| 513 | /// not aliased by the entry separator. | | |
| 514 | is_directory: bool = false, | | |
| 515 | /// Content hashing skipped; any difference in metadata implies cache | 506 | /// Content hashing skipped; any difference in metadata implies cache |
| 516 | /// miss. | 507 | /// miss. |
| 517 | metadata_only: bool = false, | 508 | metadata_only: bool = false, |
| 518 | }; | 509 | }; |
| 519 | | 510 | |
| 520 | pub const AddInputFileError = error{ | 511 | pub const AddInputPathError = error{ |
| 521 | /// The same file path has been added to the cache manifest both as a | 512 | /// The same file path has been added to the cache manifest both as a |
| 522 | /// directory and as a normal file, making the intended caching | 513 | /// directory and as a normal file, making the intended caching |
| 523 | /// behavior ambiguous. | 514 | /// behavior ambiguous. |
| 524 | IsDirectoryAmbiguous, | 515 | IsDirectoryAmbiguous, |
| 525 | } || Allocator.Error; | 516 | } || Allocator.Error; |
| 526 | | 517 | |
| 527 | /// Add a file as a dependency of process being cached. When `hit` is | 518 | /// Add a file or directory path as a dependency of process being cached. |
| 528 | /// called, the file's contents will be checked to ensure that it matches | 519 | /// When `hit` is called, the contents will be checked to ensure |
| 529 | /// the contents from previous times. | 520 | /// that it matches the contents from previous times. |
| 530 | /// | 521 | /// |
| 531 | /// The contents of the input file may be requested and subsequently | 522 | /// The contents of the input file may be requested and subsequently |
| 532 | /// obtained via methods of the returned `InputFile.Index` after calling | 523 | /// obtained via methods of the returned `InputPath.Index` after calling |
| 533 | /// `hit`. | 524 | /// `hit`. |
| 534 | pub fn addInputFile(m: *Manifest, path: Path, options: AddInputFileOptions) Allocator.Error!InputFile.Index { | 525 | /// |
| | 526 | /// Contents of a directory are considered to be the sorted list of file |
| | 527 | /// names of direct entries, separated by null byte. Each file name is |
| | 528 | /// prefixed by `Io.File.Kind` byte, +1 so that the zero tag is not aliased |
| | 529 | /// by the entry separator. |
| | 530 | /// |
| | 531 | /// See also: |
| | 532 | /// * `addPathPost` |
| | 533 | pub fn addInputPath(m: *Manifest, path: Path, options: AddInputPathOptions) AddInputPathError!InputPath.Index { |
| 535 | const gpa = m.cache.gpa; | 534 | const gpa = m.cache.gpa; |
| 536 | try m.files.ensureUnusedCapacity(gpa, 1); | 535 | try m.files.ensureUnusedCapacity(gpa, 1); |
| 537 | try m.input_files.ensureUnusedCapacity(gpa, 1); | 536 | try m.input_paths.ensureUnusedCapacity(gpa, 1); |
| 538 | | 537 | |
| 539 | const prev_contents_len = m.contents.items.len; | 538 | const prev_contents_len = m.contents.items.len; |
| 540 | const header: *File = @ptrCast(try m.contents.addManyAsSlice(gpa, @sizeOf(File))); | 539 | const header: *File = @ptrCast(try m.contents.addManyAsSlice(gpa, @sizeOf(File))); |
| ... | @@ -558,7 +557,7 @@ pub const Manifest = struct { | ... | @@ -558,7 +557,7 @@ pub const Manifest = struct { |
| 558 | }); | 557 | }); |
| 559 | if (gop.found_existing) { | 558 | if (gop.found_existing) { |
| 560 | m.contents.shrinkRetainingCapacity(prev_contents_len); | 559 | m.contents.shrinkRetainingCapacity(prev_contents_len); |
| 561 | const existing_input_file = &m.input_files.items[gop.index]; | 560 | const existing_input_file = &m.input_paths.items[gop.index]; |
| 562 | if (options.handle) |handle| { | 561 | if (options.handle) |handle| { |
| 563 | existing_input_file.handle = handle; | 562 | existing_input_file.handle = handle; |
| 564 | existing_input_file.have_handle = true; | 563 | existing_input_file.have_handle = true; |
| ... | @@ -579,7 +578,7 @@ pub const Manifest = struct { | ... | @@ -579,7 +578,7 @@ pub const Manifest = struct { |
| 579 | if (!options.metadata_only) | 578 | if (!options.metadata_only) |
| 580 | existing_header.flags.metadata_only = false; | 579 | existing_header.flags.metadata_only = false; |
| 581 | } else { | 580 | } else { |
| 582 | m.input_files.appendAssumeCapacity(.{ | 581 | m.input_paths.appendAssumeCapacity(.{ |
| 583 | .request_handle = options.request_handle, | 582 | .request_handle = options.request_handle, |
| 584 | .have_handle = options.handle != null, | 583 | .have_handle = options.handle != null, |
| 585 | .handle = if (options.handle) |handle| handle else undefined, | 584 | .handle = if (options.handle) |handle| handle else undefined, |
| ... | @@ -587,7 +586,7 @@ pub const Manifest = struct { | ... | @@ -587,7 +586,7 @@ pub const Manifest = struct { |
| 587 | .have_digest = false, | 586 | .have_digest = false, |
| 588 | .have_stat = options.stat != null, | 587 | .have_stat = options.stat != null, |
| 589 | }); | 588 | }); |
| 590 | assert(m.input_files.items.len - 1 == gop.index); | 589 | assert(m.input_paths.items.len - 1 == gop.index); |
| 591 | if (options.stat) |stat| { | 590 | if (options.stat) |stat| { |
| 592 | header.size = stat.size; | 591 | header.size = stat.size; |
| 593 | header.inode = stat.inode; | 592 | header.inode = stat.inode; |
| ... | @@ -597,9 +596,9 @@ pub const Manifest = struct { | ... | @@ -597,9 +596,9 @@ pub const Manifest = struct { |
| 597 | return @fromBackingInt(gop.index); | 596 | return @fromBackingInt(gop.index); |
| 598 | } | 597 | } |
| 599 | | 598 | |
| 600 | pub fn addInputFileOptional(m: *Manifest, opt_path: ?Path, options: AddInputFileOptions) Allocator.Error!void { | 599 | pub fn addInputFileOptional(m: *Manifest, opt_path: ?Path, options: AddInputPathOptions) Allocator.Error!void { |
| 601 | m.hash.add(opt_path != null); | 600 | m.hash.add(opt_path != null); |
| 602 | _ = try addInputFile(m, opt_path orelse return, options); | 601 | _ = try addInputPath(m, opt_path orelse return, options); |
| 603 | } | 602 | } |
| 604 | | 603 | |
| 605 | /// Check the cache to see if the input exists in it. | 604 | /// Check the cache to see if the input exists in it. |
| ... | @@ -623,8 +622,8 @@ pub const Manifest = struct { | ... | @@ -623,8 +622,8 @@ pub const Manifest = struct { |
| 623 | pub fn checkProgressless(man: *Manifest) Check.Error!Check.Status { | 622 | pub fn checkProgressless(man: *Manifest) Check.Error!Check.Status { |
| 624 | assert(man.manifest_file == null); | 623 | assert(man.manifest_file == null); |
| 625 | | 624 | |
| 626 | for (man.files.keys()[0..man.input_files.items.len]) |file_off| { | 625 | for (man.files.keys()[0..man.input_paths.items.len]) |file_off| { |
| 627 | file_off.get(man).manifestDigestHash(&man.hash.hasher); | 626 | man.digestHash(file_off, &man.hash.hasher); |
| 628 | } | 627 | } |
| 629 | | 628 | |
| 630 | man.diagnostic = .none; | 629 | man.diagnostic = .none; |
| ... | @@ -709,16 +708,16 @@ pub const Manifest = struct { | ... | @@ -709,16 +708,16 @@ pub const Manifest = struct { |
| 709 | | 708 | |
| 710 | // We're going to construct a second hash. Its input will begin with the digest we've | 709 | // We're going to construct a second hash. Its input will begin with the digest we've |
| 711 | // already computed (`bin_digest`), and then it'll have the digests of each input file, | 710 | // already computed (`bin_digest`), and then it'll have the digests of each input file, |
| 712 | // including "post" files (see `addFilePost`). If this is a hit, we learn the set of "post" | 711 | // including "post" files (see `addPathPost`). If this is a hit, we learn the set of "post" |
| 713 | // files from the manifest on disk. If this is a miss, we'll learn those from future calls | 712 | // files from the manifest on disk. If this is a miss, we'll learn those from future calls |
| 714 | // to `addFilePost` etc. As such, the state of `man.hash.hasher` after this function | 713 | // to `addPathPost` etc. As such, the state of `man.hash.hasher` after this function |
| 715 | // depends on whether this is a hit or a miss. | 714 | // depends on whether this is a hit or a miss. |
| 716 | // | 715 | // |
| 717 | // If we return `CacheStatus.hit`, then `man.hash.hasher` must already include | 716 | // If we return `CacheStatus.hit`, then `man.hash.hasher` must already include |
| 718 | // the digests of the "post" files, so the caller can call `final`. Otherwise, on a cache | 717 | // the digests of the "post" files, so the caller can call `final`. Otherwise, on a cache |
| 719 | // miss, `man.hash.hasher` will include the digests of all non-"post" files -- that is, | 718 | // miss, `man.hash.hasher` will include the digests of all non-"post" files -- that is, |
| 720 | // the ones we've already been told about. The rest will be discovered through calls to | 719 | // the ones we've already been told about. The rest will be discovered through calls to |
| 721 | // `addFilePost` etc, which will update the hasher. After all files are added, the user can | 720 | // `addPathPost` etc, which will update the hasher. After all files are added, the user can |
| 722 | // use `final`, and will at some point `writeManifest` the file list to disk. | 721 | // use `final`, and will at some point `writeManifest` the file list to disk. |
| 723 | | 722 | |
| 724 | man.hash.hasher = hasher_init; | 723 | man.hash.hasher = hasher_init; |
| ... | @@ -762,11 +761,11 @@ pub const Manifest = struct { | ... | @@ -762,11 +761,11 @@ pub const Manifest = struct { |
| 762 | } | 761 | } |
| 763 | | 762 | |
| 764 | fn shrinkFilesToInput(m: *Manifest) void { | 763 | fn shrinkFilesToInput(m: *Manifest) void { |
| 765 | if (m.files.count() <= m.input_files.items.len) return; | 764 | if (m.files.count() <= m.input_paths.items.len) return; |
| 766 | const off = m.files.keys()[m.input_files.items.len]; | 765 | const off = m.files.keys()[m.input_paths.items.len]; |
| 767 | m.contents.shrinkRetainingCapacity(@backingInt(off)); | 766 | m.contents.shrinkRetainingCapacity(@backingInt(off)); |
| 768 | assert(m.contents.len % @alignOf(File) == 0); | 767 | assert(m.contents.items.len % @alignOf(File) == 0); |
| 769 | m.files.shrinkRetainingCapacity(m.input_files.items.len); | 768 | m.files.shrinkRetainingCapacity(m.input_paths.items.len); |
| 770 | } | 769 | } |
| 771 | | 770 | |
| 772 | /// Assumes that `self.hash.hasher` has been updated only with the original digest and that | 771 | /// Assumes that `self.hash.hasher` has been updated only with the original digest and that |
| ... | @@ -784,59 +783,70 @@ pub const Manifest = struct { | ... | @@ -784,59 +783,70 @@ pub const Manifest = struct { |
| 784 | return error.CacheCheckFailed; | 783 | return error.CacheCheckFailed; |
| 785 | }, | 784 | }, |
| 786 | }; | 785 | }; |
| | 786 | const contents = m.contents.items; |
| 787 | | 787 | |
| 788 | // Guess number of files based on manifest contents len to reduce allocations. | 788 | var off: u32 = 0; |
| 789 | try m.files.ensureUnusedCapacity(gpa, m.contents.len / (@sizeOf(File) + 32)); | 789 | var c: Check = .{}; |
| 790 | | | |
| 791 | var file_index: usize = 0; | | |
| 792 | var off: usize = 0; | | |
| 793 | | 790 | |
| 794 | // This group we always want to compute the hash digests, even on a cache miss. | 791 | // This group we always want to compute the hash digests, even on a |
| | 792 | // cache miss, because they will be used in the manifest digest. |
| 795 | var input_group: Io.Group = .init; | 793 | var input_group: Io.Group = .init; |
| 796 | defer input_group.cancel(io); | 794 | defer input_group.cancel(io); |
| 797 | | 795 | |
| | 796 | // First the input files section, which must match our input files, |
| | 797 | // otherwise it's invalid format. |
| | 798 | for (m.input_paths.items, m.files.keys()[0..m.input_paths.items.len]) |*input_path, input_file_off| { |
| | 799 | if (off + 1 >= contents.len) return error.InvalidFormat; |
| | 800 | const file_off: File.Offset = @fromBackingInt(off); |
| | 801 | const file = try file_off.getFallible(contents); |
| | 802 | if (file.flags.prefix >= m.cache.prefixes_len) return error.InvalidFormat; |
| | 803 | const path = try filePathFallible(contents, file_off); |
| | 804 | if (path.len == 0) return error.InvalidFormat; |
| | 805 | if (input_file_off != file_off) return error.InvalidFormat; |
| | 806 | |
| | 807 | input_group.async(io, checkInputFile, .{ m, &c, file_off, path, input_path }); |
| | 808 | |
| | 809 | off = @intCast(@as(usize, off) + @sizeOf(File) + path.len + 1); |
| | 810 | } |
| | 811 | |
| | 812 | // Guess number of files based on manifest contents len to reduce allocations. |
| | 813 | // This is not an upper bound; subsequent insertions may potentially allocate. |
| | 814 | try m.files.ensureUnusedCapacity(gpa, contents.len / (@sizeOf(File) + 32)); |
| | 815 | |
| 798 | // This group we would like to cancel as soon as a cache miss is discovered. | 816 | // This group we would like to cancel as soon as a cache miss is discovered. |
| 799 | const PostResult = union(enum) { | 817 | const PostResult = union(enum) { |
| 800 | checkFile: Check.Status, | 818 | checkFile: Check.Status, |
| 801 | }; | 819 | }; |
| 802 | var post_select_buffer: [10]PostResult = undefined; | 820 | var post_select_buffer: [10]PostResult = undefined; |
| 803 | var post_select: Io.Select(PostResult) = .init(&post_select_buffer); | 821 | var post_select: Io.Select(PostResult) = .init(io, &post_select_buffer); |
| 804 | var post_select_remaining: usize = 0; | 822 | var post_select_remaining: usize = 0; |
| 805 | var c: Check = .{}; | 823 | defer post_select.cancelDiscard(); |
| 806 | defer post_select.cancel(io); | | |
| 807 | | 824 | |
| 808 | while (off + 1 < m.contents.len) { | 825 | while (off + 1 < contents.len) { |
| 809 | const file_off: File.Offset = @fromBackingInt(off); | 826 | const file_off: File.Offset = @fromBackingInt(off); |
| 810 | const file = try File.getFallible(file_off, m); | 827 | const file = try file_off.getFallible(m); |
| 811 | if (file.flags.prefix >= m.cache.prefixes_len) return error.InvalidFormat; | 828 | if (file.flags.prefix >= m.cache.prefixes_len) return error.InvalidFormat; |
| 812 | const path = try file.pathFallible(); | 829 | const path = try filePathFallible(contents, file_off); |
| 813 | if (path.len == 0) return error.InvalidFormat; | 830 | if (path.len == 0) return error.InvalidFormat; |
| 814 | | 831 | |
| 815 | if (file_index < m.input_files.items.len) { | 832 | try m.files.put(gpa, file_off, {}); |
| 816 | if (m.files.keys()[file_index] != file_off) return error.InvalidFormat; | | |
| 817 | | | |
| 818 | input_group.async(io, checkInputFile, .{ m, &c, file_off, path }); | | |
| 819 | } else { | | |
| 820 | try m.files.put(gpa, file_off); | | |
| 821 | | 833 | |
| 822 | post_select.async(.checkFile, checkFile, .{ m, &c, file_off, path }); | 834 | post_select.async(.checkFile, checkFile, .{ m, &c, file_off, path }); |
| 823 | post_select_remaining += 1; | 835 | post_select_remaining += 1; |
| 824 | } | | |
| 825 | | 836 | |
| 826 | file_index += 1; | 837 | off = @intCast(@as(usize, off) + @sizeOf(File) + path.len + 1); |
| 827 | off += @sizeOf(File) + path.len + 1; | | |
| 828 | } | 838 | } |
| 829 | | 839 | |
| 830 | // Final terminating zero byte to distinguish empty manifest file from | 840 | // Final terminating zero byte to distinguish empty manifest file from |
| 831 | // manifest with zero files. | 841 | // manifest with zero files. |
| 832 | const file_valid = off + 1 == m.contents.len and m.contents[off] == 0; | 842 | const file_valid = off + 1 == contents.len and contents[off] == 0; |
| 833 | if (!file_valid or file_index < m.input_files.items.len) { | 843 | if (!file_valid) { |
| 834 | try input_group.await(io); | 844 | try input_group.await(io); |
| 835 | return .miss; | 845 | return .miss; |
| 836 | } | 846 | } |
| 837 | | 847 | |
| 838 | // Don't track the trailing zero byte in contents. | 848 | // Don't track the trailing zero byte in contents. |
| 839 | m.contents.len -= 1; | 849 | m.contents.items.len -= 1; |
| 840 | | 850 | |
| 841 | var post_await_buffer: [10]PostResult = undefined; | 851 | var post_await_buffer: [10]PostResult = undefined; |
| 842 | while (post_select_remaining > 0) { | 852 | while (post_select_remaining > 0) { |
| ... | @@ -880,11 +890,17 @@ pub const Manifest = struct { | ... | @@ -880,11 +890,17 @@ pub const Manifest = struct { |
| 880 | return .hit; | 890 | return .hit; |
| 881 | } | 891 | } |
| 882 | | 892 | |
| 883 | fn checkInputFile(m: *Manifest, c: *Check, file_off: File.Offset, file_path: [:0]const u8) Io.Cancelable!void { | 893 | fn checkInputFile( |
| 884 | // TODO use already open handle | 894 | m: *Manifest, |
| 885 | // TODO use already provided stat | 895 | c: *Check, |
| 886 | // TODO implement request_handle | 896 | file_off: File.Offset, |
| 887 | // TODO implement request_contents | 897 | file_path: [:0]const u8, |
| | 898 | input_path: *InputPath, |
| | 899 | ) Io.Cancelable!void { |
| | 900 | if (input_path.have_handle) @panic("TODO"); |
| | 901 | if (input_path.have_stat) @panic("TODO"); |
| | 902 | if (input_path.contents != .not_requested) @panic("TODO"); |
| | 903 | if (input_path.request_handle) @panic("TODO"); |
| 888 | switch (try checkFile(m, c, file_off, file_path)) { | 904 | switch (try checkFile(m, c, file_off, file_path)) { |
| 889 | .hit => return, | 905 | .hit => return, |
| 890 | .miss => @atomicStore(Check.Status, &c.status, .miss, .unordered), | 906 | .miss => @atomicStore(Check.Status, &c.status, .miss, .unordered), |
| ... | @@ -897,7 +913,7 @@ pub const Manifest = struct { | ... | @@ -897,7 +913,7 @@ pub const Manifest = struct { |
| 897 | c: *Check, | 913 | c: *Check, |
| 898 | file_off: File.Offset, | 914 | file_off: File.Offset, |
| 899 | file_path: [:0]const u8, | 915 | file_path: [:0]const u8, |
| 900 | ) Io.Cancelable!Check.Status { | 916 | ) error{ Canceled, CacheCheckFailed }!Check.Status { |
| 901 | const file = file_off.get(m); | 917 | const file = file_off.get(m); |
| 902 | const cache = m.cache; | 918 | const cache = m.cache; |
| 903 | const gpa = cache.gpa; | 919 | const gpa = cache.gpa; |
| ... | @@ -905,7 +921,7 @@ pub const Manifest = struct { | ... | @@ -905,7 +921,7 @@ pub const Manifest = struct { |
| 905 | const parent_dir = cache.prefixes()[file.flags.prefix].handle; | 921 | const parent_dir = cache.prefixes()[file.flags.prefix].handle; |
| 906 | | 922 | |
| 907 | if (file.flags.metadata_only) { | 923 | if (file.flags.metadata_only) { |
| 908 | const actual_stat = parent_dir.statFile() catch |err| switch (err) { | 924 | const actual_stat = parent_dir.statFile(io, file_path, .{}) catch |err| switch (err) { |
| 909 | error.FileNotFound => return .miss, | 925 | error.FileNotFound => return .miss, |
| 910 | error.Canceled => |e| return e, | 926 | error.Canceled => |e| return e, |
| 911 | else => |e| return c.fail(m, .{ .file_stat = .{ | 927 | else => |e| return c.fail(m, .{ .file_stat = .{ |
| ... | @@ -999,10 +1015,10 @@ pub const Manifest = struct { | ... | @@ -999,10 +1015,10 @@ pub const Manifest = struct { |
| 999 | /// not including post files). | 1015 | /// not including post files). |
| 1000 | /// | 1016 | /// |
| 1001 | /// Assumes that `bin_digest` is populated for all input files. | 1017 | /// Assumes that `bin_digest` is populated for all input files. |
| 1002 | pub fn unhit(man: *Manifest, bin_digest: BinDigest) void { | 1018 | pub fn unhit(man: *Manifest, bin_digest: *const BinDigest) void { |
| 1003 | // Reset the hash. | 1019 | // Reset the hash. |
| 1004 | man.hash.hasher = hasher_init; | 1020 | man.hash.hasher = hasher_init; |
| 1005 | man.hash.hasher.update(&bin_digest); | 1021 | man.hash.hasher.update(bin_digest); |
| 1006 | man.shrinkFilesToInput(); | 1022 | man.shrinkFilesToInput(); |
| 1007 | for (man.files.keys()) |off| { | 1023 | for (man.files.keys()) |off| { |
| 1008 | const file = off.get(man); | 1024 | const file = off.get(man); |
| ... | @@ -1053,11 +1069,8 @@ pub const Manifest = struct { | ... | @@ -1053,11 +1069,8 @@ pub const Manifest = struct { |
| 1053 | return timestamp.nanoseconds >= man.recent_problematic_timestamp.nanoseconds; | 1069 | return timestamp.nanoseconds >= man.recent_problematic_timestamp.nanoseconds; |
| 1054 | } | 1070 | } |
| 1055 | | 1071 | |
| 1056 | pub const AddFilePostOptions = struct { | 1072 | pub const AddPathPostOptions = struct { |
| 1057 | handle: union(enum) { | 1073 | handle: PathHandle = .{ .file = null }, |
| 1058 | file: ?Io.File, | | |
| 1059 | dir: ?Io.Dir, | | |
| 1060 | } = .{ .file = null }, | | |
| 1061 | stat: ?Stat = null, | 1074 | stat: ?Stat = null, |
| 1062 | /// If it is a directory, there is a special encoding required for contents, which | 1075 | /// If it is a directory, there is a special encoding required for contents, which |
| 1063 | /// is null-separated sorted entries, each one prefixed with `File.Kind`. | 1076 | /// is null-separated sorted entries, each one prefixed with `File.Kind`. |
| ... | @@ -1065,7 +1078,7 @@ pub const Manifest = struct { | ... | @@ -1065,7 +1078,7 @@ pub const Manifest = struct { |
| 1065 | metadata_only: bool = false, | 1078 | metadata_only: bool = false, |
| 1066 | }; | 1079 | }; |
| 1067 | | 1080 | |
| 1068 | pub const AddFilePostError = error{ | 1081 | pub const AddPathPostError = error{ |
| 1069 | /// The same file path has been added to the cache manifest both as a | 1082 | /// The same file path has been added to the cache manifest both as a |
| 1070 | /// directory and as a normal file, making the intended caching | 1083 | /// directory and as a normal file, making the intended caching |
| 1071 | /// behavior ambiguous. | 1084 | /// behavior ambiguous. |
| ... | @@ -1074,7 +1087,10 @@ pub const Manifest = struct { | ... | @@ -1074,7 +1087,10 @@ pub const Manifest = struct { |
| 1074 | | 1087 | |
| 1075 | /// Add a file as a dependency of process being cached, after cache miss | 1088 | /// Add a file as a dependency of process being cached, after cache miss |
| 1076 | /// occurs. | 1089 | /// occurs. |
| 1077 | pub fn addFilePost(m: *Manifest, path: Path, options: AddFilePostOptions) AddFilePostError!void { | 1090 | /// |
| | 1091 | /// See also: |
| | 1092 | /// * `addInputPath` |
| | 1093 | pub fn addPathPost(m: *Manifest, path: Path, options: AddPathPostOptions) AddPathPostError!void { |
| 1078 | assert(m.manifest_file != null); | 1094 | assert(m.manifest_file != null); |
| 1079 | const cache = m.cache; | 1095 | const cache = m.cache; |
| 1080 | const gpa = cache.gpa; | 1096 | const gpa = cache.gpa; |
| ... | @@ -1236,14 +1252,14 @@ pub const Manifest = struct { | ... | @@ -1236,14 +1252,14 @@ pub const Manifest = struct { |
| 1236 | // Clang is invoked in single-source mode but other programs may not | 1252 | // Clang is invoked in single-source mode but other programs may not |
| 1237 | .target, .target_must_resolve => {}, | 1253 | .target, .target_must_resolve => {}, |
| 1238 | .prereq => |file_path| if (self.manifest_file == null) { | 1254 | .prereq => |file_path| if (self.manifest_file == null) { |
| 1239 | _ = try self.addFilePath(.initCwd(file_path), null); | 1255 | _ = try self.addInputPath(.initCwd(file_path), .{}); |
| 1240 | } else try self.addFilePost(file_path), | 1256 | } else try self.addPathPost(file_path), |
| 1241 | .prereq_must_resolve => { | 1257 | .prereq_must_resolve => { |
| 1242 | resolve_buf.clearRetainingCapacity(); | 1258 | resolve_buf.clearRetainingCapacity(); |
| 1243 | try token.resolve(gpa, &resolve_buf); | 1259 | try token.resolve(gpa, &resolve_buf); |
| 1244 | if (self.manifest_file == null) { | 1260 | if (self.manifest_file == null) { |
| 1245 | _ = try self.addFilePath(.initCwd(resolve_buf.items), null); | 1261 | _ = try self.addInputPath(.initCwd(resolve_buf.items), .{}); |
| 1246 | } else try self.addFilePost(resolve_buf.items); | 1262 | } else try self.addPathPost(resolve_buf.items); |
| 1247 | }, | 1263 | }, |
| 1248 | else => |err| { | 1264 | else => |err| { |
| 1249 | try err.printError(gpa, &error_buf); | 1265 | try err.printError(gpa, &error_buf); |
| ... | @@ -1336,25 +1352,45 @@ pub const Manifest = struct { | ... | @@ -1336,25 +1352,45 @@ pub const Manifest = struct { |
| 1336 | return .{ .manifest_file = self.manifest_file.? }; | 1352 | return .{ .manifest_file = self.manifest_file.? }; |
| 1337 | } | 1353 | } |
| 1338 | | 1354 | |
| 1339 | pub fn takeFiles(man: *Manifest) Files { | 1355 | pub const SelfContainedFiles = struct { |
| 1340 | defer man.files = .empty; | 1356 | /// References memory inside `contents`. |
| 1341 | return man.files; | 1357 | files: Files, |
| 1342 | } | 1358 | contents: std.ArrayList(u8), |
| | 1359 | |
| | 1360 | pub const empty: @This() = .{ |
| | 1361 | .files = .empty, |
| | 1362 | .contents = .empty, |
| | 1363 | }; |
| | 1364 | |
| | 1365 | pub fn deinit(scf: *SelfContainedFiles, gpa: Allocator) void { |
| | 1366 | scf.files.deinit(gpa); |
| | 1367 | scf.contents.deinit(gpa); |
| | 1368 | scf.* = undefined; |
| | 1369 | } |
| | 1370 | |
| | 1371 | pub fn path(scf: *const SelfContainedFiles, file_offset: File.Offset) [:0]const u8 { |
| | 1372 | return filePath(scf.contents.items, file_offset); |
| | 1373 | } |
| | 1374 | }; |
| 1343 | | 1375 | |
| 1344 | pub fn freeFiles(gpa: Allocator, files: *Files) void { | 1376 | pub fn takeFiles(m: *Manifest) SelfContainedFiles { |
| 1345 | for (files.keys()) |*file| file.deinit(gpa); | 1377 | defer m.files = .empty; |
| 1346 | files.deinit(gpa); | 1378 | defer m.contents = .empty; |
| | 1379 | return .{ |
| | 1380 | .files = m.files, |
| | 1381 | .contents = m.contents, |
| | 1382 | }; |
| 1347 | } | 1383 | } |
| 1348 | | 1384 | |
| 1349 | /// Releases the manifest file and frees any memory the Manifest was using. | 1385 | /// Releases the manifest file and frees any memory the Manifest was using. |
| 1350 | /// `Manifest.hit` must be called first. | 1386 | /// `Manifest.hit` must be called first. |
| 1351 | /// | 1387 | /// |
| 1352 | /// Don't forget to call `writeManifest` before this! | 1388 | /// Don't forget to call `writeManifest` before this! |
| 1353 | pub fn deinit(man: *Manifest) void { | 1389 | pub fn deinit(m: *Manifest) void { |
| 1354 | const io = man.cache.io; | 1390 | const io = m.cache.io; |
| 1355 | const gpa = man.cache.gpa; | 1391 | const gpa = m.cache.gpa; |
| 1356 | | 1392 | |
| 1357 | if (man.manifest_file) |file| { | 1393 | if (m.manifest_file) |file| { |
| 1358 | if (builtin.os.tag == .windows) { | 1394 | if (builtin.os.tag == .windows) { |
| 1359 | // See Lock.release for why this is required on Windows | 1395 | // See Lock.release for why this is required on Windows |
| 1360 | file.unlock(io); | 1396 | file.unlock(io); |
| ... | @@ -1362,8 +1398,9 @@ pub const Manifest = struct { | ... | @@ -1362,8 +1398,9 @@ pub const Manifest = struct { |
| 1362 | | 1398 | |
| 1363 | file.close(io); | 1399 | file.close(io); |
| 1364 | } | 1400 | } |
| 1365 | freeFiles(gpa, &man.files); | 1401 | m.files.deinit(gpa); |
| 1366 | man.* = undefined; | 1402 | m.contents.deinit(gpa); |
| | 1403 | m.* = undefined; |
| 1367 | } | 1404 | } |
| 1368 | | 1405 | |
| 1369 | pub fn populateFileSystemInputs(man: *Manifest, buf: *std.ArrayList(u8)) Allocator.Error!void { | 1406 | pub fn populateFileSystemInputs(man: *Manifest, buf: *std.ArrayList(u8)) Allocator.Error!void { |
| ... | @@ -1497,6 +1534,25 @@ pub const Manifest = struct { | ... | @@ -1497,6 +1534,25 @@ pub const Manifest = struct { |
| 1497 | hasher.update(contents.items[contents_start..][0..contents_len]); | 1534 | hasher.update(contents.items[contents_start..][0..contents_len]); |
| 1498 | hasher.final(bin_digest); | 1535 | hasher.final(bin_digest); |
| 1499 | } | 1536 | } |
| | 1537 | |
| | 1538 | fn digestHash(m: *const Manifest, off: File.Offset, hasher: *Hasher) void { |
| | 1539 | const contents = m.contents.items; |
| | 1540 | const flags_off = @offsetOf(File, "flags"); |
| | 1541 | comptime assert(@offsetOf(File, "path_start") - flags_off == 1); |
| | 1542 | const hash_start = @backingInt(off) + flags_off; |
| | 1543 | const hash_end = mem.findScalarPos(u8, contents, hash_start, 0).?; |
| | 1544 | hasher.update(contents[hash_start..hash_end]); |
| | 1545 | } |
| | 1546 | |
| | 1547 | fn filePathFallible(contents: []const u8, off: File.Offset) error{InvalidFormat}![:0]const u8 { |
| | 1548 | const path_start = @backingInt(off) + @offsetOf(File, "path_start"); |
| | 1549 | const path_end = mem.findScalarPos(u8, contents, path_start, 0) orelse return error.InvalidFormat; |
| | 1550 | return contents[path_start..path_end :0]; |
| | 1551 | } |
| | 1552 | |
| | 1553 | fn filePath(contents: []const u8, off: File.Offset) [:0]const u8 { |
| | 1554 | return filePathFallible(contents, off) catch unreachable; |
| | 1555 | } |
| 1500 | }; | 1556 | }; |
| 1501 | | 1557 | |
| 1502 | /// Create/Write a file, close it, then grab its stat.mtime timestamp. | 1558 | /// Create/Write a file, close it, then grab its stat.mtime timestamp. |
| ... | @@ -1555,7 +1611,7 @@ test "cache file and then recall it" { | ... | @@ -1555,7 +1611,7 @@ test "cache file and then recall it" { |
| 1555 | ch.hash.add(true); | 1611 | ch.hash.add(true); |
| 1556 | ch.hash.add(@as(u16, 1234)); | 1612 | ch.hash.add(@as(u16, 1234)); |
| 1557 | ch.hash.addBytes("1234"); | 1613 | ch.hash.addBytes("1234"); |
| 1558 | _ = try ch.addFilePath(.initCwd(temp_file), null); | 1614 | _ = try ch.addInputPath(.initCwd(temp_file), .{}); |
| 1559 | | 1615 | |
| 1560 | // There should be nothing in the cache | 1616 | // There should be nothing in the cache |
| 1561 | try testing.expectEqual(false, try ch.hit(.none)); | 1617 | try testing.expectEqual(false, try ch.hit(.none)); |
| ... | @@ -1570,7 +1626,7 @@ test "cache file and then recall it" { | ... | @@ -1570,7 +1626,7 @@ test "cache file and then recall it" { |
| 1570 | ch.hash.add(true); | 1626 | ch.hash.add(true); |
| 1571 | ch.hash.add(@as(u16, 1234)); | 1627 | ch.hash.add(@as(u16, 1234)); |
| 1572 | ch.hash.addBytes("1234"); | 1628 | ch.hash.addBytes("1234"); |
| 1573 | _ = try ch.addFilePath(.initCwd(temp_file), null); | 1629 | _ = try ch.addInputPath(.initCwd(temp_file), .{}); |
| 1574 | | 1630 | |
| 1575 | // Cache hit! We just "built" the same file | 1631 | // Cache hit! We just "built" the same file |
| 1576 | try testing.expect(try ch.hit(.none)); | 1632 | try testing.expect(try ch.hit(.none)); |
| ... | @@ -1623,7 +1679,7 @@ test "check that changing a file makes cache fail" { | ... | @@ -1623,7 +1679,7 @@ test "check that changing a file makes cache fail" { |
| 1623 | defer ch.deinit(); | 1679 | defer ch.deinit(); |
| 1624 | | 1680 | |
| 1625 | ch.hash.addBytes("1234"); | 1681 | ch.hash.addBytes("1234"); |
| 1626 | const temp_file_idx = try ch.addFilePath(.initCwd(temp_file), 100); | 1682 | const temp_file_idx = try ch.addInputPath(.initCwd(temp_file), .{ .request_contents = true }); |
| 1627 | | 1683 | |
| 1628 | // There should be nothing in the cache | 1684 | // There should be nothing in the cache |
| 1629 | try testing.expectEqual(false, try ch.hit(.none)); | 1685 | try testing.expectEqual(false, try ch.hit(.none)); |
| ... | @@ -1642,7 +1698,7 @@ test "check that changing a file makes cache fail" { | ... | @@ -1642,7 +1698,7 @@ test "check that changing a file makes cache fail" { |
| 1642 | defer ch.deinit(); | 1698 | defer ch.deinit(); |
| 1643 | | 1699 | |
| 1644 | ch.hash.addBytes("1234"); | 1700 | ch.hash.addBytes("1234"); |
| 1645 | const temp_file_idx = try ch.addFilePath(.initCwd(temp_file), 100); | 1701 | const temp_file_idx = try ch.addInputPath(.initCwd(temp_file), .{ .request_contents = true }); |
| 1646 | | 1702 | |
| 1647 | // A file that we depend on has been updated, so the cache should not contain an entry for it | 1703 | // A file that we depend on has been updated, so the cache should not contain an entry for it |
| 1648 | try testing.expectEqual(false, try ch.hit(.none)); | 1704 | try testing.expectEqual(false, try ch.hit(.none)); |
| ... | @@ -1689,7 +1745,7 @@ test "no file inputs" { | ... | @@ -1689,7 +1745,7 @@ test "no file inputs" { |
| 1689 | man.hash.addBytes("1234"); | 1745 | man.hash.addBytes("1234"); |
| 1690 | | 1746 | |
| 1691 | // There should be nothing in the cache | 1747 | // There should be nothing in the cache |
| 1692 | try testing.expectEqual(false, try man.hit(.none)); | 1748 | try testing.expectEqual(false, try man.check(.none)); |
| 1693 | | 1749 | |
| 1694 | digest1 = man.final(); | 1750 | digest1 = man.final(); |
| 1695 | | 1751 | |
| ... | @@ -1701,7 +1757,7 @@ test "no file inputs" { | ... | @@ -1701,7 +1757,7 @@ test "no file inputs" { |
| 1701 | | 1757 | |
| 1702 | man.hash.addBytes("1234"); | 1758 | man.hash.addBytes("1234"); |
| 1703 | | 1759 | |
| 1704 | try testing.expect(try man.hit(.none)); | 1760 | try testing.expect(try man.check(.none)); |
| 1705 | digest2 = man.final(); | 1761 | digest2 = man.final(); |
| 1706 | try testing.expectEqual(false, man.have_exclusive_lock); | 1762 | try testing.expectEqual(false, man.have_exclusive_lock); |
| 1707 | } | 1763 | } |
| ... | @@ -1750,12 +1806,12 @@ test "Manifest with files added after initial hash work" { | ... | @@ -1750,12 +1806,12 @@ test "Manifest with files added after initial hash work" { |
| 1750 | defer ch.deinit(); | 1806 | defer ch.deinit(); |
| 1751 | | 1807 | |
| 1752 | ch.hash.addBytes("1234"); | 1808 | ch.hash.addBytes("1234"); |
| 1753 | _ = try ch.addFilePath(.initCwd(temp_file1), null); | 1809 | _ = try ch.addInputPath(.initCwd(temp_file1), .{}); |
| 1754 | | 1810 | |
| 1755 | // There should be nothing in the cache | 1811 | // There should be nothing in the cache |
| 1756 | try testing.expectEqual(false, try ch.hit(.none)); | 1812 | try testing.expectEqual(false, try ch.hit(.none)); |
| 1757 | | 1813 | |
| 1758 | _ = try ch.addFilePost(temp_file2); | 1814 | _ = try ch.addPathPost(temp_file2); |
| 1759 | | 1815 | |
| 1760 | digest1 = ch.final(); | 1816 | digest1 = ch.final(); |
| 1761 | try ch.writeManifest(); | 1817 | try ch.writeManifest(); |
| ... | @@ -1765,7 +1821,7 @@ test "Manifest with files added after initial hash work" { | ... | @@ -1765,7 +1821,7 @@ test "Manifest with files added after initial hash work" { |
| 1765 | defer ch.deinit(); | 1821 | defer ch.deinit(); |
| 1766 | | 1822 | |
| 1767 | ch.hash.addBytes("1234"); | 1823 | ch.hash.addBytes("1234"); |
| 1768 | _ = try ch.addFilePath(.initCwd(temp_file1), null); | 1824 | _ = try ch.addInputPath(.initCwd(temp_file1), .{}); |
| 1769 | | 1825 | |
| 1770 | try testing.expect(try ch.hit(.none)); | 1826 | try testing.expect(try ch.hit(.none)); |
| 1771 | digest2 = ch.final(); | 1827 | digest2 = ch.final(); |
| ... | @@ -1788,12 +1844,12 @@ test "Manifest with files added after initial hash work" { | ... | @@ -1788,12 +1844,12 @@ test "Manifest with files added after initial hash work" { |
| 1788 | defer ch.deinit(); | 1844 | defer ch.deinit(); |
| 1789 | | 1845 | |
| 1790 | ch.hash.addBytes("1234"); | 1846 | ch.hash.addBytes("1234"); |
| 1791 | _ = try ch.addFilePath(.initCwd(temp_file1), null); | 1847 | _ = try ch.addInputPath(.initCwd(temp_file1), .{}); |
| 1792 | | 1848 | |
| 1793 | // A file that we depend on has been updated, so the cache should not contain an entry for it | 1849 | // A file that we depend on has been updated, so the cache should not contain an entry for it |
| 1794 | try testing.expectEqual(false, try ch.hit(.none)); | 1850 | try testing.expectEqual(false, try ch.hit(.none)); |
| 1795 | | 1851 | |
| 1796 | _ = try ch.addFilePost(temp_file2); | 1852 | _ = try ch.addPathPost(temp_file2); |
| 1797 | | 1853 | |
| 1798 | digest3 = ch.final(); | 1854 | digest3 = ch.final(); |
| 1799 | | 1855 | |