| author | |
| committer | |
| log | f3c0555975053a6d3ffd9bc239b732658aebe5d6 |
| tree | 4b36410576c6386ce515be41c1b9a388e7780c4d |
| parent | a92427bf275bc85d4a0c5f086bbb8156f00f2b6c |
| signature |
`std.Build.Step.ConfigHeader` emits a *directory* containing a config
header under a given sub path, but there's no good way to actually
access that directory as a `LazyPath` in the configure phase. This is
silly; it's perfectly valid to refer to that directory, perhaps to
explicitly pass as a "-I" flag to a different toolchain invoked via a
`Step.Run`. So now, instead of the `GeneratedFile` being the actual
*file*, it should be that *directory*, i.e. `cache/o/<digest>`. We can
then easily get the *file* if needed just by using `LazyPath.path` to go
"deeper", which there is a helper function for.
The legacy `getOutput` function is now a deprecated alias for
`getOutputFile`, and `getOutputDir` is introduced.
`std.Build.Module.IncludeDir.appendZigProcessFlags` needed a fix after
this change, so I took the opportunity to refactor it a little. I was
looking at this function while working on ziglang/translate-c yesterday
and realised it could be expressed much more simply -- particularly
after the `ConfigHeader` change here.
I had to update the test `standalone/cmakedefine/` -- it turns out this
test was well and truly reaching into build system internals, and doing
horrible not-really-allowed stuff like overriding the `makeFn` of a
`TopLevelStep`. To top it all off, the test forgot to set
`b.default_step` to its "test" step, so the test never even ran. I've
refactored it to follow accepted practices and to actually, like, work.4 files changed, 83 insertions(+), 71 deletions(-)
lib/std/Build/Module.zig+18-32| ... | @@ -173,39 +173,25 @@ pub const IncludeDir = union(enum) { | ... | @@ -173,39 +173,25 @@ pub const IncludeDir = union(enum) { |
| 173 | zig_args: *std.ArrayList([]const u8), | 173 | zig_args: *std.ArrayList([]const u8), |
| 174 | asking_step: ?*Step, | 174 | asking_step: ?*Step, |
| 175 | ) !void { | 175 | ) !void { |
| 176 | switch (include_dir) { | 176 | const flag: []const u8, const lazy_path: LazyPath = switch (include_dir) { |
| 177 | .path => |include_path| { | 177 | // zig fmt: off |
| 178 | try zig_args.appendSlice(&.{ "-I", include_path.getPath2(b, asking_step) }); | 178 | .path => |lp| .{ "-I", lp }, |
| 179 | .path_system => |lp| .{ "-isystem", lp }, | ||
| 180 | .path_after => |lp| .{ "-idirafter", lp }, | ||
| 181 | .framework_path => |lp| .{ "-F", lp }, | ||
| 182 | .framework_path_system => |lp| .{ "-iframework", lp }, | ||
| 183 | .config_header_step => |ch| .{ "-I", ch.getOutputDir() }, | ||
| 184 | .other_step => |comp| .{ "-I", comp.installed_headers_include_tree.?.getDirectory() }, | ||
| 185 | // zig fmt: on | ||
| 186 | .embed_path => |lazy_path| { | ||
| 187 | // Special case: this is a single arg. | ||
| 188 | const resolved = lazy_path.getPath3(b, asking_step); | ||
| 189 | const arg = b.fmt("--embed-dir={}", .{resolved}); | ||
| 190 | return zig_args.append(arg); | ||
| 179 | }, | 191 | }, |
| 180 | .path_system => |include_path| { | 192 | }; |
| 181 | try zig_args.appendSlice(&.{ "-isystem", include_path.getPath2(b, asking_step) }); | 193 | const resolved_str = try lazy_path.getPath3(b, asking_step).toString(b.graph.arena); |
| 182 | }, | 194 | return zig_args.appendSlice(&.{ flag, resolved_str }); |
| 183 | .path_after => |include_path| { | ||
| 184 | try zig_args.appendSlice(&.{ "-idirafter", include_path.getPath2(b, asking_step) }); | ||
| 185 | }, | ||
| 186 | .framework_path => |include_path| { | ||
| 187 | try zig_args.appendSlice(&.{ "-F", include_path.getPath2(b, asking_step) }); | ||
| 188 | }, | ||
| 189 | .framework_path_system => |include_path| { | ||
| 190 | try zig_args.appendSlice(&.{ "-iframework", include_path.getPath2(b, asking_step) }); | ||
| 191 | }, | ||
| 192 | .other_step => |other| { | ||
| 193 | if (other.generated_h) |header| { | ||
| 194 | try zig_args.appendSlice(&.{ "-isystem", std.fs.path.dirname(header.getPath()).? }); | ||
| 195 | } | ||
| 196 | if (other.installed_headers_include_tree) |include_tree| { | ||
| 197 | try zig_args.appendSlice(&.{ "-I", include_tree.generated_directory.getPath() }); | ||
| 198 | } | ||
| 199 | }, | ||
| 200 | .config_header_step => |config_header| { | ||
| 201 | const full_file_path = config_header.output_file.getPath(); | ||
| 202 | const header_dir_path = full_file_path[0 .. full_file_path.len - config_header.include_path.len]; | ||
| 203 | try zig_args.appendSlice(&.{ "-I", header_dir_path }); | ||
| 204 | }, | ||
| 205 | .embed_path => |embed_path| { | ||
| 206 | try zig_args.append(try std.mem.concat(b.allocator, u8, &.{ "--embed-dir=", embed_path.getPath2(b, asking_step) })); | ||
| 207 | }, | ||
| 208 | } | ||
| 209 | } | 195 | } |
| 210 | }; | 196 | }; |
| 211 | 197 |
lib/std/Build/Step/ConfigHeader.zig+13-8| ... | @@ -39,7 +39,8 @@ pub const Value = union(enum) { | ... | @@ -39,7 +39,8 @@ pub const Value = union(enum) { |
| 39 | 39 | ||
| 40 | step: Step, | 40 | step: Step, |
| 41 | values: std.StringArrayHashMap(Value), | 41 | values: std.StringArrayHashMap(Value), |
| 42 | output_file: std.Build.GeneratedFile, | 42 | /// This directory contains the generated file under the name `include_path`. |
| 43 | generated_dir: std.Build.GeneratedFile, | ||
| 43 | 44 | ||
| 44 | style: Style, | 45 | style: Style, |
| 45 | max_bytes: usize, | 46 | max_bytes: usize, |
| ... | @@ -99,7 +100,7 @@ pub fn create(owner: *std.Build, options: Options) *ConfigHeader { | ... | @@ -99,7 +100,7 @@ pub fn create(owner: *std.Build, options: Options) *ConfigHeader { |
| 99 | .max_bytes = options.max_bytes, | 100 | .max_bytes = options.max_bytes, |
| 100 | .include_path = include_path, | 101 | .include_path = include_path, |
| 101 | .include_guard_override = options.include_guard_override, | 102 | .include_guard_override = options.include_guard_override, |
| 102 | .output_file = .{ .step = &config_header.step }, | 103 | .generated_dir = .{ .step = &config_header.step }, |
| 103 | }; | 104 | }; |
| 104 | 105 | ||
| 105 | return config_header; | 106 | return config_header; |
| ... | @@ -115,9 +116,15 @@ pub fn addValues(config_header: *ConfigHeader, values: anytype) void { | ... | @@ -115,9 +116,15 @@ pub fn addValues(config_header: *ConfigHeader, values: anytype) void { |
| 115 | } | 116 | } |
| 116 | } | 117 | } |
| 117 | 118 | ||
| 118 | pub fn getOutput(config_header: *ConfigHeader) std.Build.LazyPath { | 119 | pub fn getOutputDir(ch: *ConfigHeader) std.Build.LazyPath { |
| 119 | return .{ .generated = .{ .file = &config_header.output_file } }; | 120 | return .{ .generated = .{ .file = &ch.generated_dir } }; |
| 120 | } | 121 | } |
| 122 | pub fn getOutputFile(ch: *ConfigHeader) std.Build.LazyPath { | ||
| 123 | return ch.getOutputDir().path(ch.step.owner, ch.include_path); | ||
| 124 | } | ||
| 125 | |||
| 126 | /// Deprecated; use `getOutputFile`. | ||
| 127 | pub const getOutput = getOutputFile; | ||
| 121 | 128 | ||
| 122 | fn addValueInner(config_header: *ConfigHeader, name: []const u8, comptime T: type, value: T) !void { | 129 | fn addValueInner(config_header: *ConfigHeader, name: []const u8, comptime T: type, value: T) !void { |
| 123 | switch (@typeInfo(T)) { | 130 | switch (@typeInfo(T)) { |
| ... | @@ -234,9 +241,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { | ... | @@ -234,9 +241,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 234 | 241 | ||
| 235 | if (try step.cacheHit(&man)) { | 242 | if (try step.cacheHit(&man)) { |
| 236 | const digest = man.final(); | 243 | const digest = man.final(); |
| 237 | config_header.output_file.path = try b.cache_root.join(arena, &.{ | 244 | config_header.generated_dir.path = try b.cache_root.join(arena, &.{ "o", &digest }); |
| 238 | "o", &digest, config_header.include_path, | ||
| 239 | }); | ||
| 240 | return; | 245 | return; |
| 241 | } | 246 | } |
| 242 | 247 | ||
| ... | @@ -262,7 +267,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { | ... | @@ -262,7 +267,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 262 | }); | 267 | }); |
| 263 | }; | 268 | }; |
| 264 | 269 | ||
| 265 | config_header.output_file.path = try b.cache_root.join(arena, &.{sub_path}); | 270 | config_header.generated_dir.path = try b.cache_root.join(arena, &.{ "o", &digest }); |
| 266 | try man.writeManifest(); | 271 | try man.writeManifest(); |
| 267 | } | 272 | } |
| 268 | 273 |
test/standalone/cmakedefine/build.zig+26-31| ... | @@ -71,40 +71,35 @@ pub fn build(b: *std.Build) void { | ... | @@ -71,40 +71,35 @@ pub fn build(b: *std.Build) void { |
| 71 | }, | 71 | }, |
| 72 | ); | 72 | ); |
| 73 | 73 | ||
| 74 | const check_exe = b.addExecutable(.{ | ||
| 75 | .name = "check", | ||
| 76 | .root_module = b.createModule(.{ | ||
| 77 | .target = b.graph.host, | ||
| 78 | .root_source_file = b.path("check.zig"), | ||
| 79 | }), | ||
| 80 | }); | ||
| 81 | |||
| 74 | const test_step = b.step("test", "Test it"); | 82 | const test_step = b.step("test", "Test it"); |
| 75 | test_step.makeFn = compare_headers; | 83 | b.default_step = test_step; |
| 76 | test_step.dependOn(&config_header.step); | 84 | test_step.dependOn(addCheck(b, check_exe, config_header)); |
| 77 | test_step.dependOn(&pwd_sh.step); | 85 | test_step.dependOn(addCheck(b, check_exe, pwd_sh)); |
| 78 | test_step.dependOn(&sigil_header.step); | 86 | test_step.dependOn(addCheck(b, check_exe, sigil_header)); |
| 79 | test_step.dependOn(&stack_header.step); | 87 | test_step.dependOn(addCheck(b, check_exe, stack_header)); |
| 80 | test_step.dependOn(&wrapper_header.step); | 88 | test_step.dependOn(addCheck(b, check_exe, wrapper_header)); |
| 81 | } | 89 | } |
| 82 | 90 | ||
| 83 | fn compare_headers(step: *std.Build.Step, options: std.Build.Step.MakeOptions) !void { | 91 | fn addCheck( |
| 84 | _ = options; | 92 | b: *std.Build, |
| 85 | const allocator = step.owner.allocator; | 93 | check_exe: *std.Build.Step.Compile, |
| 86 | const expected_fmt = "expected_{s}"; | 94 | ch: *ConfigHeader, |
| 87 | 95 | ) *std.Build.Step { | |
| 88 | for (step.dependencies.items) |config_header_step| { | 96 | // We expect `ch.include_path` to only be a basename to infer where the expected output is. |
| 89 | const config_header: *ConfigHeader = @fieldParentPtr("step", config_header_step); | 97 | std.debug.assert(std.fs.path.dirname(ch.include_path) == null); |
| 90 | 98 | const expected_path = b.fmt("expected_{s}", .{ch.include_path}); | |
| 91 | const zig_header_path = config_header.output_file.path orelse @panic("Could not locate header file"); | ||
| 92 | |||
| 93 | const cwd = std.fs.cwd(); | ||
| 94 | |||
| 95 | const cmake_header_path = try std.fmt.allocPrint(allocator, expected_fmt, .{std.fs.path.basename(zig_header_path)}); | ||
| 96 | defer allocator.free(cmake_header_path); | ||
| 97 | |||
| 98 | const cmake_header = try cwd.readFileAlloc(allocator, cmake_header_path, config_header.max_bytes); | ||
| 99 | defer allocator.free(cmake_header); | ||
| 100 | |||
| 101 | const zig_header = try cwd.readFileAlloc(allocator, zig_header_path, config_header.max_bytes); | ||
| 102 | defer allocator.free(zig_header); | ||
| 103 | 99 | ||
| 104 | const header_text_index = std.mem.indexOf(u8, zig_header, "\n") orelse @panic("Could not find comment in header filer"); | 100 | const run_check = b.addRunArtifact(check_exe); |
| 101 | run_check.addFileArg(ch.getOutputFile()); | ||
| 102 | run_check.addFileArg(b.path(expected_path)); | ||
| 105 | 103 | ||
| 106 | if (!std.mem.eql(u8, zig_header[header_text_index + 1 ..], cmake_header)) { | 104 | return &run_check.step; |
| 107 | @panic("processed cmakedefine header does not match expected output"); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | } | 105 | } |
test/standalone/cmakedefine/check.zig created+26| ... | @@ -0,0 +1,26 @@ | ||
| 1 | pub fn main() !void { | ||
| 2 | var arena_state: std.heap.ArenaAllocator = .init(std.heap.page_allocator); | ||
| 3 | defer arena_state.deinit(); | ||
| 4 | const arena = arena_state.allocator(); | ||
| 5 | |||
| 6 | const args = try std.process.argsAlloc(arena); | ||
| 7 | |||
| 8 | if (args.len != 3) return error.BadUsage; | ||
| 9 | const actual_path = args[1]; | ||
| 10 | const expected_path = args[2]; | ||
| 11 | |||
| 12 | const actual = try std.fs.cwd().readFileAlloc(arena, actual_path, 1024 * 1024); | ||
| 13 | const expected = try std.fs.cwd().readFileAlloc(arena, expected_path, 1024 * 1024); | ||
| 14 | |||
| 15 | // The actual output starts with a comment which we should strip out before comparing. | ||
| 16 | const comment_str = "/* This file was generated by ConfigHeader using the Zig Build System. */\n"; | ||
| 17 | if (!std.mem.startsWith(u8, actual, comment_str)) { | ||
| 18 | return error.MissingOrMalformedComment; | ||
| 19 | } | ||
| 20 | const actual_without_comment = actual[comment_str.len..]; | ||
| 21 | |||
| 22 | if (!std.mem.eql(u8, actual_without_comment, expected)) { | ||
| 23 | return error.DoesNotMatch; | ||
| 24 | } | ||
| 25 | } | ||
| 26 | const std = @import("std"); | ||