| ... | @@ -1397,6 +1397,8 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { | ... | @@ -1397,6 +1397,8 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { |
| 1397 | process.exit(0); | 1397 | process.exit(0); |
| 1398 | } | 1398 | } |
| 1399 | | 1399 | |
| | 1400 | try validateFingerprints(gpa, &job_queue); |
| | 1401 | |
| 1400 | // Create the dependencies.zig file for configurer to | 1402 | // Create the dependencies.zig file for configurer to |
| 1401 | // obtain via `@import("@dependencies")`. | 1403 | // obtain via `@import("@dependencies")`. |
| 1402 | { | 1404 | { |
| ... | @@ -1714,6 +1716,81 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { | ... | @@ -1714,6 +1716,81 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { |
| 1714 | }; | 1716 | }; |
| 1715 | } | 1717 | } |
| 1716 | | 1718 | |
| | 1719 | const ValidFingerprintKey = struct { |
| | 1720 | project_id: Package.ProjectId, |
| | 1721 | version: std.SemanticVersion, |
| | 1722 | |
| | 1723 | const Context = struct { |
| | 1724 | pub fn hash(this: @This(), a: ValidFingerprintKey) u32 { |
| | 1725 | _ = this; |
| | 1726 | var hasher: std.hash.Wyhash = .init(a.project_id.fingerprint_id); |
| | 1727 | hasher.update(&a.project_id.padded_name); |
| | 1728 | hasher.update(@ptrCast((&a.version.major)[0..1])); |
| | 1729 | hasher.update(@ptrCast((&a.version.minor)[0..1])); |
| | 1730 | hasher.update(@ptrCast((&a.version.patch)[0..1])); |
| | 1731 | if (a.version.pre) |pre| hasher.update(pre); |
| | 1732 | return @truncate(hasher.final()); |
| | 1733 | } |
| | 1734 | pub fn eql(this: @This(), a: ValidFingerprintKey, b: ValidFingerprintKey, i: usize) bool { |
| | 1735 | _ = this; |
| | 1736 | _ = i; |
| | 1737 | return a.project_id.eql(&b.project_id) and a.version.order(b.version) == .eq; |
| | 1738 | } |
| | 1739 | }; |
| | 1740 | }; |
| | 1741 | |
| | 1742 | const ValidFingerprintTable = std.array_hash_map.Custom( |
| | 1743 | ValidFingerprintKey, |
| | 1744 | struct { fetch: *Fetch, pkg_hash: Package.Hash, printed: bool }, |
| | 1745 | ValidFingerprintKey.Context, |
| | 1746 | true, |
| | 1747 | ); |
| | 1748 | |
| | 1749 | /// Ensure that the dependency tree did not have any fingerprint matches with the same semantic version but |
| | 1750 | /// differing package hashes. |
| | 1751 | fn validateFingerprints(gpa: Allocator, job_queue: *const Fetch.JobQueue) !void { |
| | 1752 | var any_errors = false; |
| | 1753 | |
| | 1754 | var table: ValidFingerprintTable = .empty; |
| | 1755 | defer table.deinit(gpa); |
| | 1756 | |
| | 1757 | for (job_queue.table.keys(), job_queue.table.values()) |pkg_hash, fetch| { |
| | 1758 | if (!fetch.have_manifest) continue; |
| | 1759 | const gop = try table.getOrPut(gpa, .{ |
| | 1760 | .project_id = .init(fetch.manifest.name, fetch.manifest.id), |
| | 1761 | .version = fetch.manifest.version, |
| | 1762 | }); |
| | 1763 | if (gop.found_existing) { |
| | 1764 | any_errors = true; |
| | 1765 | const fingerprint: Package.Fingerprint = .init(fetch.manifest.id, fetch.manifest.name); |
| | 1766 | const stripped_version: std.SemanticVersion = .{ |
| | 1767 | .major = fetch.manifest.version.major, |
| | 1768 | .minor = fetch.manifest.version.minor, |
| | 1769 | .patch = fetch.manifest.version.patch, |
| | 1770 | .pre = fetch.manifest.version.pre, |
| | 1771 | }; |
| | 1772 | if (!gop.value_ptr.printed) { |
| | 1773 | gop.value_ptr.printed = true; |
| | 1774 | log.err("contradictory hash for package with fingerprint 0x{x} and version {f}: {s}", .{ |
| | 1775 | fingerprint.int(), stripped_version, gop.value_ptr.pkg_hash.toSlice(), |
| | 1776 | }); |
| | 1777 | } |
| | 1778 | log.err("contradictory hash for package with fingerprint 0x{x} and version {f}: {s}", .{ |
| | 1779 | fingerprint.int(), stripped_version, pkg_hash.toSlice(), |
| | 1780 | }); |
| | 1781 | } else { |
| | 1782 | gop.value_ptr.* = .{ .fetch = fetch, .pkg_hash = pkg_hash, .printed = false }; |
| | 1783 | } |
| | 1784 | } |
| | 1785 | if (any_errors) { |
| | 1786 | log.info("potential cause: package ownership dispute", .{}); |
| | 1787 | log.info("potential cause: maintainer neglected to bump version after making changes", .{}); |
| | 1788 | log.info("potential cause: dependency on unreleased changes", .{}); |
| | 1789 | log.info("hint: use --print-tree to visualize all package versions and hashes", .{}); |
| | 1790 | return error.FailedButCacheIntact; |
| | 1791 | } |
| | 1792 | } |
| | 1793 | |
| 1717 | fn printPkgTree( | 1794 | fn printPkgTree( |
| 1718 | terminal: Io.Terminal, | 1795 | terminal: Io.Terminal, |
| 1719 | arena: Allocator, | 1796 | arena: Allocator, |