authorgravatar for carl@astholm.seCarl Åstholm <carl@astholm.se> 2024-03-03 17:14:02+01:00
committergravatar for carl@astholm.seCarl Åstholm <carl@astholm.se> 2024-04-07 15:34:47+02:00
loge16db2988777e18c56925b4a458914b20a183c12
tree6d86501b0a4083c1122a0dd649266ab88911ddac
parent2c7be4f8dd55653be6cbf5d06f8f9c47e8d9276e

Implement `WriteFile.addCopyDirectory`


2 files changed, 131 insertions(+), 5 deletions(-)

lib/std/Build/Step/Compile.zig+4-1
...@@ -493,7 +493,10 @@ fn addHeaderInstallationToIncludeTree(cs: *Compile, installation: HeaderInstalla...@@ -493,7 +493,10 @@ fn addHeaderInstallationToIncludeTree(cs: *Compile, installation: HeaderInstalla
493 _ = wf.addCopyFile(file.source, file.dest_rel_path);493 _ = wf.addCopyFile(file.source, file.dest_rel_path);
494 },494 },
495 .directory => |dir| {495 .directory => |dir| {
496 _ = dir; // TODO496 _ = wf.addCopyDirectory(dir.source, dir.dest_rel_path, .{
497 .exclude_extensions = dir.options.exclude_extensions,
498 .include_extensions = dir.options.include_extensions,
499 });
497 },500 },
498 };501 };
499}502}
lib/std/Build/Step/WriteFile.zig+127-4
...@@ -15,9 +15,11 @@ const ArrayList = std.ArrayList;...@@ -15,9 +15,11 @@ const ArrayList = std.ArrayList;
15const WriteFile = @This();15const WriteFile = @This();
1616
17step: Step,17step: Step,
18/// The elements here are pointers because we need stable pointers for the18
19/// GeneratedFile field.19// The elements here are pointers because we need stable pointers for the GeneratedFile field.
20files: std.ArrayListUnmanaged(*File),20files: std.ArrayListUnmanaged(*File),
21directories: std.ArrayListUnmanaged(*Directory),
22
21output_source_files: std.ArrayListUnmanaged(OutputSourceFile),23output_source_files: std.ArrayListUnmanaged(OutputSourceFile),
22generated_directory: std.Build.GeneratedFile,24generated_directory: std.Build.GeneratedFile,
2325
...@@ -33,6 +35,33 @@ pub const File = struct {...@@ -33,6 +35,33 @@ pub const File = struct {
33 }35 }
34};36};
3537
38pub const Directory = struct {
39 source: std.Build.LazyPath,
40 sub_path: []const u8,
41 options: Options,
42 generated_dir: std.Build.GeneratedFile,
43
44 pub const Options = struct {
45 /// File paths that end in any of these suffixes will be excluded from copying.
46 exclude_extensions: []const []const u8 = &.{},
47 /// Only file paths that end in any of these suffixes will be included in copying.
48 /// `null` means that all suffixes will be included.
49 /// `exclude_extensions` takes precedence over `include_extensions`.
50 include_extensions: ?[]const []const u8 = &.{".h"},
51
52 pub fn dupe(self: Options, b: *std.Build) Options {
53 return .{
54 .exclude_extensions = b.dupeStrings(self.exclude_extensions),
55 .include_extensions = if (self.include_extensions) |incs| b.dupeStrings(incs) else null,
56 };
57 }
58 };
59
60 pub fn getPath(self: *Directory) std.Build.LazyPath {
61 return .{ .generated = &self.generated_dir };
62 }
63};
64
36pub const OutputSourceFile = struct {65pub const OutputSourceFile = struct {
37 contents: Contents,66 contents: Contents,
38 sub_path: []const u8,67 sub_path: []const u8,
...@@ -53,6 +82,7 @@ pub fn create(owner: *std.Build) *WriteFile {...@@ -53,6 +82,7 @@ pub fn create(owner: *std.Build) *WriteFile {
53 .makeFn = make,82 .makeFn = make,
54 }),83 }),
55 .files = .{},84 .files = .{},
85 .directories = .{},
56 .output_source_files = .{},86 .output_source_files = .{},
57 .generated_directory = .{ .step = &wf.step },87 .generated_directory = .{ .step = &wf.step },
58 };88 };
...@@ -96,6 +126,28 @@ pub fn addCopyFile(wf: *WriteFile, source: std.Build.LazyPath, sub_path: []const...@@ -96,6 +126,28 @@ pub fn addCopyFile(wf: *WriteFile, source: std.Build.LazyPath, sub_path: []const
96 return file.getPath();126 return file.getPath();
97}127}
98128
129pub fn addCopyDirectory(
130 wf: *WriteFile,
131 source: std.Build.LazyPath,
132 sub_path: []const u8,
133 options: Directory.Options,
134) std.Build.LazyPath {
135 const b = wf.step.owner;
136 const gpa = b.allocator;
137 const dir = gpa.create(Directory) catch @panic("OOM");
138 dir.* = .{
139 .source = source.dupe(b),
140 .sub_path = b.dupePath(sub_path),
141 .options = options.dupe(b),
142 .generated_dir = .{ .step = &wf.step },
143 };
144 wf.directories.append(gpa, dir) catch @panic("OOM");
145
146 wf.maybeUpdateName();
147 source.addStepDependencies(&wf.step);
148 return dir.getPath();
149}
150
99/// A path relative to the package root.151/// A path relative to the package root.
100/// Be careful with this because it updates source files. This should not be152/// Be careful with this because it updates source files. This should not be
101/// used as part of the normal build process, but as a utility occasionally153/// used as part of the normal build process, but as a utility occasionally
...@@ -130,11 +182,16 @@ pub fn getDirectory(wf: *WriteFile) std.Build.LazyPath {...@@ -130,11 +182,16 @@ pub fn getDirectory(wf: *WriteFile) std.Build.LazyPath {
130}182}
131183
132fn maybeUpdateName(wf: *WriteFile) void {184fn maybeUpdateName(wf: *WriteFile) void {
133 if (wf.files.items.len == 1) {185 if (wf.files.items.len == 1 and wf.directories.items.len == 0) {
134 // First time adding a file; update name.186 // First time adding a file; update name.
135 if (std.mem.eql(u8, wf.step.name, "WriteFile")) {187 if (std.mem.eql(u8, wf.step.name, "WriteFile")) {
136 wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wf.files.items[0].sub_path});188 wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wf.files.items[0].sub_path});
137 }189 }
190 } else if (wf.directories.items.len == 1 and wf.files.items.len == 0) {
191 // First time adding a directory; update name.
192 if (std.mem.eql(u8, wf.step.name, "WriteFile")) {
193 wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wf.directories.items[0].sub_path});
194 }
138 }195 }
139}196}
140197
...@@ -209,6 +266,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -209,6 +266,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
209 },266 },
210 }267 }
211 }268 }
269 for (wf.directories.items) |dir| {
270 man.hash.addBytes(dir.source.getPath2(b, step));
271 man.hash.addBytes(dir.sub_path);
272 for (dir.options.exclude_extensions) |ext| man.hash.addBytes(ext);
273 if (dir.options.include_extensions) |incs| for (incs) |inc| man.hash.addBytes(inc);
274 }
212275
213 if (try step.cacheHit(&man)) {276 if (try step.cacheHit(&man)) {
214 const digest = man.final();277 const digest = man.final();
...@@ -233,6 +296,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -233,6 +296,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
233 };296 };
234 defer cache_dir.close();297 defer cache_dir.close();
235298
299 const cwd = fs.cwd();
300
236 for (wf.files.items) |file| {301 for (wf.files.items) |file| {
237 if (fs.path.dirname(file.sub_path)) |dirname| {302 if (fs.path.dirname(file.sub_path)) |dirname| {
238 cache_dir.makePath(dirname) catch |err| {303 cache_dir.makePath(dirname) catch |err| {
...@@ -252,7 +317,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -252,7 +317,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
252 .copy => |file_source| {317 .copy => |file_source| {
253 const source_path = file_source.getPath(b);318 const source_path = file_source.getPath(b);
254 const prev_status = fs.Dir.updateFile(319 const prev_status = fs.Dir.updateFile(
255 fs.cwd(),320 cwd,
256 source_path,321 source_path,
257 cache_dir,322 cache_dir,
258 file.sub_path,323 file.sub_path,
...@@ -279,6 +344,64 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -279,6 +344,64 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
279 cache_path, file.sub_path,344 cache_path, file.sub_path,
280 });345 });
281 }346 }
347 for (wf.directories.items) |dir| {
348 const full_src_dir_path = dir.source.getPath2(b, step);
349 const dest_dirname = dir.sub_path;
350
351 if (dest_dirname.len != 0) {
352 cache_dir.makePath(dest_dirname) catch |err| {
353 return step.fail("unable to make path '{}{s}{c}{s}': {s}", .{
354 b.cache_root, cache_path, fs.path.sep, dest_dirname, @errorName(err),
355 });
356 };
357 }
358
359 var src_dir = b.build_root.handle.openDir(full_src_dir_path, .{ .iterate = true }) catch |err| {
360 return step.fail("unable to open source directory '{s}': {s}", .{
361 full_src_dir_path, @errorName(err),
362 });
363 };
364 defer src_dir.close();
365
366 var it = try src_dir.walk(b.allocator);
367 next_entry: while (try it.next()) |entry| {
368 for (dir.options.exclude_extensions) |ext| {
369 if (std.mem.endsWith(u8, entry.path, ext)) continue :next_entry;
370 }
371 if (dir.options.include_extensions) |incs| {
372 for (incs) |inc| {
373 if (std.mem.endsWith(u8, entry.path, inc)) break;
374 } else {
375 continue :next_entry;
376 }
377 }
378 const full_src_entry_path = b.pathJoin(&.{ full_src_dir_path, entry.path });
379 const dest_path = b.pathJoin(&.{ dest_dirname, entry.path });
380 switch (entry.kind) {
381 .directory => try cache_dir.makePath(dest_path),
382 .file => {
383 const prev_status = fs.Dir.updateFile(
384 cwd,
385 full_src_entry_path,
386 cache_dir,
387 dest_path,
388 .{},
389 ) catch |err| {
390 return step.fail("unable to update file from '{s}' to '{}{s}{c}{s}': {s}", .{
391 full_src_entry_path,
392 b.cache_root,
393 cache_path,
394 fs.path.sep,
395 dest_path,
396 @errorName(err),
397 });
398 };
399 _ = prev_status;
400 },
401 else => continue,
402 }
403 }
404 }
282405
283 try step.writeManifest(&man);406 try step.writeManifest(&man);
284}407}