Commit e16db29887
Changed files (2)
lib
std
Build
lib/std/Build/Step/Compile.zig
@@ -493,7 +493,10 @@ fn addHeaderInstallationToIncludeTree(cs: *Compile, installation: HeaderInstalla
_ = wf.addCopyFile(file.source, file.dest_rel_path);
},
.directory => |dir| {
- _ = dir; // TODO
+ _ = wf.addCopyDirectory(dir.source, dir.dest_rel_path, .{
+ .exclude_extensions = dir.options.exclude_extensions,
+ .include_extensions = dir.options.include_extensions,
+ });
},
};
}
lib/std/Build/Step/WriteFile.zig
@@ -15,9 +15,11 @@ const ArrayList = std.ArrayList;
const WriteFile = @This();
step: Step,
-/// The elements here are pointers because we need stable pointers for the
-/// GeneratedFile field.
+
+// The elements here are pointers because we need stable pointers for the GeneratedFile field.
files: std.ArrayListUnmanaged(*File),
+directories: std.ArrayListUnmanaged(*Directory),
+
output_source_files: std.ArrayListUnmanaged(OutputSourceFile),
generated_directory: std.Build.GeneratedFile,
@@ -33,6 +35,33 @@ pub const File = struct {
}
};
+pub const Directory = struct {
+ source: std.Build.LazyPath,
+ sub_path: []const u8,
+ options: Options,
+ generated_dir: std.Build.GeneratedFile,
+
+ pub const Options = struct {
+ /// File paths that end in any of these suffixes will be excluded from copying.
+ exclude_extensions: []const []const u8 = &.{},
+ /// Only file paths that end in any of these suffixes will be included in copying.
+ /// `null` means that all suffixes will be included.
+ /// `exclude_extensions` takes precedence over `include_extensions`.
+ include_extensions: ?[]const []const u8 = &.{".h"},
+
+ pub fn dupe(self: Options, b: *std.Build) Options {
+ return .{
+ .exclude_extensions = b.dupeStrings(self.exclude_extensions),
+ .include_extensions = if (self.include_extensions) |incs| b.dupeStrings(incs) else null,
+ };
+ }
+ };
+
+ pub fn getPath(self: *Directory) std.Build.LazyPath {
+ return .{ .generated = &self.generated_dir };
+ }
+};
+
pub const OutputSourceFile = struct {
contents: Contents,
sub_path: []const u8,
@@ -53,6 +82,7 @@ pub fn create(owner: *std.Build) *WriteFile {
.makeFn = make,
}),
.files = .{},
+ .directories = .{},
.output_source_files = .{},
.generated_directory = .{ .step = &wf.step },
};
@@ -96,6 +126,28 @@ pub fn addCopyFile(wf: *WriteFile, source: std.Build.LazyPath, sub_path: []const
return file.getPath();
}
+pub fn addCopyDirectory(
+ wf: *WriteFile,
+ source: std.Build.LazyPath,
+ sub_path: []const u8,
+ options: Directory.Options,
+) std.Build.LazyPath {
+ const b = wf.step.owner;
+ const gpa = b.allocator;
+ const dir = gpa.create(Directory) catch @panic("OOM");
+ dir.* = .{
+ .source = source.dupe(b),
+ .sub_path = b.dupePath(sub_path),
+ .options = options.dupe(b),
+ .generated_dir = .{ .step = &wf.step },
+ };
+ wf.directories.append(gpa, dir) catch @panic("OOM");
+
+ wf.maybeUpdateName();
+ source.addStepDependencies(&wf.step);
+ return dir.getPath();
+}
+
/// A path relative to the package root.
/// Be careful with this because it updates source files. This should not be
/// 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 {
}
fn maybeUpdateName(wf: *WriteFile) void {
- if (wf.files.items.len == 1) {
+ if (wf.files.items.len == 1 and wf.directories.items.len == 0) {
// First time adding a file; update name.
if (std.mem.eql(u8, wf.step.name, "WriteFile")) {
wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wf.files.items[0].sub_path});
}
+ } else if (wf.directories.items.len == 1 and wf.files.items.len == 0) {
+ // First time adding a directory; update name.
+ if (std.mem.eql(u8, wf.step.name, "WriteFile")) {
+ wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wf.directories.items[0].sub_path});
+ }
}
}
@@ -209,6 +266,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
},
}
}
+ for (wf.directories.items) |dir| {
+ man.hash.addBytes(dir.source.getPath2(b, step));
+ man.hash.addBytes(dir.sub_path);
+ for (dir.options.exclude_extensions) |ext| man.hash.addBytes(ext);
+ if (dir.options.include_extensions) |incs| for (incs) |inc| man.hash.addBytes(inc);
+ }
if (try step.cacheHit(&man)) {
const digest = man.final();
@@ -233,6 +296,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
};
defer cache_dir.close();
+ const cwd = fs.cwd();
+
for (wf.files.items) |file| {
if (fs.path.dirname(file.sub_path)) |dirname| {
cache_dir.makePath(dirname) catch |err| {
@@ -252,7 +317,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
.copy => |file_source| {
const source_path = file_source.getPath(b);
const prev_status = fs.Dir.updateFile(
- fs.cwd(),
+ cwd,
source_path,
cache_dir,
file.sub_path,
@@ -279,6 +344,64 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
cache_path, file.sub_path,
});
}
+ for (wf.directories.items) |dir| {
+ const full_src_dir_path = dir.source.getPath2(b, step);
+ const dest_dirname = dir.sub_path;
+
+ if (dest_dirname.len != 0) {
+ cache_dir.makePath(dest_dirname) catch |err| {
+ return step.fail("unable to make path '{}{s}{c}{s}': {s}", .{
+ b.cache_root, cache_path, fs.path.sep, dest_dirname, @errorName(err),
+ });
+ };
+ }
+
+ var src_dir = b.build_root.handle.openDir(full_src_dir_path, .{ .iterate = true }) catch |err| {
+ return step.fail("unable to open source directory '{s}': {s}", .{
+ full_src_dir_path, @errorName(err),
+ });
+ };
+ defer src_dir.close();
+
+ var it = try src_dir.walk(b.allocator);
+ next_entry: while (try it.next()) |entry| {
+ for (dir.options.exclude_extensions) |ext| {
+ if (std.mem.endsWith(u8, entry.path, ext)) continue :next_entry;
+ }
+ if (dir.options.include_extensions) |incs| {
+ for (incs) |inc| {
+ if (std.mem.endsWith(u8, entry.path, inc)) break;
+ } else {
+ continue :next_entry;
+ }
+ }
+ const full_src_entry_path = b.pathJoin(&.{ full_src_dir_path, entry.path });
+ const dest_path = b.pathJoin(&.{ dest_dirname, entry.path });
+ switch (entry.kind) {
+ .directory => try cache_dir.makePath(dest_path),
+ .file => {
+ const prev_status = fs.Dir.updateFile(
+ cwd,
+ full_src_entry_path,
+ cache_dir,
+ dest_path,
+ .{},
+ ) catch |err| {
+ return step.fail("unable to update file from '{s}' to '{}{s}{c}{s}': {s}", .{
+ full_src_entry_path,
+ b.cache_root,
+ cache_path,
+ fs.path.sep,
+ dest_path,
+ @errorName(err),
+ });
+ };
+ _ = prev_status;
+ },
+ else => continue,
+ }
+ }
+ }
try step.writeManifest(&man);
}