| ... | @@ -15,9 +15,11 @@ const ArrayList = std.ArrayList; | ... | @@ -15,9 +15,11 @@ const ArrayList = std.ArrayList; |
| 15 | const WriteFile = @This(); | 15 | const WriteFile = @This(); |
| 16 | | 16 | |
| 17 | step: Step, | 17 | step: Step, |
| 18 | /// The elements here are pointers because we need stable pointers for the | 18 | |
| 19 | /// GeneratedFile field. | 19 | // The elements here are pointers because we need stable pointers for the GeneratedFile field. |
| 20 | files: std.ArrayListUnmanaged(*File), | 20 | files: std.ArrayListUnmanaged(*File), |
| | 21 | directories: std.ArrayListUnmanaged(*Directory), |
| | 22 | |
| 21 | output_source_files: std.ArrayListUnmanaged(OutputSourceFile), | 23 | output_source_files: std.ArrayListUnmanaged(OutputSourceFile), |
| 22 | generated_directory: std.Build.GeneratedFile, | 24 | generated_directory: std.Build.GeneratedFile, |
| 23 | | 25 | |
| ... | @@ -33,6 +35,33 @@ pub const File = struct { | ... | @@ -33,6 +35,33 @@ pub const File = struct { |
| 33 | } | 35 | } |
| 34 | }; | 36 | }; |
| 35 | | 37 | |
| | 38 | pub 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 | |
| 36 | pub const OutputSourceFile = struct { | 65 | pub 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 | } |
| 98 | | 128 | |
| | 129 | pub 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 be | 152 | /// 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 occasionally | 153 | /// 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 | } |
| 131 | | 183 | |
| 132 | fn maybeUpdateName(wf: *WriteFile) void { | 184 | fn 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 | } |
| 140 | | 197 | |
| ... | @@ -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 | } |
| 212 | | 275 | |
| 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(); |
| 235 | | 298 | |
| | 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 | } |
| 282 | | 405 | |
| 283 | try step.writeManifest(&man); | 406 | try step.writeManifest(&man); |
| 284 | } | 407 | } |