diff --git a/lib/std/build.zig b/lib/std/build.zig index 22f454adca..685fd2bdc5 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -3015,7 +3015,8 @@ pub const InstallDirStep = struct { const self = @fieldParentPtr(InstallDirStep, "step", step); const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir); const full_src_dir = self.builder.pathFromRoot(self.options.source_dir); - var it = try fs.walkPath(self.builder.allocator, full_src_dir); + const src_dir = try std.fs.cwd().openDir(full_src_dir, .{ .iterate = true }); + var it = try src_dir.walk(self.builder.allocator); next_entry: while (try it.next()) |entry| { for (self.options.exclude_extensions) |ext| { if (mem.endsWith(u8, entry.path, ext)) { @@ -3023,9 +3024,12 @@ pub const InstallDirStep = struct { } } - const rel_path = entry.path[full_src_dir.len + 1 ..]; + const full_path = try fs.path.join(self.builder.allocator, &[_][]const u8{ + full_src_dir, entry.path, + }); + const dest_path = try fs.path.join(self.builder.allocator, &[_][]const u8{ - dest_prefix, rel_path, + dest_prefix, entry.path, }); switch (entry.kind) { @@ -3038,7 +3042,7 @@ pub const InstallDirStep = struct { } } - try self.builder.updateFile(entry.path, dest_path); + try self.builder.updateFile(full_path, dest_path); }, else => continue, } diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 4ecdc3803b..2504965e3a 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -737,6 +737,94 @@ pub const Dir = struct { } } + pub const Walker = struct { + stack: std.ArrayList(StackItem), + name_buffer: std.ArrayList(u8), + + pub const WalkerEntry = struct { + /// The containing directory. This can be used to operate directly on `basename` + /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths. + /// The directory remains open until `next` or `deinit` is called. + dir: Dir, + basename: []const u8, + path: []const u8, + kind: Dir.Entry.Kind, + }; + + const StackItem = struct { + iter: Dir.Iterator, + dirname_len: usize, + }; + + /// After each call to this function, and on deinit(), the memory returned + /// from this function becomes invalid. A copy must be made in order to keep + /// a reference to the path. + pub fn next(self: *Walker) !?WalkerEntry { + while (self.stack.items.len != 0) { + // `top` becomes invalid after appending to `self.stack` + var top = &self.stack.items[self.stack.items.len - 1]; + const dirname_len = top.dirname_len; + if (try top.iter.next()) |base| { + self.name_buffer.shrinkRetainingCapacity(dirname_len); + if (self.name_buffer.items.len != 0) { + try self.name_buffer.append(path.sep); + } + try self.name_buffer.appendSlice(base.name); + if (base.kind == .Directory) { + var new_dir = top.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) { + error.NameTooLong => unreachable, // no path sep in base.name + else => |e| return e, + }; + { + errdefer new_dir.close(); + try self.stack.append(StackItem{ + .iter = new_dir.iterate(), + .dirname_len = self.name_buffer.items.len, + }); + top = &self.stack.items[self.stack.items.len - 1]; + } + } + return WalkerEntry{ + .dir = top.iter.dir, + .basename = self.name_buffer.items[dirname_len + 1 ..], + .path = self.name_buffer.items, + .kind = base.kind, + }; + } else { + self.stack.pop().iter.dir.close(); + } + } + return null; + } + + pub fn deinit(self: *Walker) void { + while (self.stack.popOrNull()) |*item| item.iter.dir.close(); + self.stack.deinit(); + self.name_buffer.deinit(); + } + }; + + /// Recursively iterates over a directory. + /// Must call `Walker.deinit` when done. + /// The order of returned file system entries is undefined. + pub fn walk(self: Dir, allocator: *Allocator) !Walker { + var name_buffer = std.ArrayList(u8).init(allocator); + errdefer name_buffer.deinit(); + + var stack = std.ArrayList(Walker.StackItem).init(allocator); + errdefer stack.deinit(); + + try stack.append(Walker.StackItem{ + .iter = self.iterate(), + .dirname_len = 0, + }); + + return Walker{ + .stack = stack, + .name_buffer = name_buffer, + }; + } + pub const OpenError = error{ FileNotFound, NotDir, @@ -2259,100 +2347,7 @@ pub fn symLinkAbsoluteZ(target_path_c: [*:0]const u8, sym_link_path_c: [*:0]cons pub const symLink = @compileError("deprecated: use Dir.symLink or symLinkAbsolute"); pub const symLinkC = @compileError("deprecated: use Dir.symLinkZ or symLinkAbsoluteZ"); -pub const Walker = struct { - stack: std.ArrayList(StackItem), - name_buffer: std.ArrayList(u8), - - pub const Entry = struct { - /// The containing directory. This can be used to operate directly on `basename` - /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths. - /// The directory remains open until `next` or `deinit` is called. - dir: Dir, - /// TODO make this null terminated for API convenience - basename: []const u8, - - path: []const u8, - kind: Dir.Entry.Kind, - }; - - const StackItem = struct { - dir_it: Dir.Iterator, - dirname_len: usize, - }; - - /// After each call to this function, and on deinit(), the memory returned - /// from this function becomes invalid. A copy must be made in order to keep - /// a reference to the path. - pub fn next(self: *Walker) !?Entry { - while (true) { - if (self.stack.items.len == 0) return null; - // `top` becomes invalid after appending to `self.stack`. - var top = &self.stack.items[self.stack.items.len - 1]; - const dirname_len = top.dirname_len; - if (try top.dir_it.next()) |base| { - self.name_buffer.shrinkRetainingCapacity(dirname_len); - try self.name_buffer.append(path.sep); - try self.name_buffer.appendSlice(base.name); - if (base.kind == .Directory) { - var new_dir = top.dir_it.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) { - error.NameTooLong => unreachable, // no path sep in base.name - else => |e| return e, - }; - { - errdefer new_dir.close(); - try self.stack.append(StackItem{ - .dir_it = new_dir.iterate(), - .dirname_len = self.name_buffer.items.len, - }); - top = &self.stack.items[self.stack.items.len - 1]; - } - } - return Entry{ - .dir = top.dir_it.dir, - .basename = self.name_buffer.items[dirname_len + 1 ..], - .path = self.name_buffer.items, - .kind = base.kind, - }; - } else { - self.stack.pop().dir_it.dir.close(); - } - } - } - - pub fn deinit(self: *Walker) void { - while (self.stack.popOrNull()) |*item| item.dir_it.dir.close(); - self.stack.deinit(); - self.name_buffer.deinit(); - } -}; - -/// Recursively iterates over a directory. -/// Must call `Walker.deinit` when done. -/// `dir_path` must not end in a path separator. -/// The order of returned file system entries is undefined. -pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { - assert(!mem.endsWith(u8, dir_path, path.sep_str)); - - var dir = try cwd().openDir(dir_path, .{ .iterate = true }); - errdefer dir.close(); - - var name_buffer = std.ArrayList(u8).init(allocator); - errdefer name_buffer.deinit(); - - try name_buffer.appendSlice(dir_path); - - var walker = Walker{ - .stack = std.ArrayList(Walker.StackItem).init(allocator), - .name_buffer = name_buffer, - }; - - try walker.stack.append(Walker.StackItem{ - .dir_it = dir.iterate(), - .dirname_len = dir_path.len, - }); - - return walker; -} +pub const walkPath = @compileError("deprecated: use Dir.walk"); pub const OpenSelfExeError = error{ SharingViolation, diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 9464959cdb..54f3e238d3 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -927,8 +927,9 @@ test "walker" { } const tmp_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] }); + const tmp_dir = try fs.cwd().openDir(tmp_path, .{ .iterate = true }); - var walker = try fs.walkPath(testing.allocator, tmp_path); + var walker = try tmp_dir.walk(testing.allocator); defer walker.deinit(); i = 0; @@ -941,7 +942,7 @@ test "walker" { try fs.path.join(allocator, &[_][]const u8{ expected_dir_name, name }); var entry = (try walker.next()).?; - try testing.expectEqualStrings(expected_dir_name, try fs.path.relative(allocator, tmp_path, entry.path)); + try testing.expectEqualStrings(expected_dir_name, entry.path); } }