Skip to content

std.Build: some miscelleanous bits #24205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 17, 2025
Merged
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
4 changes: 3 additions & 1 deletion lib/std/Build/Cache/Path.zig
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ pub fn join(p: Path, arena: Allocator, sub_path: []const u8) Allocator.Error!Pat

pub fn resolvePosix(p: Path, arena: Allocator, sub_path: []const u8) Allocator.Error!Path {
if (sub_path.len == 0) return p;
const new_sub_path = try fs.path.resolvePosix(arena, &.{ p.sub_path, sub_path });
return .{
.root_dir = p.root_dir,
.sub_path = try fs.path.resolvePosix(arena, &.{ p.sub_path, sub_path }),
// Use "" instead of "." to represent `root_dir` itself.
.sub_path = if (std.mem.eql(u8, new_sub_path, ".")) "" else new_sub_path,
};
}

Expand Down
50 changes: 18 additions & 32 deletions lib/std/Build/Module.zig
Original file line number Diff line number Diff line change
Expand Up @@ -173,39 +173,25 @@ pub const IncludeDir = union(enum) {
zig_args: *std.ArrayList([]const u8),
asking_step: ?*Step,
) !void {
switch (include_dir) {
.path => |include_path| {
try zig_args.appendSlice(&.{ "-I", include_path.getPath2(b, asking_step) });
const flag: []const u8, const lazy_path: LazyPath = switch (include_dir) {
// zig fmt: off
.path => |lp| .{ "-I", lp },
.path_system => |lp| .{ "-isystem", lp },
.path_after => |lp| .{ "-idirafter", lp },
.framework_path => |lp| .{ "-F", lp },
.framework_path_system => |lp| .{ "-iframework", lp },
.config_header_step => |ch| .{ "-I", ch.getOutputDir() },
.other_step => |comp| .{ "-I", comp.installed_headers_include_tree.?.getDirectory() },
// zig fmt: on
.embed_path => |lazy_path| {
// Special case: this is a single arg.
const resolved = lazy_path.getPath3(b, asking_step);
const arg = b.fmt("--embed-dir={}", .{resolved});
return zig_args.append(arg);
},
.path_system => |include_path| {
try zig_args.appendSlice(&.{ "-isystem", include_path.getPath2(b, asking_step) });
},
.path_after => |include_path| {
try zig_args.appendSlice(&.{ "-idirafter", include_path.getPath2(b, asking_step) });
},
.framework_path => |include_path| {
try zig_args.appendSlice(&.{ "-F", include_path.getPath2(b, asking_step) });
},
.framework_path_system => |include_path| {
try zig_args.appendSlice(&.{ "-iframework", include_path.getPath2(b, asking_step) });
},
.other_step => |other| {
if (other.generated_h) |header| {
try zig_args.appendSlice(&.{ "-isystem", std.fs.path.dirname(header.getPath()).? });
}
if (other.installed_headers_include_tree) |include_tree| {
try zig_args.appendSlice(&.{ "-I", include_tree.generated_directory.getPath() });
}
},
.config_header_step => |config_header| {
const full_file_path = config_header.output_file.getPath();
const header_dir_path = full_file_path[0 .. full_file_path.len - config_header.include_path.len];
try zig_args.appendSlice(&.{ "-I", header_dir_path });
},
.embed_path => |embed_path| {
try zig_args.append(try std.mem.concat(b.allocator, u8, &.{ "--embed-dir=", embed_path.getPath2(b, asking_step) }));
},
}
};
const resolved_str = try lazy_path.getPath3(b, asking_step).toString(b.graph.arena);
return zig_args.appendSlice(&.{ flag, resolved_str });
}
};

Expand Down
21 changes: 13 additions & 8 deletions lib/std/Build/Step/ConfigHeader.zig
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ pub const Value = union(enum) {

step: Step,
values: std.StringArrayHashMap(Value),
output_file: std.Build.GeneratedFile,
/// This directory contains the generated file under the name `include_path`.
generated_dir: std.Build.GeneratedFile,

style: Style,
max_bytes: usize,
Expand Down Expand Up @@ -99,7 +100,7 @@ pub fn create(owner: *std.Build, options: Options) *ConfigHeader {
.max_bytes = options.max_bytes,
.include_path = include_path,
.include_guard_override = options.include_guard_override,
.output_file = .{ .step = &config_header.step },
.generated_dir = .{ .step = &config_header.step },
};

return config_header;
Expand All @@ -115,9 +116,15 @@ pub fn addValues(config_header: *ConfigHeader, values: anytype) void {
}
}

