1const ObjCopy = @This();
2
3const std = @import("std");
4const Io = std.Io;
5const Path = std.Build.Cache.Path;
6const Configuration = std.Build.Configuration;
7
8const Step = @import("../Step.zig");
9const Maker = @import("../../Maker.zig");
10
11pub 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}