| 1 | const ObjCopy = @This(); |
| 2 | |
| 3 | const std = @import("std"); |
| 4 | const Io = std.Io; |
| 5 | const Path = std.Build.Cache.Path; |
| 6 | const Configuration = std.Build.Configuration; |
| 7 | |
| 8 | const Step = @import("../Step.zig"); |
| 9 | const Maker = @import("../../Maker.zig"); |
| 10 | |
| 11 | pub fn make( |
| 12 | obj_copy: *ObjCopy, |
| 13 | step_index: Configuration.Step.Index, |
| 14 | maker: *Maker, |
| 15 | progress_node: std.Progress.Node, |
| 16 | ) Step.ExtendedMakeError!void { |
| 17 | _ = obj_copy; |
| 18 | const graph = maker.graph; |
| 19 | const arena = maker.graph.arena; // TODO don't leak into process arena |
| 20 | const io = graph.io; |
| 21 | const step = maker.stepByIndex(step_index); |
| 22 | const conf = &maker.scanned_config.configuration; |
| 23 | const conf_step = step_index.ptr(conf); |
| 24 | const conf_oc = conf_step.extended.get(conf.extra).obj_copy; |
| 25 | const cache_root = graph.local_cache_root; |
| 26 | const input_lazy_path = conf_oc.input_file.get(conf); |
| 27 | const only_section: ?[]const u8 = if (conf_oc.only_section.value) |s| s.slice(conf) else null; |
| 28 | const opt_basename: ?[]const u8 = if (conf_oc.basename.value) |s| s.slice(conf) else null; |
| 29 | const opt_debug_basename: ?[]const u8 = if (conf_oc.debug_basename.value) |s| s.slice(conf) else null; |
| 30 | |
| 31 | try step.singleUnchangingWatchInput(maker, arena, input_lazy_path); |
| 32 | |
| 33 | var man = graph.cache.obtain(); |
| 34 | defer man.deinit(); |
| 35 | |
| 36 | const input_path = try maker.resolveLazyPath(arena, input_lazy_path, step_index); |
| 37 | _ = try man.addFilePath(input_path, null); |
| 38 | man.hash.addOptionalBytes(only_section); |
| 39 | man.hash.addOptionalBytes(opt_basename); |
| 40 | man.hash.addOptionalBytes(opt_debug_basename); |
| 41 | man.hash.addOptional(conf_oc.pad_to.value); |
| 42 | man.hash.add(conf_oc.flags.format); |
| 43 | man.hash.add(conf_oc.flags.compress_debug); |
| 44 | man.hash.add(conf_oc.flags.strip); |
| 45 | man.hash.add(conf_oc.debug_file.value != null); |
| 46 | |
| 47 | const basename = opt_basename orelse Io.Dir.path.basename(input_path.sub_path); |
| 48 | |
| 49 | if (try step.cacheHit(maker, &man, progress_node)) { |
| 50 | // Cache hit, skip subprocess execution. |
| 51 | const digest = man.final(); |
| 52 | maker.generatedPath(conf_oc.output_file).* = .{ |
| 53 | .root_dir = cache_root, |
| 54 | .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest, basename }), |
| 55 | }; |
| 56 | if (conf_oc.debug_file.value) |debug_file| { |
| 57 | const debug_basename = opt_debug_basename orelse try arena.print("{s}.debug", .{ |
| 58 | Io.Dir.path.basename(input_path.sub_path), |
| 59 | }); |
| 60 | maker.generatedPath(debug_file).* = .{ |
| 61 | .root_dir = cache_root, |
| 62 | .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest, debug_basename }), |
| 63 | }; |
| 64 | } |
| 65 | return; |
| 66 | } |
| 67 | |
| 68 | // We don't find out more input files while executing objcopy so we can |
| 69 | // already obtain the digest and use it directly as the output path. |
| 70 | const digest = man.final(); |
| 71 | const dest_path: Path = .{ |
| 72 | .root_dir = cache_root, |
| 73 | .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest, basename }), |
| 74 | }; |
| 75 | const dest_dirname = dest_path.dirname().?; |
| 76 | dest_dirname.root_dir.handle.createDirPath(io, dest_dirname.sub_path) catch |err| |
| 77 | return step.fail(maker, "failed to create path {f}: {t}", .{ dest_dirname, err }); |
| 78 | |
| 79 | var argv: std.ArrayList([]const u8) = .empty; |
| 80 | try argv.ensureUnusedCapacity(arena, 11); |
| 81 | |
| 82 | argv.addManyAsArrayAssumeCapacity(2).* = .{ graph.zig_exe, "objcopy" }; |
| 83 | |
| 84 | if (only_section) |s| argv.addManyAsArrayAssumeCapacity(2).* = .{ "-j", s }; |
| 85 | |
| 86 | switch (conf_oc.flags.strip) { |
| 87 | .none => {}, |
| 88 | .debug => argv.appendAssumeCapacity("--strip-debug"), |
| 89 | .debug_and_symbols => argv.appendAssumeCapacity("--strip-all"), |
| 90 | } |
| 91 | |
| 92 | if (conf_oc.pad_to.value) |pad_to| { |
| 93 | argv.addManyAsArrayAssumeCapacity(2).* = .{ |
| 94 | "--pad-to", try arena.print("{d}", .{pad_to}), |
| 95 | }; |
| 96 | } |
| 97 | |
| 98 | switch (conf_oc.flags.format) { |
| 99 | .default => {}, |
| 100 | else => |t| argv.addManyAsArrayAssumeCapacity(2).* = .{ "-O", @tagName(t) }, |
| 101 | } |
| 102 | |
| 103 | if (conf_oc.flags.compress_debug) |
| 104 | argv.appendAssumeCapacity("--compress-debug-sections"); |
| 105 | |
| 106 | if (conf_oc.debug_file.value) |debug_file| { |
| 107 | const debug_basename = opt_debug_basename orelse try arena.print("{s}.debug", .{ |
| 108 | Io.Dir.path.basename(input_path.sub_path), |
| 109 | }); |
| 110 | const debug_dest_path: Path = .{ |
| 111 | .root_dir = cache_root, |
| 112 | .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest, debug_basename }), |
| 113 | }; |
| 114 | argv.appendAssumeCapacity(try arena.print("--extract-to={f}", .{debug_dest_path})); |
| 115 | maker.generatedPath(debug_file).* = debug_dest_path; |
| 116 | } |
| 117 | |
| 118 | try argv.ensureUnusedCapacity(arena, conf_oc.add_section.slice.len * 2); |
| 119 | |
| 120 | for (conf_oc.add_section.slice) |section| { |
| 121 | argv.appendAssumeCapacity("--add-section"); |
| 122 | argv.appendAssumeCapacity(try arena.print("{s}={f}", .{ |
| 123 | section.section_name.slice(conf), |
| 124 | try maker.resolveLazyPathIndex(arena, section.file_path, step_index), |
| 125 | })); |
| 126 | } |
| 127 | |
| 128 | for (conf_oc.update_section.slice) |update| { |
| 129 | const name = update.section_name.slice(conf); |
| 130 | |
| 131 | try argv.ensureUnusedCapacity(arena, 4); |
| 132 | |
| 133 | if (update.flags.alignment.toBytes()) |a| { |
| 134 | argv.appendAssumeCapacity("--set-section-alignment"); |
| 135 | argv.appendAssumeCapacity(try arena.print("{s}={d}", .{ name, a })); |
| 136 | } |
| 137 | |
| 138 | const f = update.flags.section_flags; |
| 139 | if (f != Configuration.Step.ObjCopy.SectionFlags.default) { |
| 140 | // trailing comma is allowed |
| 141 | argv.appendAssumeCapacity("--set-section-flags"); |
| 142 | argv.appendAssumeCapacity(try arena.print("{s}={s}{s}{s}{s}{s}{s}{s}{s}{s}", .{ |
| 143 | name, |
| 144 | if (f.alloc) "alloc," else "", |
| 145 | if (f.contents) "contents," else "", |
| 146 | if (f.load) "load," else "", |
| 147 | if (f.readonly) "readonly," else "", |
| 148 | if (f.code) "code," else "", |
| 149 | if (f.exclude) "exclude," else "", |
| 150 | if (f.large) "large," else "", |
| 151 | if (f.merge) "merge," else "", |
| 152 | if (f.strings) "strings," else "", |
| 153 | })); |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | argv.appendAssumeCapacity(try arena.print("{f}", .{input_path})); |
| 158 | argv.appendAssumeCapacity(try arena.print("{f}", .{dest_path})); |
| 159 | |
| 160 | argv.appendAssumeCapacity("--listen=-"); |
| 161 | _ = Step.evalZigProcess(step_index, maker, argv.items, progress_node, false) catch |err| switch (err) { |
| 162 | error.NeedCompileErrorCheck => unreachable, |
| 163 | else => |e| return e, |
| 164 | }; |
| 165 | |
| 166 | maker.generatedPath(conf_oc.output_file).* = dest_path; |
| 167 | |
| 168 | step.writeManifest(maker, &man) catch |err| switch (err) { |
| 169 | error.Canceled => |e| return e, |
| 170 | else => |e| try step.addError(maker, "failed writing cache manifest: {t}", .{e}), |
| 171 | }; |
| 172 | } |