authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-17 19:05:13-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:54:34-07:00
logc6d37f389591e722f59ca7f2b719ec8cfc0a9984
tree797d146e58652575739b35c824d5bbe16c2227b6
parent1a63d26836f5c87e45280772b8ba9c822ba75b78

configurer: make string duplication also intern

I had this idea to make b.dupe() also intern the strings since they will be ultimately serialized to Configuration. Unfortunately the idea does not work, because although a process-lived arena is used for the string_bytes ArrayList of the Configuration.Wip, when the ArrayList is resized, Allocator.free() memsets the freed memory to undefined, even though it still technically lives due to being in a process-scoped arena. So this commit will need to be partially reverted. However, I kept it for posterity, and there are some more changes which I will now note below. - dupePaths: don't rewrite backslashes to forward slashes. backslashes are valid in filenames on non-windows systems. - always compile configurer in single-threaded mode - use arena allocator for everything, no gpa for anything - construct the Configuration.Wip instance earlier, so some stuff can be prepopulated as desired. - don't forget to flush

9 files changed, 174 insertions(+), 162 deletions(-)

BRANCH_TODO+1-1
......@@ -1,10 +1,10 @@
1* remove Cache from configurer
12* implement the build options
23* don't forget to add -listen arg back
34* get zig init template working
45* finish migrating the rest of the build steps
56* make zig-pkg path root configurable in maker (make sure --system still works)
67* eliminate calls to getPath, getPath2, getPath3
7* replace b.dupe() with string internment
88* solve the TODOs added in this branch
99* get zig tests passing
1010* test a bunch of third party projects / help people migrate
lib/compiler/configurer.zig+23-25
......@@ -24,31 +24,22 @@ pub const std_options: std.Options = .{
2424};
2525
2626pub fn main(init: process.Init.Minimal) !void {
27 // The build runner is often short-lived, but thanks to `--watch` and `--webui`, that's not
28 // always the case. So, we do need a true gpa for some things.
29 var debug_gpa_state: std.heap.DebugAllocator(.{
30 // We'd rather have `zig build` run faster than catch harmless leaks in
31 // the user's build.zig script.
32 .stack_trace_frames = 0,
33 }) = .init;
34 defer _ = debug_gpa_state.deinit();
35 const gpa = debug_gpa_state.allocator();
36
37 var threaded: std.Io.Threaded = .init(gpa, .{
27 var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
28 defer arena_allocator.deinit();
29 const arena = arena_allocator.allocator();
30
31 // The configurer is always short-lived because all it does is serialize
32 // the configuration, which is picked up by a separate maker process.
33 var threaded: std.Io.Threaded = .init(arena, .{
3834 .environ = init.environ,
3935 .argv0 = .init(init.args),
4036 });
4137 defer threaded.deinit();
4238 const io = threaded.io();
4339
44 // ...but we'll back our arena by `std.heap.page_allocator` for efficiency.
45 var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
46 defer arena_allocator.deinit();
47 const arena = arena_allocator.allocator();
48
4940 const args = try init.args.toSlice(arena);
5041
51 // skip my own exe name
42 // Skip own executable name.
5243 var arg_idx: usize = 1;
5344
5445 const zig_exe = expectArgOrFatal(args, &arg_idx, "--zig");
......@@ -84,7 +75,7 @@ pub fn main(init: process.Init.Minimal) !void {
8475 .arena = arena,
8576 .cache = .{
8677 .io = io,
87 .gpa = gpa,
78 .gpa = arena,
8879 .manifest_dir = try local_cache_directory.handle.createDirPathOpen(io, "h", .{}),
8980 .cwd = try process.currentPathAlloc(io, arena),
9081 },
......@@ -97,7 +88,18 @@ pub fn main(init: process.Init.Minimal) !void {
9788 .result = try std.zig.system.resolveTargetQuery(io, .{}),
9889 },
9990 .generated_files = .empty,
91
92 // Created before running the user's configure script so that some things
93 // can be added during script execution such as strings.
94 //
95 // Use of arena here is load-bearing because `std.Build.dupe` is
96 // implemented by string internment, and then returning the interned
97 // slice. When the string bytes array is reallocated, that reference
98 // must stay alive.
99 .wip_configuration = .init(arena),
100100 };
101 assert(try graph.wip_configuration.addString("") == .empty);
102 assert(try graph.wip_configuration.addString("root") == .root);
101103
102104 graph.cache.addPrefix(.{ .path = null, .handle = cwd });
103105 graph.cache.addPrefix(build_root_directory);
......@@ -200,19 +202,15 @@ pub fn main(init: process.Init.Minimal) !void {
200202 fatal(" access the help menu with 'zig build -h'", .{});
201203 }
202204
203 var wc: Configuration.Wip = .init(gpa);
204 defer wc.deinit();
205 assert(try wc.addString("") == .empty);
206 assert(try wc.addString("root") == .root);
207
208 try serializeSystemIntegrationOptions(&graph, &wc);
205 try serializeSystemIntegrationOptions(&graph, &graph.wip_configuration);
209206
210207 var stdout_buffer: [1024]u8 = undefined;
211208 var file_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer);
212 serialize(builder, &wc, &file_writer.interface) catch |err| switch (err) {
209 serialize(builder, &graph.wip_configuration, &file_writer.interface) catch |err| switch (err) {
213210 error.WriteFailed => fatal("failed to write configuration output: {t}", .{file_writer.err.?}),
214211 error.OutOfMemory => |e| return e,
215212 };
213 file_writer.flush() catch |err| fatal("failed to write configuration output: {t}", .{err});
216214
217215 // This executable is short-lived and run in Debug mode, so we'd rather
218216 // have `zig build` run faster than catch resource leaks in the user's
lib/std/Build.zig+38-37
......@@ -115,11 +115,37 @@ pub const Graph = struct {
115115
116116 /// Indexes correspond to `Configuration.GeneratedFileIndex`.
117117 generated_files: std.ArrayList(*Step),
118 wip_configuration: Configuration.Wip,
118119
119120 pub fn addGeneratedFile(graph: *Graph, owner: *Step) Configuration.GeneratedFileIndex {
120121 graph.generated_files.append(graph.arena, owner) catch @panic("OOM");
121122 return @enumFromInt(graph.generated_files.items.len - 1);
122123 }
124
125 pub fn dupeString(graph: *Graph, bytes: []const u8) [:0]const u8 {
126 // This code assumes the `Configuration.Wip` uses arena allocation such
127 // that references to string_bytes never die even when the ArrayList is
128 // reallocated.
129 const wc = &graph.wip_configuration;
130 const i = wc.addString(bytes) catch @panic("OOM");
131 return wc.string_bytes.items[@intFromEnum(i)..][0..bytes.len :0];
132 }
133
134 pub fn dupePath(graph: *Graph, bytes: []const u8) [:0]const u8 {
135 if (builtin.os.tag != .windows) return dupeString(graph, bytes);
136 const arena = graph.arena;
137 const the_copy = arena.dupe(u8, bytes) catch @panic("OOM");
138 defer arena.free(the_copy);
139 mem.replaceScalar(u8, the_copy, '/', '\\');
140 return dupeString(graph, the_copy);
141 }
142
143 pub fn dupeStrings(graph: *Graph, strings: []const []const u8) []const []const u8 {
144 const arena = graph.arena;
145 const array = arena.alloc([]const u8, strings.len) catch @panic("OOM");
146 for (array, strings) |*dest, source| dest.* = dupeString(graph, source);
147 return array;
148 }
123149};
124150
125151const AvailableDeps = []const struct { []const u8, []const u8 };
......@@ -869,36 +895,18 @@ pub fn addConfigHeader(
869895 return config_header_step;
870896}
871897
872/// Allocator.dupe without the need to handle out of memory.
873pub fn dupe(b: *Build, bytes: []const u8) []u8 {
874 return dupeInner(b.allocator, bytes);
875}
876
877pub fn dupeInner(allocator: Allocator, bytes: []const u8) []u8 {
878 return allocator.dupe(u8, bytes) catch @panic("OOM");
898pub fn dupe(b: *Build, bytes: []const u8) [:0]const u8 {
899 return b.graph.dupeString(bytes);
879900}
880901
881902/// Duplicates an array of strings without the need to handle out of memory.
882pub fn dupeStrings(b: *Build, strings: []const []const u8) [][]u8 {
883 const array = b.allocator.alloc([]u8, strings.len) catch @panic("OOM");
884 for (array, strings) |*dest, source| dest.* = b.dupe(source);
885 return array;
886}
887
888/// Duplicates a path and converts all slashes to the OS's canonical path separator.
889pub fn dupePath(b: *Build, bytes: []const u8) []u8 {
890 return dupePathInner(b.allocator, bytes);
903pub fn dupeStrings(b: *Build, strings: []const []const u8) []const []const u8 {
904 return b.graph.dupeStrings(strings);
891905}
892906
893fn dupePathInner(allocator: Allocator, bytes: []const u8) []u8 {
894 const the_copy = dupeInner(allocator, bytes);
895 for (the_copy) |*byte| {
896 switch (byte.*) {
897 '/', '\\' => byte.* = fs.path.sep,
898 else => {},
899 }
900 }
901 return the_copy;
907/// Duplicates a path, canonicalizing path separators.
908pub fn dupePath(b: *Build, bytes: []const u8) [:0]const u8 {
909 return b.graph.dupePath(bytes);
902910}
903911
904912pub fn addWriteFile(b: *Build, file_path: []const u8, data: []const u8) *Step.WriteFile {
......@@ -2268,25 +2276,18 @@ pub const LazyPath = union(enum) {
22682276 ///
22692277 /// The `b` parameter is only used for its allocator. All *Build instances
22702278 /// share the same allocator.
2271 pub fn dupe(lazy_path: LazyPath, b: *Build) LazyPath {
2272 return lazy_path.dupeInner(b.allocator);
2273 }
2274
2275 fn dupeInner(lazy_path: LazyPath, allocator: Allocator) LazyPath {
2279 pub fn dupe(lazy_path: LazyPath, graph: *Graph) LazyPath {
22762280 return switch (lazy_path) {
2277 .src_path => |sp| .{ .src_path = .{
2278 .owner = sp.owner,
2279 .sub_path = sp.owner.dupePath(sp.sub_path),
2280 } },
2281 .cwd_relative => |p| .{ .cwd_relative = dupePathInner(allocator, p) },
2281 .src_path => |sp| .{ .src_path = .{ .owner = sp.owner, .sub_path = sp.owner.dupePath(sp.sub_path) } },
2282 .cwd_relative => |p| .{ .cwd_relative = graph.dupePath(p) },
22822283 .generated => |gen| .{ .generated = .{
22832284 .index = gen.index,
22842285 .up = gen.up,
2285 .sub_path = dupePathInner(allocator, gen.sub_path),
2286 .sub_path = graph.dupePath(gen.sub_path),
22862287 } },
22872288 .dependency => |dep| .{ .dependency = .{
22882289 .dependency = dep.dependency,
2289 .sub_path = dupePathInner(allocator, dep.sub_path),
2290 .sub_path = graph.dupePath(dep.sub_path),
22902291 } },
22912292 };
22922293 }
lib/std/Build/Configuration.zig-7
......@@ -1419,13 +1419,6 @@ pub const Path = extern struct {
14191419 global_cache,
14201420 build_root,
14211421 };
1422
1423 pub fn toCachePath(path: Path, c: *const Configuration, arena: Allocator) std.Build.Cache.Path {
1424 _ = c;
1425 _ = arena;
1426 _ = path;
1427 @panic("TODO");
1428 }
14291422};
14301423
14311424pub const InstallDestDir = enum(u32) {
lib/std/Build/Module.zig+4-3
......@@ -240,13 +240,14 @@ pub fn init(
240240 owner: *std.Build,
241241 value: union(enum) { options: CreateOptions, existing: *const Module },
242242) void {
243 const allocator = owner.allocator;
243 const graph = owner.graph;
244 const arena = graph.arena;
244245
245246 switch (value) {
246247 .options => |options| {
247248 m.* = .{
248249 .owner = owner,
249 .root_source_file = if (options.root_source_file) |lp| lp.dupe(owner) else null,
250 .root_source_file = if (options.root_source_file) |lp| lp.dupe(graph) else null,
250251 .import_table = .empty,
251252 .resolved_target = options.target,
252253 .optimize = options.optimize,
......@@ -277,7 +278,7 @@ pub fn init(
277278 .no_builtin = options.no_builtin,
278279 };
279280
280 m.import_table.ensureUnusedCapacity(allocator, options.imports.len) catch @panic("OOM");
281 m.import_table.ensureUnusedCapacity(arena, options.imports.len) catch @panic("OOM");
281282 for (options.imports) |dep| {
282283 m.import_table.putAssumeCapacity(dep.name, dep.module);
283284 }
lib/std/Build/Step/Compile.zig+35-31
......@@ -296,10 +296,10 @@ pub const HeaderInstallation = union(enum) {
296296 source: LazyPath,
297297 dest_rel_path: []const u8,
298298
299 pub fn dupe(file: File, b: *std.Build) File {
299 pub fn dupe(file: File, graph: *std.Build.Graph) File {
300300 return .{
301 .source = file.source.dupe(b),
302 .dest_rel_path = b.dupePath(file.dest_rel_path),
301 .source = file.source.dupe(graph),
302 .dest_rel_path = graph.dupePath(file.dest_rel_path),
303303 };
304304 }
305305 };
......@@ -424,13 +424,13 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
424424 };
425425
426426 if (options.zig_lib_dir) |lp| {
427 compile.zig_lib_dir = lp.dupe(compile.step.owner);
427 compile.zig_lib_dir = lp.dupe(graph);
428428 lp.addStepDependencies(&compile.step);
429429 }
430430
431431 if (options.test_runner) |runner| {
432432 compile.test_runner = .{
433 .path = runner.path.dupe(compile.step.owner),
433 .path = runner.path.dupe(graph),
434434 .mode = runner.mode,
435435 };
436436 runner.path.addStepDependencies(&compile.step);
......@@ -440,20 +440,20 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
440440 // gets embedded, so for any other target the manifest file is just ignored.
441441 if (target.ofmt == .coff) {
442442 if (options.win32_manifest) |lp| {
443 compile.win32_manifest = lp.dupe(compile.step.owner);
443 compile.win32_manifest = lp.dupe(graph);
444444 lp.addStepDependencies(&compile.step);
445445 }
446446 if (compile.kind == .lib and compile.linkage != null and compile.linkage.? == .dynamic) {
447447 // Building a Win32 DLL, check for win32 .def file.
448448 if (options.win32_module_definition) |lp| {
449 compile.win32_module_definition = lp.dupe(compile.step.owner);
449 compile.win32_module_definition = lp.dupe(graph);
450450 lp.addStepDependencies(&compile.step);
451451 }
452452 }
453453 }
454454
455455 if (options.entitlements) |lp| {
456 compile.entitlements = lp.dupe(compile.step.owner);
456 compile.entitlements = lp.dupe(graph);
457457 lp.addStepDependencies(&compile.step);
458458 }
459459
......@@ -464,12 +464,13 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
464464/// When a module links with this artifact, all headers marked for installation are added to that
465465/// module's include search path.
466466pub fn installHeader(cs: *Compile, source: LazyPath, dest_rel_path: []const u8) void {
467 const b = cs.step.owner;
467 const graph = cs.step.owner.graph;
468 const arena = graph.arena;
468469 const installation: HeaderInstallation = .{ .file = .{
469 .source = source.dupe(b),
470 .dest_rel_path = b.dupePath(dest_rel_path),
470 .source = source.dupe(graph),
471 .dest_rel_path = graph.dupePath(dest_rel_path),
471472 } };
472 cs.installed_headers.append(b.allocator, installation) catch @panic("OOM");
473 cs.installed_headers.append(arena, installation) catch @panic("OOM");
473474 cs.addHeaderInstallationToIncludeTree(installation);
474475 installation.getSource().addStepDependencies(&cs.step);
475476}
......@@ -483,13 +484,14 @@ pub fn installHeadersDirectory(
483484 dest_rel_path: []const u8,
484485 options: HeaderInstallation.Directory.Options,
485486) void {
486 const b = cs.step.owner;
487 const graph = cs.step.owner.graph;
488 const arena = graph.arena;
487489 const installation: HeaderInstallation = .{ .directory = .{
488 .source = source.dupe(b),
489 .dest_rel_path = b.dupePath(dest_rel_path),
490 .options = options.dupe(b),
490 .source = source.dupe(graph),
491 .dest_rel_path = graph.dupePath(dest_rel_path),
492 .options = options.dupe(graph),
491493 } };
492 cs.installed_headers.append(b.allocator, installation) catch @panic("OOM");
494 cs.installed_headers.append(arena, installation) catch @panic("OOM");
493495 cs.addHeaderInstallationToIncludeTree(installation);
494496 installation.getSource().addStepDependencies(&cs.step);
495497}
......@@ -506,9 +508,10 @@ pub fn installConfigHeader(cs: *Compile, config_header: *Step.ConfigHeader) void
506508/// module's include search path.
507509pub fn installLibraryHeaders(cs: *Compile, lib: *Compile) void {
508510 assert(lib.kind == .lib);
509 const arena = cs.owner.allocator;
511 const graph = cs.step.owner.graph;
512 const arena = graph.arena;
510513 for (lib.installed_headers.items) |installation| {
511 const installation_copy = installation.dupe(lib.step.owner);
514 const installation_copy = installation.dupe(graph);
512515 cs.installed_headers.append(arena, installation_copy) catch @panic("OOM");
513516 cs.addHeaderInstallationToIncludeTree(installation_copy);
514517 installation_copy.getSource().addStepDependencies(&cs.step);
......@@ -556,21 +559,21 @@ pub fn addObjCopy(cs: *Compile, options: Step.ObjCopy.Options) *Step.ObjCopy {
556559}
557560
558561pub fn setLinkerScript(compile: *Compile, source: LazyPath) void {
559 const b = compile.step.owner;
560 compile.linker_script = source.dupe(b);
562 const graph = compile.step.owner.graph;
563 compile.linker_script = source.dupe(graph);
561564 source.addStepDependencies(&compile.step);
562565}
563566
564567pub fn setVersionScript(compile: *Compile, source: LazyPath) void {
565 const b = compile.step.owner;
566 compile.version_script = source.dupe(b);
568 const graph = compile.step.owner.graph;
569 compile.version_script = source.dupe(graph);
567570 source.addStepDependencies(&compile.step);
568571}
569572
570573pub fn forceUndefinedSymbol(compile: *Compile, symbol_name: []const u8) void {
571 const b = compile.step.owner;
572 const arena = b.allocator;
573 compile.force_undefined_symbols.put(arena, b.dupe(symbol_name), {}) catch @panic("OOM");
574 const graph = compile.step.owner.graph;
575 const arena = graph.allocator;
576 compile.force_undefined_symbols.put(arena, graph.dupeString(symbol_name), {}) catch @panic("OOM");
574577}
575578
576579/// Returns whether the library, executable, or object depends on a particular system library.
......@@ -655,9 +658,9 @@ pub fn setVerboseCC(compile: *Compile, value: bool) void {
655658}
656659
657660pub fn setLibCFile(compile: *Compile, libc_file: ?LazyPath) void {
658 const b = compile.step.owner;
661 const graph = compile.step.owner.graph;
659662 if (libc_file) |f| {
660 compile.libc_file = f.dupe(b);
663 compile.libc_file = f.dupe(graph);
661664 f.addStepDependencies(&compile.step);
662665 } else {
663666 compile.libc_file = null;
......@@ -733,11 +736,12 @@ pub fn getEmittedLlvmBc(compile: *Compile) LazyPath {
733736}
734737
735738pub fn setExecCmd(compile: *Compile, args: []const ?[]const u8) void {
736 const b = compile.step.owner;
739 const graph = compile.step.owner.graph;
740 const arena = graph.arena;
737741 assert(compile.kind == .@"test");
738 const duped_args = b.allocator.alloc(?[]u8, args.len) catch @panic("OOM");
742 const duped_args = arena.alloc(?[]u8, args.len) catch @panic("OOM");
739743 for (args, 0..) |arg, i| {
740 duped_args[i] = if (arg) |a| b.dupe(a) else null;
744 duped_args[i] = if (arg) |a| graph.dupeString(a) else null;
741745 }
742746 compile.exec_cmd_args = duped_args;
743747}
lib/std/Build/Step/Run.zig+57-43
......@@ -139,7 +139,7 @@ pub const Arg = union(enum) {
139139 lazy_path: PrefixedLazyPath,
140140 decorated_directory: DecoratedLazyPath,
141141 file_content: PrefixedLazyPath,
142 bytes: []u8,
142 bytes: [:0]const u8,
143143 output_file: *Output,
144144 output_directory: *Output,
145145 /// The arguments passed after "--" on the "zig build" CLI.
......@@ -228,13 +228,14 @@ pub fn addArtifactArg(run: *Run, artifact: *Step.Compile) void {
228228}
229229
230230pub fn addPrefixedArtifactArg(run: *Run, prefix: []const u8, artifact: *Step.Compile) void {
231 const b = run.step.owner;
231 const graph = run.step.owner.graph;
232 const arena = graph.arena;
232233
233234 const prefixed_artifact: PrefixedArtifact = .{
234 .prefix = b.dupe(prefix),
235 .prefix = graph.dupeString(prefix),
235236 .artifact = artifact,
236237 };
237 run.argv.append(b.allocator, .{ .artifact = prefixed_artifact }) catch @panic("OOM");
238 run.argv.append(arena, .{ .artifact = prefixed_artifact }) catch @panic("OOM");
238239
239240 const bin_file = artifact.getEmittedBin();
240241 bin_file.addStepDependencies(&run.step);
......@@ -279,8 +280,8 @@ pub fn addPrefixedOutputFileArg(
279280
280281 const output = arena.create(Output) catch @panic("OOM");
281282 output.* = .{
282 .prefix = b.dupe(prefix),
283 .basename = b.dupe(basename),
283 .prefix = graph.dupeString(prefix),
284 .basename = graph.dupeString(basename),
284285 .generated_file = graph.addGeneratedFile(&run.step),
285286 };
286287 run.argv.append(arena, .{ .output_file = output }) catch @panic("OOM");
......@@ -318,13 +319,14 @@ pub fn addFileArg(run: *Run, lp: std.Build.LazyPath) void {
318319/// * `addFileArg` - same thing but without the prefix
319320/// * `addOutputFileArg` - for files generated by the child process
320321pub fn addPrefixedFileArg(run: *Run, prefix: []const u8, lp: std.Build.LazyPath) void {
321 const b = run.step.owner;
322 const graph = run.step.owner.graph;
323 const arena = graph.arena;
322324
323325 const prefixed_file_source: PrefixedLazyPath = .{
324 .prefix = b.dupe(prefix),
325 .lazy_path = lp.dupe(b),
326 .prefix = graph.dupeString(prefix),
327 .lazy_path = lp.dupe(graph),
326328 };
327 run.argv.append(b.allocator, .{ .lazy_path = prefixed_file_source }) catch @panic("OOM");
329 run.argv.append(arena, .{ .lazy_path = prefixed_file_source }) catch @panic("OOM");
328330 lp.addStepDependencies(&run.step);
329331}
330332
......@@ -365,7 +367,8 @@ pub fn addFileContentArg(run: *Run, lp: std.Build.LazyPath) void {
365367/// Related:
366368/// * `addFileContentArg` - same thing but without the prefix
367369pub fn addPrefixedFileContentArg(run: *Run, prefix: []const u8, lp: std.Build.LazyPath) void {
368 const b = run.step.owner;
370 const graph = run.step.owner.graph;
371 const arena = graph.arena;
369372
370373 // Some parts of this step's configure phase API rely on the first argument being somewhat
371374 // transparent/readable, but the content of the file specified by `lp` remains completely
......@@ -375,10 +378,10 @@ pub fn addPrefixedFileContentArg(run: *Run, prefix: []const u8, lp: std.Build.La
375378 }
376379
377380 const prefixed_file_source: PrefixedLazyPath = .{
378 .prefix = b.dupe(prefix),
379 .lazy_path = lp.dupe(b),
381 .prefix = graph.dupeString(prefix),
382 .lazy_path = lp.dupe(graph),
380383 };
381 run.argv.append(b.allocator, .{ .file_content = prefixed_file_source }) catch @panic("OOM");
384 run.argv.append(arena, .{ .file_content = prefixed_file_source }) catch @panic("OOM");
382385 lp.addStepDependencies(&run.step);
383386}
384387
......@@ -415,18 +418,19 @@ pub fn addPrefixedOutputDirectoryArg(
415418 basename: []const u8,
416419) std.Build.LazyPath {
417420 if (basename.len == 0) @panic("basename must not be empty");
418 const b = run.step.owner;
421 const graph = run.step.owner.graph;
422 const arena = graph.arena;
419423
420 const output = b.allocator.create(Output) catch @panic("OOM");
424 const output = arena.create(Output) catch @panic("OOM");
421425 output.* = .{
422 .prefix = b.dupe(prefix),
423 .basename = b.dupe(basename),
426 .prefix = graph.dupeString(prefix),
427 .basename = graph.dupeString(basename),
424428 .generated_file = .{ .step = &run.step },
425429 };
426 run.argv.append(b.allocator, .{ .output_directory = output }) catch @panic("OOM");
430 run.argv.append(arena, .{ .output_directory = output }) catch @panic("OOM");
427431
428432 if (run.rename_step_with_output_arg) {
429 run.setName(b.fmt("{s} ({s})", .{ run.step.name, basename }));
433 run.setName(std.fmt.allocPrint(arena, "{s} ({s})", .{ run.step.name, basename }) catch @panic("OOM"));
430434 }
431435
432436 return .{ .generated = .{ .file = &output.generated_file } };
......@@ -437,10 +441,11 @@ pub fn addDirectoryArg(run: *Run, lazy_directory: std.Build.LazyPath) void {
437441}
438442
439443pub fn addPrefixedDirectoryArg(run: *Run, prefix: []const u8, lazy_directory: std.Build.LazyPath) void {
440 const b = run.step.owner;
441 run.argv.append(b.allocator, .{ .decorated_directory = .{
442 .prefix = b.dupe(prefix),
443 .lazy_path = lazy_directory.dupe(b),
444 const graph = run.step.owner.graph;
445 const arena = graph.arena;
446 run.argv.append(arena, .{ .decorated_directory = .{
447 .prefix = graph.dupeString(prefix),
448 .lazy_path = lazy_directory.dupe(graph),
444449 .suffix = "",
445450 } }) catch @panic("OOM");
446451 lazy_directory.addStepDependencies(&run.step);
......@@ -452,11 +457,12 @@ pub fn addDecoratedDirectoryArg(
452457 lazy_directory: std.Build.LazyPath,
453458 suffix: []const u8,
454459) void {
455 const b = run.step.owner;
456 run.argv.append(b.allocator, .{ .decorated_directory = .{
457 .prefix = b.dupe(prefix),
458 .lazy_path = lazy_directory.dupe(b),
459 .suffix = b.dupe(suffix),
460 const graph = run.step.owner.graph;
461 const arena = graph.arena;
462 run.argv.append(arena, .{ .decorated_directory = .{
463 .prefix = graph.dupeString(prefix),
464 .lazy_path = lazy_directory.dupe(graph),
465 .suffix = graph.dupeString(suffix),
460466 } }) catch @panic("OOM");
461467 lazy_directory.addStepDependencies(&run.step);
462468}
......@@ -479,8 +485,8 @@ pub fn addPrefixedDepFileOutputArg(run: *Run, prefix: []const u8, basename: []co
479485
480486 const dep_file = arena.create(Output) catch @panic("OOM");
481487 dep_file.* = .{
482 .prefix = b.dupe(prefix),
483 .basename = b.dupe(basename),
488 .prefix = graph.dupeString(prefix),
489 .basename = graph.dupeString(basename),
484490 .generated_file = graph.addGeneratedFile(&run.step),
485491 };
486492
......@@ -492,8 +498,9 @@ pub fn addPrefixedDepFileOutputArg(run: *Run, prefix: []const u8, basename: []co
492498}
493499
494500pub fn addArg(run: *Run, arg: []const u8) void {
495 const b = run.step.owner;
496 run.argv.append(b.allocator, .{ .bytes = b.dupe(arg) }) catch @panic("OOM");
501 const graph = run.step.owner.graph;
502 const arena = graph.arena;
503 run.argv.append(arena, .{ .bytes = graph.dupeString(arg) }) catch @panic("OOM");
497504}
498505
499506pub fn addArgs(run: *Run, args: []const []const u8) void {
......@@ -509,8 +516,9 @@ pub fn setStdIn(run: *Run, stdin: StdIn) void {
509516}
510517
511518pub fn setCwd(run: *Run, cwd: Build.LazyPath) void {
519 const graph = run.step.owner.graph;
512520 cwd.addStepDependencies(&run.step);
513 run.cwd = cwd.dupe(run.step.owner);
521 run.cwd = cwd.dupe(graph);
514522}
515523
516524pub fn clearEnvironment(run: *Run) void {
......@@ -580,24 +588,28 @@ pub fn removeEnvironmentVariable(run: *Run, key: []const u8) void {
580588
581589/// Adds a check for exact stderr match. Does not add any other checks.
582590pub fn expectStdErrEqual(run: *Run, bytes: []const u8) void {
583 run.addCheck(.{ .expect_stderr_exact = run.step.owner.dupe(bytes) });
591 const graph = run.step.owner.graph;
592 run.addCheck(.{ .expect_stderr_exact = graph.dupeString(bytes) });
584593}
585594
586595pub fn expectStdErrMatch(run: *Run, bytes: []const u8) void {
587 run.addCheck(.{ .expect_stderr_match = run.step.owner.dupe(bytes) });
596 const graph = run.step.owner.graph;
597 run.addCheck(.{ .expect_stderr_match = graph.dupeString(bytes) });
588598}
589599
590600/// Adds a check for exact stdout match as well as a check for exit code 0, if
591601/// there is not already an expected termination check.
592602pub fn expectStdOutEqual(run: *Run, bytes: []const u8) void {
593 run.addCheck(.{ .expect_stdout_exact = run.step.owner.dupe(bytes) });
603 const graph = run.step.owner.graph;
604 run.addCheck(.{ .expect_stdout_exact = graph.dupeString(bytes) });
594605 if (!run.hasTermCheck()) run.expectExitCode(0);
595606}
596607
597608/// Adds a check for stdout match as well as a check for exit code 0, if there
598609/// is not already an expected termination check.
599610pub fn expectStdOutMatch(run: *Run, bytes: []const u8) void {
600 run.addCheck(.{ .expect_stdout_match = run.step.owner.dupe(bytes) });
611 const graph = run.step.owner.graph;
612 run.addCheck(.{ .expect_stdout_match = graph.dupeString(bytes) });
601613 if (!run.hasTermCheck()) run.expectExitCode(0);
602614}
603615
......@@ -641,7 +653,7 @@ pub fn captureStdErr(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPa
641653 captured.* = .{
642654 .output = .{
643655 .prefix = "",
644 .basename = if (options.basename) |basename| b.dupe(basename) else "stderr",
656 .basename = if (options.basename) |basename| graph.dupeString(basename) else "stderr",
645657 .generated_file = graph.addGeneratedFile(&run.step),
646658 },
647659 .trim_whitespace = options.trim_whitespace,
......@@ -664,7 +676,7 @@ pub fn captureStdOut(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPa
664676 captured.* = .{
665677 .output = .{
666678 .prefix = "",
667 .basename = if (options.basename) |basename| b.dupe(basename) else "stdout",
679 .basename = if (options.basename) |basename| graph.dupeString(basename) else "stdout",
668680 .generated_file = graph.addGeneratedFile(&run.step),
669681 },
670682 .trim_whitespace = options.trim_whitespace,
......@@ -678,7 +690,9 @@ pub fn captureStdOut(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPa
678690/// If the Run step is determined to have side-effects, the Run step is always
679691/// executed when it appears in the build graph, regardless of whether this
680692/// file has been modified.
681pub fn addFileInput(self: *Run, file_input: std.Build.LazyPath) void {
682 file_input.addStepDependencies(&self.step);
683 self.file_inputs.append(self.step.owner.allocator, file_input.dupe(self.step.owner)) catch @panic("OOM");
693pub fn addFileInput(run: *Run, file_input: std.Build.LazyPath) void {
694 const graph = run.step.owner.graph;
695 const arena = graph.arena;
696 file_input.addStepDependencies(&run.step);
697 run.file_inputs.append(arena, file_input.dupe(graph)) catch @panic("OOM");
684698}
lib/std/Build/Step/WriteFile.zig+15-15
......@@ -55,10 +55,10 @@ pub const Directory = struct {
5555 /// `exclude_extensions` takes precedence over `include_extensions`.
5656 include_extensions: ?[]const []const u8 = null,
5757
58 pub fn dupe(opts: Options, b: *std.Build) Options {
58 pub fn dupe(opts: Options, graph: *std.Build.Graph) Options {
5959 return .{
60 .exclude_extensions = b.dupeStrings(opts.exclude_extensions),
61 .include_extensions = if (opts.include_extensions) |incs| b.dupeStrings(incs) else null,
60 .exclude_extensions = graph.dupeStrings(opts.exclude_extensions),
61 .include_extensions = if (opts.include_extensions) |incs| graph.dupeStrings(incs) else null,
6262 };
6363 }
6464
......@@ -103,13 +103,13 @@ pub fn create(owner: *std.Build) *WriteFile {
103103}
104104
105105pub fn add(write_file: *WriteFile, sub_path: []const u8, bytes: []const u8) std.Build.LazyPath {
106 const b = write_file.step.owner;
107 const gpa = b.allocator;
108 const file = File{
109 .sub_path = b.dupePath(sub_path),
110 .contents = .{ .bytes = b.dupe(bytes) },
106 const graph = write_file.step.owner.graph;
107 const arena = graph.arena;
108 const file: File = .{
109 .sub_path = graph.dupePath(sub_path),
110 .contents = .{ .bytes = graph.dupeString(bytes) },
111111 };
112 write_file.files.append(gpa, file) catch @panic("OOM");
112 write_file.files.append(arena, file) catch @panic("OOM");
113113 write_file.maybeUpdateName();
114114 return .{
115115 .generated = .{
......@@ -154,14 +154,14 @@ pub fn addCopyDirectory(
154154 sub_path: []const u8,
155155 options: Directory.Options,
156156) std.Build.LazyPath {
157 const b = write_file.step.owner;
158 const gpa = b.allocator;
157 const graph = write_file.step.owner.graph;
158 const arena = graph.arena;
159159 const dir = Directory{
160 .source = source.dupe(b),
161 .sub_path = b.dupePath(sub_path),
162 .options = options.dupe(b),
160 .source = source.dupe(graph),
161 .sub_path = graph.dupePath(sub_path),
162 .options = options.dupe(graph),
163163 };
164 write_file.directories.append(gpa, dir) catch @panic("OOM");
164 write_file.directories.append(arena, dir) catch @panic("OOM");
165165
166166 write_file.maybeUpdateName();
167167 source.addStepDependencies(&write_file.step);
src/main.zig+1
......@@ -5356,6 +5356,7 @@ fn cmdBuild(
53565356 .cc_argv = &.{},
53575357 .inherited = .{
53585358 .resolved_target = resolved_target,
5359 .single_threaded = true,
53595360 },
53605361 .global = config,
53615362 .parent = null,