authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-10-15 18:39:10+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-10-15 18:39:10+02:00
logda32e05a1ccdeb9ede15405a599893b643ff474e
treeda41f0710b7947ad287a7c9144fa596ac863b1b7
parentcb7a773ab8d8eb1838a3f764a9bfb735f30bfb5e
parentf6a00a4751c7d49f7a4406e9c640ebaee85662f9

Merge pull request '`--fork` fixes' (#37241) from fix-fork into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/37241

2 files changed, 94 insertions(+), 22 deletions(-)

lib/compiler/Maker.zig+77-15
...@@ -1385,7 +1385,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {...@@ -1385,7 +1385,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
1385 printPkgTree(.{1385 printPkgTree(.{
1386 .writer = initStdoutWriter(io),1386 .writer = initStdoutWriter(io),
1387 .mode = try .detect(io, .stdout(), NO_COLOR, CLICOLOR_FORCE),1387 .mode = try .detect(io, .stdout(), NO_COLOR, CLICOLOR_FORCE),
1388 }, arena, &job_queue, &fetch) catch |err| switch (err) {1388 }, arena, graph.global_cache_root, &job_queue, &fetch, &fork_set) catch |err| switch (err) {
1389 error.WriteFailed => return stdout_writer_allocation.err.?,1389 error.WriteFailed => return stdout_writer_allocation.err.?,
1390 else => |e| return e,1390 else => |e| return e,
1391 };1391 };
...@@ -1398,7 +1398,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {...@@ -1398,7 +1398,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
1398 process.exit(0);1398 process.exit(0);
1399 }1399 }
14001400
1401 try validateFingerprints(gpa, &job_queue);1401 try validateFingerprints(gpa, &job_queue, &fork_set);
14021402
1403 // Create the dependencies.zig file for configurer to1403 // Create the dependencies.zig file for configurer to
1404 // obtain via `@import("@dependencies")`.1404 // obtain via `@import("@dependencies")`.
...@@ -1444,10 +1444,29 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {...@@ -1444,10 +1444,29 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
1444 // The first one is a dummy package for the current project.1444 // The first one is a dummy package for the current project.
1445 continue;1445 continue;
1446 }1446 }
1447 if (!f.has_build_zig)1447 if (!f.has_build_zig) continue;
1448 continue;1448
1449 if (f.have_manifest) {
1450 const project_id: Package.ProjectId = .init(f.manifest.name, f.manifest.id);
1451 if (fork_set.getKeyPtrAdapted(project_id, @as(Fetch.JobQueue.Fork.Adapter, .{}))) |fork| {
1452 const path = try fork.path.toString(arena);
1453 const gop = deps_mod.deps.getOrPutAssumeCapacity(path);
1454 if (gop.found_existing) {
1455 assert(fork.uses > 1);
1456 f.cli_module = gop.value_ptr.*;
1457 } else {
1458 const m = try arena.create(CliModule);
1459 m.* = .{
1460 .root_path = try f.package_root.toString(arena),
1461 .name = path,
1462 };
1463 gop.value_ptr.* = m;
1464 f.cli_module = m;
1465 }
1466 continue;
1467 }
1468 }
1449 const hash_slice = try arena.dupe(u8, hash.toSlice());1469 const hash_slice = try arena.dupe(u8, hash.toSlice());
1450
1451 const m = try arena.create(CliModule);1470 const m = try arena.create(CliModule);
1452 m.* = .{1471 m.* = .{
1453 .root_path = try f.package_root.toString(arena),1472 .root_path = try f.package_root.toString(arena),
...@@ -1469,7 +1488,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {...@@ -1469,7 +1488,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
1469 const man = &f.manifest;1488 const man = &f.manifest;
1470 const dep_names = man.dependencies.keys();1489 const dep_names = man.dependencies.keys();
1471 try mod.deps.ensureUnusedCapacity(arena, @intCast(dep_names.len));1490 try mod.deps.ensureUnusedCapacity(arena, @intCast(dep_names.len));
1472 for (dep_names, man.dependencies.values()) |name, dep| {1491 for (dep_names, man.dependencies.values()) |name, *dep| {
1473 const dep_digest = Package.Fetch.depDigest(1492 const dep_digest = Package.Fetch.depDigest(
1474 f.package_root,1493 f.package_root,
1475 graph.global_cache_root,1494 graph.global_cache_root,
...@@ -1749,7 +1768,11 @@ const ValidFingerprintTable = std.array_hash_map.Custom(...@@ -1749,7 +1768,11 @@ const ValidFingerprintTable = std.array_hash_map.Custom(
17491768
1750/// Ensure that the dependency tree did not have any fingerprint matches with the same semantic version but1769/// Ensure that the dependency tree did not have any fingerprint matches with the same semantic version but
1751/// differing package hashes.1770/// differing package hashes.
1752fn validateFingerprints(gpa: Allocator, job_queue: *const Fetch.JobQueue) !void {1771fn validateFingerprints(
1772 gpa: Allocator,
1773 job_queue: *const Fetch.JobQueue,
1774 fork_set: *const Package.Fetch.JobQueue.ForkSet,
1775) !void {
1753 var any_errors = false;1776 var any_errors = false;
17541777
1755 var table: ValidFingerprintTable = .empty;1778 var table: ValidFingerprintTable = .empty;
...@@ -1757,8 +1780,13 @@ fn validateFingerprints(gpa: Allocator, job_queue: *const Fetch.JobQueue) !void...@@ -1757,8 +1780,13 @@ fn validateFingerprints(gpa: Allocator, job_queue: *const Fetch.JobQueue) !void
17571780
1758 for (job_queue.table.keys(), job_queue.table.values()) |pkg_hash, fetch| {1781 for (job_queue.table.keys(), job_queue.table.values()) |pkg_hash, fetch| {
1759 if (!fetch.have_manifest) continue;1782 if (!fetch.have_manifest) continue;
1783
1784 // Forked packages are exempt since they overide multiple package hashes to have the forked contents.
1785 const project_id: Package.ProjectId = .init(fetch.manifest.name, fetch.manifest.id);
1786 if (fork_set.containsAdapted(project_id, @as(Fetch.JobQueue.Fork.Adapter, .{}))) continue;
1787
1760 const gop = try table.getOrPut(gpa, .{1788 const gop = try table.getOrPut(gpa, .{
1761 .project_id = .init(fetch.manifest.name, fetch.manifest.id),1789 .project_id = project_id,
1762 .version = fetch.manifest.version,1790 .version = fetch.manifest.version,
1763 });1791 });
1764 if (gop.found_existing) {1792 if (gop.found_existing) {
...@@ -1795,8 +1823,10 @@ fn validateFingerprints(gpa: Allocator, job_queue: *const Fetch.JobQueue) !void...@@ -1795,8 +1823,10 @@ fn validateFingerprints(gpa: Allocator, job_queue: *const Fetch.JobQueue) !void
1795fn printPkgTree(1823fn printPkgTree(
1796 terminal: Io.Terminal,1824 terminal: Io.Terminal,
1797 arena: Allocator,1825 arena: Allocator,
1826 global_cache_root: Cache.Directory,
1798 job_queue: *const Fetch.JobQueue,1827 job_queue: *const Fetch.JobQueue,
1799 root_fetch: *const Fetch,1828 root_fetch: *const Fetch,
1829 fork_set: *const Package.Fetch.JobQueue.ForkSet,
1800) !void {1830) !void {
1801 if (!root_fetch.have_manifest) return;1831 if (!root_fetch.have_manifest) return;
1802 const names = root_fetch.manifest.dependencies.keys();1832 const names = root_fetch.manifest.dependencies.keys();
...@@ -1805,37 +1835,49 @@ fn printPkgTree(...@@ -1805,37 +1835,49 @@ fn printPkgTree(
1805 .parent = null,1835 .parent = null,
1806 .last = i == names.len - 1,1836 .last = i == names.len - 1,
1807 };1837 };
1808 try printPkgTreeChild(terminal, arena, job_queue, &print_node, name, dep.hash);1838 switch (dep.location) {
1839 .url => {
1840 const child_hash = if (dep.hash) |h| Package.Hash.fromSlice(h) else null;
1841 try printPkgTreeChild(terminal, arena, global_cache_root, job_queue, &print_node, name, child_hash, fork_set);
1842 },
1843 .path => {
1844 const child_hash = Package.Fetch.depDigest(root_fetch.package_root, global_cache_root, dep);
1845 try printPkgTreeChild(terminal, arena, global_cache_root, job_queue, &print_node, name, child_hash, fork_set);
1846 },
1847 }
1809 }1848 }
1810}1849}
18111850
1812fn printPkgTreeChild(1851fn printPkgTreeChild(
1813 terminal: Io.Terminal,1852 terminal: Io.Terminal,
1814 arena: Allocator,1853 arena: Allocator,
1854 global_cache_root: Cache.Directory,
1815 job_queue: *const Fetch.JobQueue,1855 job_queue: *const Fetch.JobQueue,
1816 parent_print_node: *const PrintNode,1856 parent_print_node: *const PrintNode,
1817 dep_name: []const u8,1857 dep_name: []const u8,
1818 opt_hash: ?[]const u8,1858 opt_pkg_hash: ?Package.Hash,
1859 fork_set: *const Package.Fetch.JobQueue.ForkSet,
1819) !void {1860) !void {
1820 const w = terminal.writer;1861 const w = terminal.writer;
1821 try parent_print_node.printPrefixedChild(w, terminal.mode);1862 try parent_print_node.printPrefixedChild(w, terminal.mode);
1822 try w.writeAll(dep_name);1863 try w.writeAll(dep_name);
18231864
1824 const hash = opt_hash orelse {1865 const pkg_hash = opt_pkg_hash orelse {
1825 try terminal.setColor(.red);1866 try terminal.setColor(.red);
1826 try w.writeAll(" (missing hash)\n");1867 try w.writeAll(" (missing hash)\n");
1827 try terminal.setColor(.reset);1868 try terminal.setColor(.reset);
1828 return;1869 return;
1829 };1870 };
1830 try terminal.setColor(.dim);1871 try terminal.setColor(.dim);
1831 try w.print(" {s}", .{hash});1872 try w.print(" {s}", .{pkg_hash.toSlice()});
1832 try terminal.setColor(.reset);1873 try terminal.setColor(.reset);
1833 const child_fetch = job_queue.table.get(.fromSlice(hash)) orelse {1874 const child_fetch = job_queue.table.get(pkg_hash) orelse {
1834 try terminal.setColor(.red);1875 try terminal.setColor(.red);
1835 try w.writeAll(" (fetch failed)\n");1876 try w.writeAll(" (fetch failed)\n");
1836 try terminal.setColor(.reset);1877 try terminal.setColor(.reset);
1837 return;1878 return;
1838 };1879 };
1880
1839 switch (child_fetch.lazy_status) {1881 switch (child_fetch.lazy_status) {
1840 .eager, .available => {},1882 .eager, .available => {},
1841 .unavailable => {1883 .unavailable => {
...@@ -1845,8 +1887,19 @@ fn printPkgTreeChild(...@@ -1845,8 +1887,19 @@ fn printPkgTreeChild(
1845 return;1887 return;
1846 },1888 },
1847 }1889 }
1890 if (!child_fetch.have_manifest) {
1891 try w.writeByte('\n');
1892 return;
1893 }
1894
1895 const project_id: Package.ProjectId = .init(child_fetch.manifest.name, child_fetch.manifest.id);
1896 if (fork_set.getKeyPtrAdapted(project_id, @as(Fetch.JobQueue.Fork.Adapter, .{}))) |fork| {
1897 try terminal.setColor(.yellow);
1898 try w.print(" (fork: {f})", .{fork.path});
1899 try terminal.setColor(.reset);
1900 }
1901
1848 try w.writeByte('\n');1902 try w.writeByte('\n');
1849 if (!child_fetch.have_manifest) return;
18501903
1851 const names = child_fetch.manifest.dependencies.keys();1904 const names = child_fetch.manifest.dependencies.keys();
1852 for (names, child_fetch.manifest.dependencies.values(), 0..) |name, *dep, i| {1905 for (names, child_fetch.manifest.dependencies.values(), 0..) |name, *dep, i| {
...@@ -1854,7 +1907,16 @@ fn printPkgTreeChild(...@@ -1854,7 +1907,16 @@ fn printPkgTreeChild(
1854 .parent = parent_print_node,1907 .parent = parent_print_node,
1855 .last = i == names.len - 1,1908 .last = i == names.len - 1,
1856 };1909 };
1857 try printPkgTreeChild(terminal, arena, job_queue, &print_node, name, dep.hash);1910 switch (dep.location) {
1911 .url => {
1912 const child_hash = if (dep.hash) |h| Package.Hash.fromSlice(h) else null;
1913 try printPkgTreeChild(terminal, arena, global_cache_root, job_queue, &print_node, name, child_hash, fork_set);
1914 },
1915 .path => {
1916 const child_hash = Package.Fetch.depDigest(child_fetch.package_root, global_cache_root, dep);
1917 try printPkgTreeChild(terminal, arena, global_cache_root, job_queue, &print_node, name, child_hash, fork_set);
1918 },
1919 }
1858 }1920 }
1859}1921}
18601922
lib/compiler/Maker/Fetch.zig+17-7
...@@ -289,11 +289,21 @@ pub const JobQueue = struct {...@@ -289,11 +289,21 @@ pub const JobQueue = struct {
289 \\289 \\
290 , .{std.fmt.alt(fetch.package_root, .formatEscapeString)});290 , .{std.fmt.alt(fetch.package_root, .formatEscapeString)});
291291
292 if (fetch.has_build_zig) {292 if (fetch.has_build_zig) b: {
293 if (fetch.have_manifest) {
294 const project_id: Package.ProjectId = .init(fetch.manifest.name, fetch.manifest.id);
295 if (jq.fork_set.getKeyPtrAdapted(project_id, @as(JobQueue.Fork.Adapter, .{}))) |fork| {
296 try w.print(
297 \\ pub const build_zig = @import({qf});
298 \\
299 , .{fork.path});
300 }
301 break :b;
302 }
293 try w.print(303 try w.print(
294 \\ pub const build_zig = @import("{f}");304 \\ pub const build_zig = @import({q});
295 \\305 \\
296 , .{std.zig.fmtString(hash_slice)});306 , .{hash_slice});
297 }307 }
298308
299 if (fetch.have_manifest) {309 if (fetch.have_manifest) {
...@@ -302,7 +312,7 @@ pub const JobQueue = struct {...@@ -302,7 +312,7 @@ pub const JobQueue = struct {
302 \\ pub const deps: []const struct { []const u8, []const u8 } = &.{312 \\ pub const deps: []const struct { []const u8, []const u8 } = &.{
303 \\313 \\
304 );314 );
305 for (manifest.dependencies.keys(), manifest.dependencies.values()) |name, dep| {315 for (manifest.dependencies.keys(), manifest.dependencies.values()) |name, *dep| {
306 const h = depDigest(fetch.package_root, jq.global_cache, dep) orelse continue;316 const h = depDigest(fetch.package_root, jq.global_cache, dep) orelse continue;
307 try w.print(317 try w.print(
308 " .{{ \"{f}\", \"{f}\" }},\n",318 " .{{ \"{f}\", \"{f}\" }},\n",
...@@ -335,7 +345,7 @@ pub const JobQueue = struct {...@@ -335,7 +345,7 @@ pub const JobQueue = struct {
335 assert(root_fetch.have_manifest);345 assert(root_fetch.have_manifest);
336 const root_manifest = &root_fetch.manifest;346 const root_manifest = &root_fetch.manifest;
337347
338 for (root_manifest.dependencies.keys(), root_manifest.dependencies.values()) |name, dep| {348 for (root_manifest.dependencies.keys(), root_manifest.dependencies.values()) |name, *dep| {
339 const h = depDigest(root_fetch.package_root, jq.global_cache, dep) orelse continue;349 const h = depDigest(root_fetch.package_root, jq.global_cache, dep) orelse continue;
340 try w.print(350 try w.print(
341 " .{{ \"{f}\", \"{f}\" }},\n",351 " .{{ \"{f}\", \"{f}\" }},\n",
...@@ -595,7 +605,7 @@ pub fn run(f: *Fetch) RunError!void {...@@ -595,7 +605,7 @@ pub fn run(f: *Fetch) RunError!void {
595 if (remote.hash) |expected_hash| {605 if (remote.hash) |expected_hash| {
596 const expected_project_id: Package.ProjectId = expected_hash.projectId();606 const expected_project_id: Package.ProjectId = expected_hash.projectId();
597 if (job_queue.fork_set.getKeyPtrAdapted(expected_project_id, @as(JobQueue.Fork.Adapter, .{}))) |fork| {607 if (job_queue.fork_set.getKeyPtrAdapted(expected_project_id, @as(JobQueue.Fork.Adapter, .{}))) |fork| {
598 log.debug("using fork {f} for {s}", .{ fork.path, fork.manifest.name });608 log.debug("using fork {f} for {s}", .{ fork.path, expected_hash.toSlice() });
599 fork.uses += 1;609 fork.uses += 1;
600 f.package_root = fork.path;610 f.package_root = fork.path;
601 f.remote_package_root = f.package_root;611 f.remote_package_root = f.package_root;
...@@ -2033,7 +2043,7 @@ const Filter = struct {...@@ -2033,7 +2043,7 @@ const Filter = struct {
2033 }2043 }
2034};2044};
20352045
2036pub fn depDigest(pkg_root: Path, cache_root: Directory, dep: Manifest.Dependency) ?Package.Hash {2046pub fn depDigest(pkg_root: Path, cache_root: Directory, dep: *const Manifest.Dependency) ?Package.Hash {
2037 if (dep.hash) |h| return .fromSlice(h);2047 if (dep.hash) |h| return .fromSlice(h);
20382048
2039 switch (dep.location) {2049 switch (dep.location) {