pub fn getOutput(config_header: *ConfigHeader) std.Build.LazyPath {
return .{ .generated = .{ .file = &config_header.output_file } };
pub fn getOutputDir(ch: *ConfigHeader) std.Build.LazyPath {
return .{ .generated = .{ .file = &ch.generated_dir } };
}
pub fn getOutputFile(ch: *ConfigHeader) std.Build.LazyPath {
return ch.getOutputDir().path(ch.step.owner, ch.include_path);
}

/// Deprecated; use `getOutputFile`.
pub const getOutput = getOutputFile;

fn addValueInner(config_header: *ConfigHeader, name: []const u8, comptime T: type, value: T) !void {
switch (@typeInfo(T)) {
Expand Down Expand Up @@ -234,9 +241,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {

if (try step.cacheHit(&man)) {
const digest = man.final();
config_header.output_file.path = try b.cache_root.join(arena, &.{
"o", &digest, config_header.include_path,
});
config_header.generated_dir.path = try b.cache_root.join(arena, &.{ "o", &digest });
return;
}

Expand All @@ -262,7 +267,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
});
};

config_header.output_file.path = try b.cache_root.join(arena, &.{sub_path});
config_header.generated_dir.path = try b.cache_root.join(arena, &.{ "o", &digest });
try man.writeManifest();
}

Expand Down
57 changes: 26 additions & 31 deletions test/standalone/cmakedefine/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -71,40 +71,35 @@ pub fn build(b: *std.Build) void {
},
);

const check_exe = b.addExecutable(.{
.name = "check",
.root_module = b.createModule(.{
.target = b.graph.host,
.root_source_file = b.path("check.zig"),
}),
});

const test_step = b.step("test", "Test it");
test_step.makeFn = compare_headers;
test_step.dependOn(&config_header.step);
test_step.dependOn(&pwd_sh.step);
test_step.dependOn(&sigil_header.step);
test_step.dependOn(&stack_header.step);
test_step.dependOn(&wrapper_header.step);
b.default_step = test_step;
test_step.dependOn(addCheck(b, check_exe, config_header));
test_step.dependOn(addCheck(b, check_exe, pwd_sh));
test_step.dependOn(addCheck(b, check_exe, sigil_header));
test_step.dependOn(addCheck(b, check_exe, stack_header));
test_step.dependOn(addCheck(b, check_exe, wrapper_header));
}

fn compare_headers(step: *std.Build.Step, options: std.Build.Step.MakeOptions) !void {
_ = options;
const allocator = step.owner.allocator;
const expected_fmt = "expected_{s}";

for (step.dependencies.items) |config_header_step| {
const config_header: *ConfigHeader = @fieldParentPtr("step", config_header_step);

const zig_header_path = config_header.output_file.path orelse @panic("Could not locate header file");

const cwd = std.fs.cwd();

const cmake_header_path = try std.fmt.allocPrint(allocator, expected_fmt, .{std.fs.path.basename(zig_header_path)});
defer allocator.free(cmake_header_path);

const cmake_header = try cwd.readFileAlloc(allocator, cmake_header_path, config_header.max_bytes);
defer allocator.free(cmake_header);

const zig_header = try cwd.readFileAlloc(allocator, zig_header_path, config_header.max_bytes);
defer allocator.free(zig_header);
fn addCheck(
b: *std.Build,
check_exe: *std.Build.Step.Compile,
ch: *ConfigHeader,
) *std.Build.Step {
// We expect `ch.include_path` to only be a basename to infer where the expected output is.
std.debug.assert(std.fs.path.dirname(ch.include_path) == null);
const expected_path = b.fmt("expected_{s}", .{ch.include_path});

const header_text_index = std.mem.indexOf(u8, zig_header, "\n") orelse @panic("Could not find comment in header filer");
const run_check = b.addRunArtifact(check_exe);
run_check.addFileArg(ch.getOutputFile());
run_check.addFileArg(b.path(expected_path));

if (!std.mem.eql(u8, zig_header[header_text_index + 1 ..], cmake_header)) {
@panic("processed cmakedefine header does not match expected output");
}
}
return &run_check.step;
}
26 changes: 26 additions & 0 deletions test/standalone/cmakedefine/check.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
pub fn main() !void {
var arena_state: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
defer arena_state.deinit();
const arena = arena_state.allocator();

const args = try std.process.argsAlloc(arena);

if (args.len != 3) return error.BadUsage;
const actual_path = args[1];
const expected_path = args[2];

const actual = try std.fs.cwd().readFileAlloc(arena, actual_path, 1024 * 1024);
const expected = try std.fs.cwd().readFileAlloc(arena, expected_path, 1024 * 1024);

// The actual output starts with a comment which we should strip out before comparing.
const comment_str = "/* This file was generated by ConfigHeader using the Zig Build System. */\n";
if (!std.mem.startsWith(u8, actual, comment_str)) {
return error.MissingOrMalformedComment;
}
const actual_without_comment = actual[comment_str.len..];

if (!std.mem.eql(u8, actual_without_comment, expected)) {
return error.DoesNotMatch;
}
}
const std = @import("std");
Loading