Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion build_readme.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/Walk.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
};
Expand Down
228 changes: 228 additions & 0 deletions src/build_runner_0.16.zig
Original file line number Diff line number Diff line change
@@ -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);
}
}
25 changes: 16 additions & 9 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
};

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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);
}
Expand Down