zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 8a8bf5ad023451a22fd7abf438e6c7ee105ac6bf (tree)
parent affe5ed867009c889f2df7b624387c2bceefcc0e
Author: Andrew Kelley <andrew@ziglang.org>
Date:   Thu,  7 May 2026 15:32:02 -0700

maker: update ObjCopy to new system

Diffstat:
MBRANCH_TODO | 1+
Mlib/compiler/Maker.zig | 5++---
Mlib/compiler/Maker/Step.zig | 10++++++----
Mlib/compiler/Maker/Step/ObjCopy.zig | 169+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mlib/std/Build/Configuration.zig | 99++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mlib/std/Build/Step/ObjCopy.zig | 45+++++++--------------------------------------
6 files changed, 215 insertions(+), 114 deletions(-)

diff --git a/BRANCH_TODO b/BRANCH_TODO @@ -20,6 +20,7 @@ - and adjust dependencyInner to not openDir() ## Followup Issues +* stop leaking into global process arena * reduce the size of Maker.Step.Extended (make Run smaller) probably by using an arena per make * link_eh_frame_hdr should be DefaultingBool * make --foo, --no-foo CLI args uniform (make them -f args instead) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig @@ -612,7 +612,7 @@ pub fn main(init: process.Init.Minimal) !void { step.state = .precheck_done; const deps = step_index.ptr(c).deps.slice(c); step.pending_deps = @intCast(deps.len); - step.reset(gpa); + step.reset(&maker); } continue :rebuild; }, @@ -1506,10 +1506,9 @@ fn constructGraphAndCheckForDependencyLoop( /// invalidated. pub fn invalidateResult(maker: *Maker, step: *Step) bool { if (step.state == .precheck_done) return false; - const gpa = maker.gpa; assert(step.pending_deps == 0); step.state = .precheck_done; - step.reset(gpa); + step.reset(maker); for (step.dependants.items) |dependant_index| { const dependant = maker.stepByIndex(dependant_index); _ = invalidateResult(maker, dependant); diff --git a/lib/compiler/Maker/Step.zig b/lib/compiler/Maker/Step.zig @@ -23,6 +23,7 @@ pub const Run = @import("Step/Run.zig"); pub const InstallArtifact = @import("Step/InstallArtifact.zig"); pub const InstallFile = @import("Step/InstallFile.zig"); pub const UpdateSourceFiles = @import("Step/UpdateSourceFiles.zig"); +pub const ObjCopy = @import("Step/ObjCopy.zig"); /// Avoid false sharing. _: void align(std.atomic.cache_line) = {}, @@ -76,7 +77,7 @@ pub const Extended = union(enum) { install_artifact: InstallArtifact, install_dir: Todo, install_file: InstallFile, - obj_copy: Todo, + obj_copy: ObjCopy, options: Todo, remove_dir: Todo, run: Run, @@ -306,8 +307,9 @@ pub fn make( } /// Prepares the step for being re-evaluated. -pub fn reset(step: *Step, gpa: Allocator) void { +pub fn reset(step: *Step, maker: *Maker) void { assert(step.state == .precheck_done); + const gpa = maker.gpa; if (step.result_failed_command) |cmd| gpa.free(cmd); @@ -318,7 +320,7 @@ pub fn reset(step: *Step, gpa: Allocator) void { step.result_peak_rss = 0; step.result_failed_command = null; step.test_results = .{}; - step.clearWatchInputs(); + step.clearWatchInputs(maker); step.result_error_bundle.deinit(gpa); step.result_error_bundle = std.zig.ErrorBundle.empty; @@ -732,7 +734,7 @@ fn failWithCacheError( pub fn writeManifest(s: *Step, maker: *Maker, man: *Cache.Manifest) !void { if (s.test_results.isSuccess()) { man.writeManifest() catch |err| { - try s.addError(maker, "unable to write cache manifest: {t}", .{err}); + try s.addError(maker, "failed writing cache manifest: {t}", .{err}); }; } } diff --git a/lib/compiler/Maker/Step/ObjCopy.zig b/lib/compiler/Maker/Step/ObjCopy.zig @@ -2,6 +2,7 @@ const ObjCopy = @This(); const std = @import("std"); const Io = std.Io; +const Path = std.Build.Cache.Path; const allocPrint = std.fmt.allocPrint; const Configuration = std.Build.Configuration; @@ -23,120 +24,152 @@ pub fn make( const conf_step = step_index.ptr(conf); const conf_oc = conf_step.extended.get(conf.extra).obj_copy; const cache_root = graph.local_cache_root; + const input_lazy_path = conf_oc.input_file.get(conf); + const only_section: ?[]const u8 = if (conf_oc.only_section.value) |s| s.slice(conf) else null; + const opt_basename: ?[]const u8 = if (conf_oc.basename.value) |s| s.slice(conf) else null; + const opt_debug_basename: ?[]const u8 = if (conf_oc.debug_basename.value) |s| s.slice(conf) else null; - try step.singleUnchangingWatchInput(maker, arena, conf_oc.input_file); + try step.singleUnchangingWatchInput(maker, arena, input_lazy_path); var man = graph.cache.obtain(); defer man.deinit(); - const src_path = try maker.resolveLazyPathIndex(arena, conf_oc.input_file, step_index); - _ = try man.addFilePath(src_path, null); - man.hash.addOptionalBytes(conf_oc.only_section); - man.hash.addOptional(conf_oc.pad_to); - man.hash.addOptional(conf_oc.format); - man.hash.add(conf_oc.compress_debug); - man.hash.add(conf_oc.strip); - man.hash.add(conf_oc.output_file_debug != null); + const input_path = try maker.resolveLazyPath(arena, input_lazy_path, step_index); + _ = try man.addFilePath(input_path, null); + man.hash.addOptionalBytes(only_section); + man.hash.addOptionalBytes(opt_basename); + man.hash.addOptionalBytes(opt_debug_basename); + man.hash.addOptional(conf_oc.pad_to.value); + man.hash.add(conf_oc.flags.format); + man.hash.add(conf_oc.flags.compress_debug); + man.hash.add(conf_oc.flags.strip); + man.hash.add(conf_oc.debug_file.value != null); - if (try step.cacheHit(&man)) { + const basename = opt_basename orelse Io.Dir.path.basename(input_path.sub_path); + + if (try step.cacheHit(maker, &man)) { // Cache hit, skip subprocess execution. const digest = man.final(); - conf_oc.output_file.path = try cache_root.join(arena, &.{ - "o", &digest, conf_oc.basename, - }); - if (conf_oc.output_file_debug) |*file| { - file.path = try cache_root.join(arena, &.{ - "o", &digest, try allocPrint(arena, "{s}.debug", .{conf_oc.basename}), + maker.generatedPath(conf_oc.output_file).* = .{ + .root_dir = cache_root, + .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest, basename }), + }; + if (conf_oc.debug_file.value) |debug_file| { + const debug_basename = opt_debug_basename orelse try allocPrint(arena, "{s}.debug", .{ + Io.Dir.path.basename(input_path.sub_path), }); + maker.generatedPath(debug_file).* = .{ + .root_dir = cache_root, + .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest, debug_basename }), + }; } return; } + // We don't find out more input files while executing objcopy so we can + // already obtain the digest and use it directly as the output path. const digest = man.final(); - const cache_path = "o" ++ Io.Dir.path.sep_str ++ digest; - const full_dest_path = try cache_root.join(arena, &.{ cache_path, conf_oc.basename }); - const full_dest_path_debug = try cache_root.join(arena, &.{ - cache_path, try allocPrint(arena, "{s}.debug", .{conf_oc.basename}), - }); - cache_root.handle.createDirPath(io, cache_path) catch |err| - return step.fail("unable to make path {s}: {t}", .{ cache_path, err }); + const dest_path: Path = .{ + .root_dir = cache_root, + .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest, basename }), + }; + const dest_dirname = dest_path.dirname().?; + dest_dirname.root_dir.handle.createDirPath(io, dest_dirname.sub_path) catch |err| + return step.fail(maker, "failed to create path {f}: {t}", .{ dest_dirname, err }); var argv: std.ArrayList([]const u8) = .empty; try argv.ensureUnusedCapacity(arena, 11); argv.addManyAsArrayAssumeCapacity(2).* = .{ graph.zig_exe, "objcopy" }; - if (conf_oc.only_section) |only_section| - argv.addManyAsArrayAssumeCapacity(2).* = .{ "-j", only_section }; + if (only_section) |s| argv.addManyAsArrayAssumeCapacity(2).* = .{ "-j", s }; - switch (conf_oc.strip) { + switch (conf_oc.flags.strip) { .none => {}, .debug => argv.appendAssumeCapacity("--strip-debug"), .debug_and_symbols => argv.appendAssumeCapacity("--strip-all"), } - if (conf_oc.pad_to) |pad_to| { + if (conf_oc.pad_to.value) |pad_to| { argv.addManyAsArrayAssumeCapacity(2).* = .{ "--pad-to", try allocPrint(arena, "{d}", .{pad_to}), }; } - if (conf_oc.format) |format| { - argv.addManyAsArrayAssumeCapacity(2).* = .{ - "-O", - switch (format) { - .bin => "binary", - .hex => "hex", - .elf => "elf", - }, - }; + switch (conf_oc.flags.format) { + .default => {}, + else => |t| argv.addManyAsArrayAssumeCapacity(2).* = .{ "-O", @tagName(t) }, } - if (conf_oc.compress_debug) + if (conf_oc.flags.compress_debug) argv.appendAssumeCapacity("--compress-debug-sections"); - if (conf_oc.output_file_debug != null) - argv.appendAssumeCapacity(try allocPrint(arena, "--extract-to={s}", .{full_dest_path_debug})); + if (conf_oc.debug_file.value) |debug_file| { + const debug_basename = opt_debug_basename orelse try allocPrint(arena, "{s}.debug", .{ + Io.Dir.path.basename(input_path.sub_path), + }); + const debug_dest_path: Path = .{ + .root_dir = cache_root, + .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest, debug_basename }), + }; + argv.appendAssumeCapacity(try allocPrint(arena, "--extract-to={f}", .{debug_dest_path})); + maker.generatedPath(debug_file).* = debug_dest_path; + } - try argv.ensureUnusedCapacity(arena, 9); + try argv.ensureUnusedCapacity(arena, conf_oc.add_section.slice.len * 2); - if (conf_oc.add_section) |section| { + for (conf_oc.add_section.slice) |section| { argv.appendAssumeCapacity("--add-section"); argv.appendAssumeCapacity(try allocPrint(arena, "{s}={f}", .{ - section.section_name, try maker.resolveLazyPathIndex(arena, section.file_path, step_index), + section.section_name.slice(conf), + try maker.resolveLazyPathIndex(arena, section.file_path, step_index), })); } - if (conf_oc.set_section_alignment) |set_align| { - argv.appendAssumeCapacity("--set-section-alignment"); - argv.appendAssumeCapacity(try allocPrint(arena, "{s}={d}", .{ set_align.section_name, set_align.alignment })); - } + for (conf_oc.update_section.slice) |update| { + const name = update.section_name.slice(conf); - if (conf_oc.set_section_flags) |set_flags| { - const f = set_flags.flags; - // trailing comma is allowed - argv.appendAssumeCapacity("--set-section-flags"); - argv.appendAssumeCapacity(try allocPrint(arena, "{s}={s}{s}{s}{s}{s}{s}{s}{s}{s}", .{ - set_flags.section_name, - if (f.alloc) "alloc," else "", - if (f.contents) "contents," else "", - if (f.load) "load," else "", - if (f.readonly) "readonly," else "", - if (f.code) "code," else "", - if (f.exclude) "exclude," else "", - if (f.large) "large," else "", - if (f.merge) "merge," else "", - if (f.strings) "strings," else "", - })); + try argv.ensureUnusedCapacity(arena, 4); + + if (update.flags.alignment.toBytes()) |a| { + argv.appendAssumeCapacity("--set-section-alignment"); + argv.appendAssumeCapacity(try allocPrint(arena, "{s}={d}", .{ name, a })); + } + + const f = update.flags.section_flags; + const default_flags: Configuration.Step.ObjCopy.SectionFlags = .{}; + + if (f != default_flags) { + // trailing comma is allowed + argv.appendAssumeCapacity("--set-section-flags"); + argv.appendAssumeCapacity(try allocPrint(arena, "{s}={s}{s}{s}{s}{s}{s}{s}{s}{s}", .{ + name, + if (f.alloc) "alloc," else "", + if (f.contents) "contents," else "", + if (f.load) "load," else "", + if (f.readonly) "readonly," else "", + if (f.code) "code," else "", + if (f.exclude) "exclude," else "", + if (f.large) "large," else "", + if (f.merge) "merge," else "", + if (f.strings) "strings," else "", + })); + } } - argv.appendAssumeCapacity(src_path); - argv.appendAssumeCapacity(full_dest_path); + argv.appendAssumeCapacity(try allocPrint(arena, "{f}", .{input_path})); + argv.appendAssumeCapacity(try allocPrint(arena, "{f}", .{dest_path})); argv.appendAssumeCapacity("--listen=-"); - _ = try Step.evalZigProcess(step_index, maker, argv.items, progress_node, false); + _ = Step.evalZigProcess(step_index, maker, argv.items, progress_node, false) catch |err| switch (err) { + error.NeedCompileErrorCheck => unreachable, + else => |e| return e, + }; + + maker.generatedPath(conf_oc.output_file).* = dest_path; - conf_oc.output_file.path = full_dest_path; - if (conf_oc.output_file_debug) |*file| file.path = full_dest_path_debug; - try man.writeManifest(); + man.writeManifest() catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| try step.addError(maker, "failed writing cache manifest: {t}", .{e}), + }; } diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig @@ -1102,10 +1102,87 @@ pub const Step = extern struct { pub const ObjCopy = struct { flags: @This().Flags, + input_file: LazyPath.Index, + output_file: GeneratedFileIndex, + basename: Storage.FlagOptional(.flags, .basename, String), + debug_file: Storage.FlagOptional(.flags, .debug_file, GeneratedFileIndex), + debug_basename: Storage.FlagOptional(.flags, .debug_basename, String), + only_section: Storage.FlagOptional(.flags, .only_section, String), + pad_to: Storage.FlagOptional(.flags, .pad_to, u64), + add_section: Storage.FlagLengthPrefixedList(.flags, .add_section, AddSection), + update_section: Storage.FlagLengthPrefixedList(.flags, .update_section, UpdateSection), + + pub const Format = enum(u2) { + binary, + hex, + elf, + default, + + pub fn init(f: ?std.Build.Step.ObjCopy.Format) @This() { + return switch (f orelse return .default) { + .binary => .binary, + .hex => .hex, + .elf => .elf, + }; + } + }; + + pub const Strip = enum(u2) { + none, + debug, + debug_and_symbols, + }; + + pub const AddSection = extern struct { + section_name: String, + file_path: LazyPath.Index, + }; + + pub const UpdateSection = extern struct { + section_name: String, + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + section_flags: SectionFlags, + alignment: Alignment, + _: u17 = 0, + }; + }; + + pub const SectionFlags = packed struct(u9) { + /// add SHF_ALLOC + alloc: bool = false, + /// if section is SHT_NOBITS, set SHT_PROGBITS, otherwise do nothing + contents: bool = false, + /// if section is SHT_NOBITS, set SHT_PROGBITS, otherwise do nothing (same as contents) + load: bool = false, + /// readonly: clear default SHF_WRITE flag + readonly: bool = false, + /// add SHF_EXECINSTR + code: bool = false, + /// add SHF_EXCLUDE + exclude: bool = false, + /// add SHF_X86_64_LARGE. Fatal error if target is not x86_64 + large: bool = false, + /// add SHF_MERGE + merge: bool = false, + /// add SHF_STRINGS + strings: bool = false, + }; pub const Flags = packed struct(u32) { tag: Tag = .obj_copy, - _: u27 = 0, + basename: bool, + debug_file: bool, + debug_basename: bool, + format: Format, + strip: Strip, + compress_debug: bool, + only_section: bool, + pad_to: bool, + add_section: bool, + update_section: bool, + _: u15 = 0, }; }; @@ -1658,6 +1735,26 @@ pub const Bytes = extern struct { } }; +/// Stored as a power-of-two, with one special value to indicate none. +pub const Alignment = enum(u6) { + @"1" = 0, + @"2" = 1, + @"4" = 2, + @"8" = 3, + @"16" = 4, + @"32" = 5, + @"64" = 6, + none = std.math.maxInt(u6), + _, + + pub fn toBytes(a: @This()) ?u64 { + return switch (a) { + .none => null, + else => @as(u64, 1) << @intFromEnum(a), + }; + } +}; + pub const DefaultingBool = enum(u2) { false, true, diff --git a/lib/std/Build/Step/ObjCopy.zig b/lib/std/Build/Step/ObjCopy.zig @@ -6,11 +6,11 @@ const Configuration = std.Build.Configuration; step: Step, input_file: std.Build.LazyPath, -basename: []const u8, +basename: ?[]const u8, output_file: Configuration.GeneratedFileIndex, output_file_debug: Configuration.OptionalGeneratedFileIndex, -format: ?RawFormat, +format: ?Format, only_section: ?[]const u8, pad_to: ?u64, strip: Strip, @@ -22,38 +22,9 @@ set_section_flags: ?SetSectionFlags, pub const base_tag: Step.Tag = .obj_copy; -pub const RawFormat = enum { - bin, - hex, - elf, -}; - -pub const Strip = enum { - none, - debug, - debug_and_symbols, -}; - -pub const SectionFlags = packed struct { - /// add SHF_ALLOC - alloc: bool = false, - /// if section is SHT_NOBITS, set SHT_PROGBITS, otherwise do nothing - contents: bool = false, - /// if section is SHT_NOBITS, set SHT_PROGBITS, otherwise do nothing (same as contents) - load: bool = false, - /// readonly: clear default SHF_WRITE flag - readonly: bool = false, - /// add SHF_EXECINSTR - code: bool = false, - /// add SHF_EXCLUDE - exclude: bool = false, - /// add SHF_X86_64_LARGE. Fatal error if target is not x86_64 - large: bool = false, - /// add SHF_MERGE - merge: bool = false, - /// add SHF_STRINGS - strings: bool = false, -}; +pub const Format = enum { binary, hex, elf }; +pub const Strip = Configuration.Step.ObjCopy.Strip; +pub const SectionFlags = Configuration.Step.ObjCopy.SectionFlags; pub const AddSection = struct { section_name: []const u8, @@ -72,7 +43,7 @@ pub const SetSectionFlags = struct { pub const Options = struct { basename: ?[]const u8 = null, - format: ?RawFormat = null, + format: ?Format = null, only_section: ?[]const u8 = null, pad_to: ?u64 = null, @@ -95,7 +66,6 @@ pub fn create( options: Options, ) *ObjCopy { const graph = owner.graph; - const arena = graph.arena; const obj_copy = graph.create(ObjCopy); obj_copy.* = .{ .step = .init(.{ @@ -104,8 +74,7 @@ pub fn create( .owner = owner, }), .input_file = input_file, - .basename = options.basename orelse - std.fmt.allocPrint(arena, "{f}", .{input_file.fmt(graph)}) catch @panic("OOM"), + .basename = options.basename, .output_file = graph.addGeneratedFile(&obj_copy.step), .output_file_debug = if (options.strip != .none and options.extract_to_separate_file) .init(graph.addGeneratedFile(&obj_copy.step))