From 55678000287b31c9549b39ad79eaf4f55753b547 Mon Sep 17 00:00:00 2001 From: andrewtdiz <59515127+andrewtdiz@users.noreply.github.com> Date: Thu, 16 Oct 2025 11:23:20 -0700 Subject: [PATCH] 0.16 support --- build.zig.zon | 2 +- build_readme.zig | 2 +- src/Walk.zig | 2 +- src/build_runner_0.16.zig | 228 ++++++++++++++++++++++++++++++++++++++ src/main.zig | 25 +++-- 5 files changed, 247 insertions(+), 12 deletions(-) create mode 100644 src/build_runner_0.16.zig diff --git a/build.zig.zon b/build.zig.zon index e04aca8..50da950 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -2,7 +2,7 @@ .name = .zigdoc, .version = "0.0.0", .fingerprint = 0x50a871f441c1a7ed, - .minimum_zig_version = "0.15.1", + .minimum_zig_version = "0.16.0", .dependencies = .{}, .paths = .{ "build.zig", diff --git a/build_readme.zig b/build_readme.zig index f430e14..fef18fa 100644 --- a/build_readme.zig +++ b/build_readme.zig @@ -16,7 +16,7 @@ pub fn main() !void { const help_file = args[1]; const output_file = args[2]; - const help_content = try std.fs.cwd().readFileAlloc(allocator, help_file, 1024 * 1024); + const help_content = try std.fs.cwd().readFileAlloc(help_file, allocator, std.Io.Limit.limited(1024 * 1024)); defer allocator.free(help_content); const readme = try std.fmt.allocPrint(allocator, diff --git a/src/Walk.zig b/src/Walk.zig index 88a720f..f48c098 100644 --- a/src/Walk.zig +++ b/src/Walk.zig @@ -353,7 +353,7 @@ pub const File = struct { if (files.getIndex(resolved_path)) |imported_file_index| { return Category.makeAlias(File.Index.findRootDecl(@enumFromInt(imported_file_index)), node); } else { - const import_content = std.fs.cwd().readFileAlloc(gpa, resolved_path, 10 * 1024 * 1024) catch |err| { + const import_content = std.fs.cwd().readFileAlloc(resolved_path, gpa, std.Io.Limit.limited(10 * 1024 * 1024)) catch |err| { log.warn("import target '{s}' could not be read: {}", .{ resolved_path, err }); return .{ .global_const = node }; }; diff --git a/src/build_runner_0.16.zig b/src/build_runner_0.16.zig new file mode 100644 index 0000000..af699e0 --- /dev/null +++ b/src/build_runner_0.16.zig @@ -0,0 +1,228 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +pub const root = @import("@build"); +pub const dependencies = @import("@dependencies"); + +pub const std_options: std.Options = .{ + .side_channels_mitigations = .none, +}; + +pub fn main() !void { + var gpa_instance = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa_instance.deinit(); + const gpa = gpa_instance.allocator(); + + var single_threaded_arena = std.heap.ArenaAllocator.init(gpa); + defer single_threaded_arena.deinit(); + + var thread_safe_arena: std.heap.ThreadSafeAllocator = .{ + .child_allocator = single_threaded_arena.allocator(), + }; + const arena = thread_safe_arena.allocator(); + + const args = try std.process.argsAlloc(arena); + + // skip my own exe name + var arg_idx: usize = 1; + + const zig_exe = args[arg_idx]; + arg_idx += 1; + const zig_lib_dir = args[arg_idx]; + arg_idx += 1; + const build_root = args[arg_idx]; + arg_idx += 1; + const cache_root = args[arg_idx]; + arg_idx += 1; + const global_cache_root = args[arg_idx]; + arg_idx += 1; + + const zig_lib_directory: std.Build.Cache.Directory = .{ + .path = zig_lib_dir, + .handle = try std.fs.cwd().openDir(zig_lib_dir, .{}), + }; + + const build_root_directory: std.Build.Cache.Directory = .{ + .path = build_root, + .handle = try std.fs.cwd().openDir(build_root, .{}), + }; + + const local_cache_directory: std.Build.Cache.Directory = .{ + .path = cache_root, + .handle = try std.fs.cwd().makeOpenPath(cache_root, .{}), + }; + + const global_cache_directory: std.Build.Cache.Directory = .{ + .path = global_cache_root, + .handle = try std.fs.cwd().makeOpenPath(global_cache_root, .{}), + }; + + // Version-specific Graph initialization + const has_time_report = @hasField(std.Build.Graph, "time_report"); + + var graph: std.Build.Graph = if (has_time_report) .{ + .arena = arena, + .cache = .{ + .gpa = arena, + .manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}), + }, + .zig_exe = zig_exe, + .env_map = try std.process.getEnvMap(arena), + .global_cache_root = global_cache_directory, + .zig_lib_directory = zig_lib_directory, + .host = .{ + .query = .{}, + .result = try std.zig.system.resolveTargetQuery(.{}), + }, + .time_report = false, + } else .{ + .arena = arena, + .cache = .{ + .gpa = arena, + .manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}), + }, + .zig_exe = zig_exe, + .env_map = try std.process.getEnvMap(arena), + .global_cache_root = global_cache_directory, + .zig_lib_directory = zig_lib_directory, + .host = .{ + .query = .{}, + .result = try std.zig.system.resolveTargetQuery(.{}), + }, + }; + + graph.cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() }); + graph.cache.addPrefix(build_root_directory); + graph.cache.addPrefix(local_cache_directory); + graph.cache.addPrefix(global_cache_directory); + graph.cache.hash.addBytes(builtin.zig_version_string); + + const builder = try std.Build.create( + &graph, + build_root_directory, + local_cache_directory, + dependencies.root_deps, + ); + + // Initialize install_path - required before calling build() + builder.resolveInstallPrefix(null, .{}); + + // Call the user's build() function + try builder.runBuild(root); + + // NOW WE HAVE THE BUILD GRAPH! + // Instead of executing it, let's dump information about it + + // Use a separate allocator for our module collection to avoid interfering with build graph + var module_gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = module_gpa.deinit(); + const our_allocator = module_gpa.allocator(); + + // Buffer output to avoid version-specific writer APIs + var stdout_buf = std.array_list.Aligned(u8, null){}; + const stdout = stdout_buf.writer(arena); + + // Collect all modules - from builder.modules and compile steps + var all_modules = std.StringHashMap(*std.Build.Module).init(our_allocator); + + // Add global modules + var global_iter = builder.modules.iterator(); + while (global_iter.next()) |entry| { + try all_modules.put(entry.key_ptr.*, entry.value_ptr.*); + } + + // Walk compile steps to find their root modules and imports + var visited_steps = std.AutoHashMap(*std.Build.Step, void).init(our_allocator); + var step_iter = builder.top_level_steps.iterator(); + while (step_iter.next()) |entry| { + const tls = entry.value_ptr.*; + try collectStepModules(&all_modules, &tls.step, &visited_steps); + } + + // Output in JSON format + try stdout.writeAll("{\n"); + try stdout.writeAll(" \"modules\": {\n"); + + var module_iter = all_modules.iterator(); + var first_module = true; + while (module_iter.next()) |mod_entry| { + const import_name = mod_entry.key_ptr.*; + const module = mod_entry.value_ptr.*; + const root_source = if (module.root_source_file) |rsf| blk: { + // Skip generated files - they don't have a real path yet + if (rsf == .generated) break :blk null; + break :blk rsf.getPath2(builder, null); + } else null; + + if (root_source) |root_path| { + if (!first_module) try stdout.writeAll(",\n"); + first_module = false; + + try stdout.print(" \"{s}\": {{\n", .{import_name}); + try stdout.print(" \"root\": \"{s}\"", .{root_path}); + + if (module.import_table.count() > 0) { + try stdout.writeAll(",\n \"imports\": {\n"); + var dep_iter = module.import_table.iterator(); + var first_dep = true; + while (dep_iter.next()) |dep| { + const dep_name = dep.key_ptr.*; + const dep_module = dep.value_ptr.*; + const dep_root = if (dep_module.root_source_file) |rsf| blk: { + if (rsf == .generated) break :blk null; + break :blk rsf.getPath2(builder, null); + } else null; + if (dep_root) |droot| { + if (!first_dep) try stdout.writeAll(",\n"); + first_dep = false; + try stdout.print(" \"{s}\": \"{s}\"", .{ dep_name, droot }); + } + } + try stdout.writeAll("\n }\n"); + } else { + try stdout.writeAll("\n"); + } + + try stdout.writeAll(" }"); + } + } + + try stdout.writeAll("\n }\n"); + try stdout.writeAll("}\n"); + + // Write buffered output to stdout (version-compatible) + if (@hasDecl(std.io, "getStdOut")) { + try std.io.getStdOut().writer().writeAll(stdout_buf.items); + } else { + var buf: [8192]u8 = undefined; + var writer = std.fs.File.stdout().writer(&buf); + try writer.interface.writeAll(stdout_buf.items); + try writer.interface.flush(); + } +} + +fn collectStepModules( + modules: *std.StringHashMap(*std.Build.Module), + step: *std.Build.Step, + visited: *std.AutoHashMap(*std.Build.Step, void), +) !void { + // Avoid infinite recursion on circular dependencies + if (visited.contains(step)) return; + try visited.put(step, {}); + + // Check if this is a compile step + if (step.cast(std.Build.Step.Compile)) |compile_step| { + // Add imports from this compile step's root module + var iter = compile_step.root_module.import_table.iterator(); + while (iter.next()) |entry| { + const import_name = entry.key_ptr.*; + const module = entry.value_ptr.*; + try modules.put(import_name, module); + } + } + + // Recursively check dependencies + for (step.dependencies.items) |dep_step| { + try collectStepModules(modules, dep_step, visited); + } +} diff --git a/src/main.zig b/src/main.zig index f0acd44..a3db754 100644 --- a/src/main.zig +++ b/src/main.zig @@ -5,6 +5,7 @@ const log = std.log.scoped(.zigdoc); const build_runner_0_14 = @embedFile("build_runner_0.14.zig"); const build_runner_0_15 = @embedFile("build_runner_0.15.zig"); +const build_runner_0_16 = @embedFile("build_runner_0.16.zig"); pub fn main() !void { var debug_allocator: std.heap.DebugAllocator(.{}) = .init; @@ -145,6 +146,7 @@ fn setupBuildRunner(arena: *std.heap.ArenaAllocator) !void { const runner_src = switch (version.minor) { 14 => build_runner_0_14, 15 => build_runner_0_15, + 16 => build_runner_0_16, else => return error.UnsupportedZigVersion, }; @@ -209,9 +211,9 @@ fn parseBuildOutput(allocator: std.mem.Allocator, output: []const u8) !void { // Read and add the module file const file_content = std.fs.cwd().readFileAlloc( - allocator, root_path, - 10 * 1024 * 1024, + allocator, + std.Io.Limit.limited(10 * 1024 * 1024), ) catch |err| { std.debug.print("Failed to read module {s}: {}\n", .{ module_name, err }); continue; @@ -232,9 +234,9 @@ fn parseBuildOutput(allocator: std.mem.Allocator, output: []const u8) !void { // Read and add the imported file const import_content = std.fs.cwd().readFileAlloc( - allocator, import_path, - 10 * 1024 * 1024, + allocator, + std.Io.Limit.limited(10 * 1024 * 1024), ) catch |err| { std.debug.print("Failed to read import {s}: {}\n", .{ import_name, err }); continue; @@ -272,7 +274,7 @@ fn getStdDir(arena: *std.heap.ArenaAllocator) ![]const u8 { ); return parsed.value.std_dir; } else { - const parsed = try std.zon.parse.fromSlice( + const parsed = try std.zon.parse.fromSliceAlloc( ZigEnv, arena.allocator(), stdout, @@ -297,15 +299,20 @@ fn walkStdLib(arena: *std.heap.ArenaAllocator, std_dir_path: []const u8) !void { if (std.mem.endsWith(u8, entry.basename, "test.zig")) continue; const file_content = try entry.dir.readFileAllocOptions( - allocator, entry.basename, - 10 * 1024 * 1024, - null, + allocator, + std.Io.Limit.limited(10 * 1024 * 1024), @enumFromInt(0), 0, ); - const file_name = try std.fmt.allocPrint(allocator, "std/{s}", .{entry.path}); + // Normalize path separators to forward slashes for consistent handling + const normalized_path = try allocator.dupe(u8, entry.path); + for (normalized_path) |*ch| { + if (ch.* == '\\') ch.* = '/'; + } + const file_name = try std.fmt.allocPrint(allocator, "std/{s}", .{normalized_path}); + allocator.free(normalized_path); _ = try Walk.add_file(file_name, file_content); }