diff --git a/lib/compiler/Maker/Step.zig b/lib/compiler/Maker/Step.zig index 5854bbbf25..66e6079ab3 100644 --- a/lib/compiler/Maker/Step.zig +++ b/lib/compiler/Maker/Step.zig @@ -20,6 +20,7 @@ const Maker = @import("../Maker.zig"); pub const CheckFile = @import("Step/CheckFile.zig"); pub const Compile = @import("Step/Compile.zig"); +pub const ConfigHeader = @import("Step/ConfigHeader.zig"); pub const FindProgram = @import("Step/FindProgram.zig"); pub const Fmt = @import("Step/Fmt.zig"); pub const InstallArtifact = @import("Step/InstallArtifact.zig"); @@ -78,7 +79,7 @@ comptime { pub const Extended = union(enum) { check_file: CheckFile, compile: Compile, - config_header: Todo, + config_header: ConfigHeader, fail: Fail, find_program: FindProgram, fmt: Fmt, @@ -114,21 +115,6 @@ pub const Extended = union(enum) { }; } - pub const Todo = struct { - pub fn make( - todo: *Todo, - step_index: Configuration.Step.Index, - maker: *Maker, - progress_node: std.Progress.Node, - ) Step.ExtendedMakeError!void { - _ = todo; - _ = progress_node; - const conf = &maker.scanned_config.configuration; - const conf_step = step_index.ptr(conf); - std.debug.panic("TODO implement another step type: {s}", .{conf_step.name.slice(conf)}); - } - }; - pub const TopLevel = struct { pub fn make( top_level: *TopLevel, @@ -750,8 +736,9 @@ fn failWithCacheError( /// separately from using the cache system. pub fn writeManifest(s: *Step, maker: *Maker, man: *Cache.Manifest) !void { if (s.test_results.isSuccess()) { - man.writeManifest() catch |err| { - try s.addError(maker, "failed writing cache manifest: {t}", .{err}); + man.writeManifest() catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| try s.addError(maker, "failed writing cache manifest: {t}", .{e}), }; } } diff --git a/lib/compiler/Maker/Step/ConfigHeader.zig b/lib/compiler/Maker/Step/ConfigHeader.zig index f823d53468..b86548b561 100644 --- a/lib/compiler/Maker/Step/ConfigHeader.zig +++ b/lib/compiler/Maker/Step/ConfigHeader.zig @@ -4,22 +4,35 @@ const std = @import("std"); const Io = std.Io; const Configuration = std.Build.Configuration; const Writer = std.Io.Writer; +const Path = std.Build.Cache.Path; +const Allocator = std.mem.Allocator; const Step = @import("../Step.zig"); const Maker = @import("../../Maker.zig"); + const header_text = "This file was generated by ConfigHeader using the Zig Build System."; + const c_generated_line = "/* " ++ header_text ++ " */\n"; + const asm_generated_line = "; " ++ header_text ++ "\n"; + pub fn make( config_header: *ConfigHeader, step_index: Configuration.Step.Index, maker: *Maker, progress_node: std.Progress.Node, ) Step.ExtendedMakeError!void { + _ = config_header; + _ = progress_node; const graph = maker.graph; - const arena = maker.graph.arena; // TODO don't leak into process arena + const gpa = maker.gpa; const step = maker.stepByIndex(step_index); const io = graph.io; + const arena = graph.arena; // TODO don't leak into the process arena + const conf = &maker.scanned_config.configuration; + const conf_step = step_index.ptr(conf); + const conf_ch = conf_step.extended.get(conf.extra).config_header; + const cache_root = graph.local_cache_root; - if (config_header.style.getPath()) |lp| + if (conf_ch.style.getPath()) |lp| try step.singleUnchangingWatchInput(maker, arena, lp); var man = graph.cache.obtain(); @@ -29,45 +42,51 @@ pub fn make( // random bytes when ConfigHeader implementation is modified in a // non-backwards-compatible way. man.hash.add(@as(u32, 0xdef08d23)); - man.hash.addBytes(config_header.include_path); - man.hash.addOptionalBytes(config_header.include_guard_override); + man.hash.addBytes(conf_ch.include_path); + man.hash.addOptionalBytes(conf_ch.include_guard_override); var aw: Writer.Allocating = .init(arena); defer aw.deinit(); - const bw = &aw.writer; - const header_text = "This file was generated by ConfigHeader using the Zig Build System."; - const c_generated_line = "/* " ++ header_text ++ " */\n"; - const asm_generated_line = "; " ++ header_text ++ "\n"; - - switch (config_header.style) { - .autoconf_undef, .autoconf_at => |file_source| { - try bw.writeAll(c_generated_line); - const src_path = file_source.getPath2(b, step); - const contents = Io.Dir.cwd().readFileAlloc(io, src_path, arena, .limited(config_header.max_bytes)) catch |err| { + switch (conf_ch.flags.style) { + .autoconf_undef => { + const src_path = try maker.resolveLazyPathIndex(arena, conf_ch.template_file.value.?, step_index); + const contents = Io.Dir.cwd().readFileAlloc(io, src_path, arena, .limited(conf_ch.max_bytes)) catch |err| return step.fail("unable to read autoconf input file {s}: {t}", .{ src_path, err }); + renderAutoConfUndef(step, contents, &aw.writer, &conf_ch.values, src_path) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + else => |e| return e, }; - switch (config_header.style) { - .autoconf_undef => try render_autoconf_undef(step, contents, bw, &config_header.values, src_path), - .autoconf_at => try render_autoconf_at(step, contents, &aw, &config_header.values, src_path), - else => unreachable, - } }, - .cmake => |file_source| { - try bw.writeAll(c_generated_line); - const src_path = file_source.getPath2(b, step); - const contents = Io.Dir.cwd().readFileAlloc(io, src_path, arena, .limited(config_header.max_bytes)) catch |err| { - return step.fail("unable to read cmake input file {s}: {t}", .{ src_path, err }); + .autoconf_at => { + const src_path = try maker.resolveLazyPathIndex(arena, conf_ch.template_file.value.?, step_index); + const contents = Io.Dir.cwd().readFileAlloc(io, src_path, arena, .limited(conf_ch.max_bytes)) catch |err| + return step.fail("unable to read autoconf input file {s}: {t}", .{ src_path, err }); + renderAutoconfAt(step, contents, &aw, &conf_ch.values, src_path) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + else => |e| return e, + }; + }, + .cmake => { + const src_path = try maker.resolveLazyPathIndex(arena, conf_ch.template_file.value.?, step_index); + const contents = Io.Dir.cwd().readFileAlloc(io, src_path, arena, .limited(conf_ch.max_bytes)) catch |err| + return step.fail("unable to read cmake input file {s}: {t}", .{ src_path, err }); + renderCmake(step, contents, &aw.writer, conf_ch.values, src_path) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + else => |e| return e, }; - try render_cmake(step, contents, bw, config_header.values, src_path); }, .blank => { - try bw.writeAll(c_generated_line); - try render_blank(gpa, bw, config_header.values, config_header.include_path, config_header.include_guard_override); + renderBlank(gpa, &aw.writer, conf_ch.values, conf_ch.include_path, conf_ch.include_guard_override) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + else => |e| return e, + }; }, .nasm => { - try bw.writeAll(asm_generated_line); - try render_nasm(bw, config_header.values); + renderNasm(&aw.writer, conf_ch.values) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + else => |e| return e, + }; }, } @@ -76,7 +95,10 @@ pub fn make( if (try step.cacheHit(&man)) { const digest = man.final(); - config_header.generated_dir.path = try b.cache_root.join(arena, &.{ "o", &digest }); + maker.generatedPath().* = .{ + .root_dir = cache_root, + .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest }), + }; return; } @@ -87,35 +109,38 @@ pub fn make( // output_path is libavutil/avconfig.h // We want to open directory zig-cache/o/HASH/libavutil/ // but keep output_dir as zig-cache/o/HASH for -I include - const sub_path = b.pathJoin(&.{ "o", &digest, config_header.include_path }); - const sub_path_dirname = std.fs.path.dirname(sub_path).?; + const out_path: Path = .{ + .root_dir = cache_root, + .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest, conf_ch.include_path.slice(conf) }), + }; + const out_path_dirname = out_path.dirname().?; - b.cache_root.handle.createDirPath(io, sub_path_dirname) catch |err| { - return step.fail("unable to make path '{f}{s}': {s}", .{ - b.cache_root, sub_path_dirname, @errorName(err), - }); + out_path_dirname.root_dir.handle.createDirPath(io, out_path_dirname.sub_path) catch |err| + return step.fail("unable to make path {f}: {t}", .{ out_path_dirname, err }); + + out_path.root_dir.handle.writeFile(io, .{ .sub_path = out_path.sub_path, .data = output }) catch |err| + return step.fail("unable to write file {f}: {t}", .{ out_path, err }); + + maker.generatedPath().* = .{ + .root_dir = cache_root, + .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest }), }; - b.cache_root.handle.writeFile(io, .{ .sub_path = sub_path, .data = output }) catch |err| { - return step.fail("unable to write file '{f}{s}': {s}", .{ - b.cache_root, sub_path, @errorName(err), - }); - }; - - config_header.generated_dir.path = try b.cache_root.join(arena, &.{ "o", &digest }); - try man.writeManifest(); + try step.writeManifest(maker, &man); } -fn render_autoconf_undef( +fn renderAutoConfUndef( step: *Step, contents: []const u8, - bw: *Writer, + w: *Writer, values: *const std.array_hash_map.String(Value), src_path: []const u8, ) !void { const build = step.owner; const allocator = build.allocator; + try w.writeAll(c_generated_line); + var is_used: std.bit_set.Dynamic = try .initEmpty(allocator, values.count()); defer is_used.deinit(allocator); @@ -124,15 +149,15 @@ fn render_autoconf_undef( var line_it = std.mem.splitScalar(u8, contents, '\n'); while (line_it.next()) |line| : (line_index += 1) { if (!std.mem.startsWith(u8, line, "#")) { - try bw.writeAll(line); - try bw.writeByte('\n'); + try w.writeAll(line); + try w.writeByte('\n'); continue; } var it = std.mem.tokenizeAny(u8, line[1..], " \t\r"); const undef = it.next().?; if (!std.mem.eql(u8, undef, "undef")) { - try bw.writeAll(line); - try bw.writeByte('\n'); + try w.writeAll(line); + try w.writeByte('\n'); continue; } const name = it.next().?; @@ -144,7 +169,7 @@ fn render_autoconf_undef( continue; }; is_used.set(index); - try renderValueC(bw, name, values.values()[index]); + try renderValueC(w, name, values.values()[index]); } var unused_value_it = is_used.iterator(.{ .kind = .unset }); @@ -158,7 +183,7 @@ fn render_autoconf_undef( } } -fn render_autoconf_at( +fn renderAutoconfAt( step: *Step, contents: []const u8, aw: *Writer.Allocating, @@ -167,7 +192,9 @@ fn render_autoconf_at( ) !void { const build = step.owner; const allocator = build.allocator; - const bw = &aw.writer; + const w = &aw.writer; + + try w.writeAll(c_generated_line); const used = allocator.alloc(bool, values.count()) catch @panic("OOM"); for (used) |*u| u.* = false; @@ -180,7 +207,7 @@ fn render_autoconf_at( const last_line = line_it.index == line_it.buffer.len; const old_len = aw.written().len; - expand_variables_autoconf_at(bw, line, values, used) catch |err| switch (err) { + expandVariablesAutoconfAt(w, line, values, used) catch |err| switch (err) { error.MissingValue => { const name = aw.written()[old_len..]; defer aw.shrinkRetainingCapacity(old_len); @@ -198,7 +225,7 @@ fn render_autoconf_at( continue; }, }; - if (!last_line) try bw.writeByte('\n'); + if (!last_line) try w.writeByte('\n'); } for (values.entries.slice().items(.key), used) |name, u| { @@ -211,16 +238,18 @@ fn render_autoconf_at( if (any_errors) return error.MakeFailed; } -fn render_cmake( +fn renderCmake( step: *Step, contents: []const u8, - bw: *Writer, + w: *Writer, values: std.array_hash_map.String(Value), src_path: []const u8, ) !void { const build = step.owner; const allocator = build.allocator; + try w.writeAll(c_generated_line); + var values_copy = try values.clone(allocator); defer values_copy.deinit(allocator); @@ -230,7 +259,7 @@ fn render_cmake( while (line_it.next()) |raw_line| : (line_index += 1) { const last_line = line_it.index == line_it.buffer.len; - const line = expand_variables_cmake(allocator, raw_line, values) catch |err| switch (err) { + const line = expandVariablesCmake(allocator, raw_line, values) catch |err| switch (err) { error.InvalidCharacter => { try step.addError("{s}:{d}: error: invalid character in a variable name", .{ src_path, line_index + 1, @@ -249,16 +278,16 @@ fn render_cmake( defer allocator.free(line); const line_start = std.mem.findNone(u8, line, " \t\r") orelse { - try bw.writeAll(line); - if (!last_line) try bw.writeByte('\n'); + try w.writeAll(line); + if (!last_line) try w.writeByte('\n'); continue; }; const whitespace_prefix = line[0..line_start]; const trimmed_line = line[line_start..]; if (!std.mem.startsWith(u8, trimmed_line, "#")) { - try bw.writeAll(line); - if (!last_line) try bw.writeByte('\n'); + try w.writeAll(line); + if (!last_line) try w.writeByte('\n'); continue; } @@ -267,8 +296,8 @@ fn render_cmake( if (!std.mem.eql(u8, cmakedefine, "cmakedefine") and !std.mem.eql(u8, cmakedefine, "cmakedefine01")) { - try bw.writeAll(line); - if (!last_line) try bw.writeByte('\n'); + try w.writeAll(line); + if (!last_line) try w.writeByte('\n'); continue; } @@ -339,8 +368,8 @@ fn render_cmake( value = Value{ .ident = it.rest() }; } - try bw.writeAll(whitespace_prefix); - try renderValueC(bw, name, value); + try w.writeAll(whitespace_prefix); + try renderValueC(w, name, value); } if (any_errors) { @@ -348,13 +377,15 @@ fn render_cmake( } } -fn render_blank( +fn renderBlank( gpa: std.mem.Allocator, - bw: *Writer, + w: *Writer, defines: std.array_hash_map.String(Value), include_path: []const u8, include_guard_override: ?[]const u8, ) !void { + try w.writeAll(c_generated_line); + const include_guard_name = include_guard_override orelse blk: { const name = try gpa.dupe(u8, include_path); for (name) |*byte| { @@ -368,51 +399,52 @@ fn render_blank( }; defer if (include_guard_override == null) gpa.free(include_guard_name); - try bw.print( + try w.print( \\#ifndef {[0]s} \\#define {[0]s} \\ , .{include_guard_name}); const values = defines.values(); - for (defines.keys(), 0..) |name, i| try renderValueC(bw, name, values[i]); + for (defines.keys(), 0..) |name, i| try renderValueC(w, name, values[i]); - try bw.print( + try w.print( \\#endif /* {s} */ \\ , .{include_guard_name}); } -fn render_nasm(bw: *Writer, defines: std.array_hash_map.String(Value)) !void { - for (defines.keys(), defines.values()) |name, value| try renderValueNasm(bw, name, value); +fn renderNasm(w: *Writer, defines: std.array_hash_map.String(Value)) !void { + try w.writeAll(asm_generated_line); + for (defines.keys(), defines.values()) |name, value| try renderValueNasm(w, name, value); } -fn renderValueC(bw: *Writer, name: []const u8, value: Value) !void { +fn renderValueC(w: *Writer, name: []const u8, value: Value) !void { switch (value) { - .undef => try bw.print("/* #undef {s} */\n", .{name}), - .defined => try bw.print("#define {s}\n", .{name}), - .boolean => |b| try bw.print("#define {s} {c}\n", .{ name, @as(u8, '0') + @intFromBool(b) }), - .int => |i| try bw.print("#define {s} {d}\n", .{ name, i }), - .ident => |ident| try bw.print("#define {s} {s}\n", .{ name, ident }), + .undef => try w.print("/* #undef {s} */\n", .{name}), + .defined => try w.print("#define {s}\n", .{name}), + .boolean => |b| try w.print("#define {s} {c}\n", .{ name, @as(u8, '0') + @intFromBool(b) }), + .int => |i| try w.print("#define {s} {d}\n", .{ name, i }), + .ident => |ident| try w.print("#define {s} {s}\n", .{ name, ident }), // TODO: use C-specific escaping instead of zig string literals - .string => |string| try bw.print("#define {s} \"{f}\"\n", .{ name, std.zig.fmtString(string) }), + .string => |string| try w.print("#define {s} \"{f}\"\n", .{ name, std.zig.fmtString(string) }), } } -fn renderValueNasm(bw: *Writer, name: []const u8, value: Value) !void { +fn renderValueNasm(w: *Writer, name: []const u8, value: Value) !void { switch (value) { - .undef => try bw.print("; %undef {s}\n", .{name}), - .defined => try bw.print("%define {s}\n", .{name}), - .boolean => |b| try bw.print("%define {s} {c}\n", .{ name, @as(u8, '0') + @intFromBool(b) }), - .int => |i| try bw.print("%define {s} {d}\n", .{ name, i }), - .ident => |ident| try bw.print("%define {s} {s}\n", .{ name, ident }), + .undef => try w.print("; %undef {s}\n", .{name}), + .defined => try w.print("%define {s}\n", .{name}), + .boolean => |b| try w.print("%define {s} {c}\n", .{ name, @as(u8, '0') + @intFromBool(b) }), + .int => |i| try w.print("%define {s} {d}\n", .{ name, i }), + .ident => |ident| try w.print("%define {s} {s}\n", .{ name, ident }), // TODO: use nasm-specific escaping instead of zig string literals - .string => |string| try bw.print("%define {s} \"{f}\"\n", .{ name, std.zig.fmtString(string) }), + .string => |string| try w.print("%define {s} \"{f}\"\n", .{ name, std.zig.fmtString(string) }), } } -fn expand_variables_autoconf_at( - bw: *Writer, +fn expandVariablesAutoconfAt( + w: *Writer, contents: []const u8, values: *const std.array_hash_map.String(Value), used: []bool, @@ -437,17 +469,17 @@ fn expand_variables_autoconf_at( const key = contents[curr + 1 .. close_pos]; const index = values.getIndex(key) orelse { // Report the missing key to the caller. - try bw.writeAll(key); + try w.writeAll(key); return error.MissingValue; }; const value = values.entries.slice().items(.value)[index]; used[index] = true; - try bw.writeAll(contents[source_offset..curr]); + try w.writeAll(contents[source_offset..curr]); switch (value) { .undef, .defined => {}, - .boolean => |b| try bw.writeByte(@as(u8, '0') + @intFromBool(b)), - .int => |i| try bw.print("{d}", .{i}), - .ident, .string => |s| try bw.writeAll(s), + .boolean => |b| try w.writeByte(@as(u8, '0') + @intFromBool(b)), + .int => |i| try w.print("{d}", .{i}), + .ident, .string => |s| try w.writeAll(s), } curr = close_pos; @@ -455,10 +487,10 @@ fn expand_variables_autoconf_at( } } - try bw.writeAll(contents[source_offset..]); + try w.writeAll(contents[source_offset..]); } -fn expand_variables_cmake( +fn expandVariablesCmake( allocator: Allocator, contents: []const u8, values: std.array_hash_map.String(Value), @@ -602,7 +634,7 @@ fn testReplaceVariablesAutoconfAt( for (used) |*u| u.* = false; defer allocator.free(used); - try expand_variables_autoconf_at(&aw.writer, contents, values, used); + try expandVariablesAutoconfAt(&aw.writer, contents, values, used); for (used) |u| if (!u) return error.UnusedValue; try std.testing.expectEqualStrings(expected, aw.written()); @@ -614,13 +646,13 @@ fn testReplaceVariablesCMake( expected: []const u8, values: std.array_hash_map.String(Value), ) !void { - const actual = try expand_variables_cmake(allocator, contents, values); + const actual = try expandVariablesCmake(allocator, contents, values); defer allocator.free(actual); try std.testing.expectEqualStrings(expected, actual); } -test "expand_variables_autoconf_at simple cases" { +test "expandVariablesAutoconfAt simple cases" { const allocator = std.testing.allocator; var values: std.array_hash_map.String(Value) = .init(allocator); defer values.deinit(); @@ -716,7 +748,7 @@ test "expand_variables_autoconf_at simple cases" { values.clearRetainingCapacity(); } -test "expand_variables_autoconf_at edge cases" { +test "expandVariablesAutoconfAt edge cases" { const allocator = std.testing.allocator; var values: std.array_hash_map.String(Value) = .init(allocator); defer values.deinit(); @@ -732,7 +764,7 @@ test "expand_variables_autoconf_at edge cases" { values.clearRetainingCapacity(); } -test "expand_variables_cmake simple cases" { +test "expandVariablesCmake simple cases" { const allocator = std.testing.allocator; var values: std.array_hash_map.String(Value) = .init(allocator); defer values.deinit(); @@ -820,7 +852,7 @@ test "expand_variables_cmake simple cases" { try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "${bad}", "", values)); } -test "expand_variables_cmake edge cases" { +test "expandVariablesCmake edge cases" { const allocator = std.testing.allocator; var values: std.array_hash_map.String(Value) = .init(allocator); defer values.deinit(); @@ -881,7 +913,7 @@ test "expand_variables_cmake edge cases" { try std.testing.expectError(error.InvalidCharacter, testReplaceVariablesCMake(allocator, "${str@ing}", "", values)); } -test "expand_variables_cmake escaped characters" { +test "expandVariablesCmake escaped characters" { const allocator = std.testing.allocator; var values: std.array_hash_map.String(Value) = .init(allocator); defer values.deinit(); diff --git a/lib/compiler/Maker/Step/ObjCopy.zig b/lib/compiler/Maker/Step/ObjCopy.zig index 96fd1dfb04..937270a006 100644 --- a/lib/compiler/Maker/Step/ObjCopy.zig +++ b/lib/compiler/Maker/Step/ObjCopy.zig @@ -166,7 +166,7 @@ pub fn make( maker.generatedPath(conf_oc.output_file).* = dest_path; - man.writeManifest() catch |err| switch (err) { + step.writeManifest(maker, &man) catch |err| switch (err) { error.Canceled => |e| return e, else => |e| try step.addError(maker, "failed writing cache manifest: {t}", .{e}), };