aboutsummaryrefslogtreecommitdiff
path: root/std/fs.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2019-07-14 03:06:20 -0400
committerAndrew Kelley <andrew@ziglang.org>2019-07-15 01:45:26 -0400
commit6096dc5f94bf69cb73b0fc89d36bf125f7ff6467 (patch)
tree9fd47a9ae9ea9f73f9ca2b24867cdf6fc0450ec2 /std/fs.zig
parent7d9ee5d6d53011e9edc7ca10b76d64af807a833f (diff)
downloadzig-6096dc5f94bf69cb73b0fc89d36bf125f7ff6467.tar.gz
zig-6096dc5f94bf69cb73b0fc89d36bf125f7ff6467.zip
move some of the installation from cmake to zig build
This moves the installation of shipped source files from large CMakeLists.txt lists to zig build recursive directory installation. On my computer a cmake `make install` takes 2.4 seconds even when it has to do nothing, and prints a lot of unnecessary lines to stdout that say "up-to-date: [some file it is installing]". After this commit, the default output of `make` is down to 1 second, and it does not print any junk to stdout. Further, a `make install` is no longer required and `make` is sufficient. This closes #2874. It also closes #2585. `make` now always invokes `zig build` for installing files and libuserland.a, and zig's own caching system makes that go fast.
Diffstat (limited to 'std/fs.zig')
-rw-r--r--std/fs.zig151
1 files changed, 147 insertions, 4 deletions
diff --git a/std/fs.zig b/std/fs.zig
index daad472958..df55fb36b2 100644
--- a/std/fs.zig
+++ b/std/fs.zig
@@ -70,6 +70,61 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
}
}
+// TODO fix enum literal not casting to error union
+const PrevStatus = enum {
+ stale,
+ fresh,
+};
+
+pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus {
+ return updateFileMode(source_path, dest_path, null);
+}
+
+/// Check the file size and mtime of `source_path` and `dest_path`. If they are equal, does nothing.
+/// Otherwise, atomically copies `source_path` to `dest_path`. The destination file gains the mtime
+/// and atime of the source file so that the next call to `updateFile` will not need a copy.
+/// TODO https://github.com/ziglang/zig/issues/2885
+/// Returns the previous status of the file before updating.
+pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus {
+ var src_file = try File.openRead(source_path);
+ defer src_file.close();
+
+ const src_stat = try src_file.stat();
+ check_dest_stat: {
+ const dest_stat = blk: {
+ var dest_file = File.openRead(dest_path) catch |err| switch (err) {
+ error.FileNotFound => break :check_dest_stat,
+ else => |e| return e,
+ };
+ defer dest_file.close();
+
+ break :blk try dest_file.stat();
+ };
+
+ if (src_stat.size == dest_stat.size and
+ src_stat.mtime == dest_stat.mtime and
+ src_stat.mode == dest_stat.mode)
+ {
+ return PrevStatus.fresh;
+ }
+ }
+ const in_stream = &src_file.inStream().stream;
+
+ var atomic_file = try AtomicFile.init(dest_path, mode orelse src_stat.mode);
+ defer atomic_file.deinit();
+
+ var buf: [mem.page_size * 6]u8 = undefined;
+ while (true) {
+ const amt = try in_stream.readFull(buf[0..]);
+ try atomic_file.file.write(buf[0..amt]);
+ if (amt != buf.len) {
+ try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime);
+ try atomic_file.finish();
+ return PrevStatus.stale;
+ }
+ }
+}
+
/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
/// merged and readily available,
/// there is a possibility of power loss or application termination leaving temporary files present
@@ -105,7 +160,7 @@ pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.M
var atomic_file = try AtomicFile.init(dest_path, mode);
defer atomic_file.deinit();
- var buf: [mem.page_size]u8 = undefined;
+ var buf: [mem.page_size * 6]u8 = undefined;
while (true) {
const amt = try in_file.read(buf[0..]);
try atomic_file.file.write(buf[0..amt]);
@@ -256,9 +311,6 @@ pub fn deleteDirW(dir_path: [*]const u16) !void {
return os.rmdirW(dir_path);
}
-/// Whether ::full_path describes a symlink, file, or directory, this function
-/// removes it. If it cannot be removed because it is a non-empty directory,
-/// this function recursively removes its entries and then tries again.
const DeleteTreeError = error{
OutOfMemory,
AccessDenied,
@@ -290,7 +342,11 @@ const DeleteTreeError = error{
Unexpected,
};
+/// Whether `full_path` describes a symlink, file, or directory, this function
+/// removes it. If it cannot be removed because it is a non-empty directory,
+/// this function recursively removes its entries and then tries again.
/// TODO determine if we can remove the allocator requirement
+/// https://github.com/ziglang/zig/issues/2886
pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void {
start_over: while (true) {
var got_access_denied = false;
@@ -427,7 +483,9 @@ pub const Dir = struct {
Unexpected,
};
+ /// Call close when done.
/// TODO remove the allocator requirement from this API
+ /// https://github.com/ziglang/zig/issues/2885
pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir {
return Dir{
.allocator = allocator,
@@ -683,13 +741,98 @@ pub const Dir = struct {
}
};
+pub const Walker = struct {
+ stack: std.ArrayList(StackItem),
+ name_buffer: std.Buffer,
+
+ pub const Entry = struct {
+ path: []const u8,
+ basename: []const u8,
+ kind: Dir.Entry.Kind,
+ };
+
+ const StackItem = struct {
+ dir_it: Dir,
+ 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.len == 0) return null;
+ // `top` becomes invalid after appending to `self.stack`.
+ const top = &self.stack.toSlice()[self.stack.len - 1];
+ const dirname_len = top.dirname_len;
+ if (try top.dir_it.next()) |base| {
+ self.name_buffer.shrink(dirname_len);
+ try self.name_buffer.appendByte(path.sep);
+ try self.name_buffer.append(base.name);
+ if (base.kind == .Directory) {
+ // TODO https://github.com/ziglang/zig/issues/2888
+ var new_dir = try Dir.open(self.stack.allocator, self.name_buffer.toSliceConst());
+ {
+ errdefer new_dir.close();
+ try self.stack.append(StackItem{
+ .dir_it = new_dir,
+ .dirname_len = self.name_buffer.len(),
+ });
+ }
+ }
+ return Entry{
+ .basename = self.name_buffer.toSliceConst()[dirname_len + 1 ..],
+ .path = self.name_buffer.toSliceConst(),
+ .kind = base.kind,
+ };
+ } else {
+ self.stack.pop().dir_it.close();
+ }
+ }
+ }
+
+ pub fn deinit(self: *Walker) void {
+ while (self.stack.popOrNull()) |*item| item.dir_it.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.
+/// TODO: https://github.com/ziglang/zig/issues/2888
+pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
+ assert(!mem.endsWith(u8, dir_path, path.sep_str));
+
+ var dir_it = try Dir.open(allocator, dir_path);
+ errdefer dir_it.close();
+
+ var name_buffer = try std.Buffer.init(allocator, dir_path);
+ errdefer name_buffer.deinit();
+
+ var walker = Walker{
+ .stack = std.ArrayList(Walker.StackItem).init(allocator),
+ .name_buffer = name_buffer,
+ };
+
+ try walker.stack.append(Walker.StackItem{
+ .dir_it = dir_it,
+ .dirname_len = dir_path.len,
+ });
+
+ return walker;
+}
+
/// Read value of a symbolic link.
/// The return value is a slice of buffer, from index `0`.
+/// TODO https://github.com/ziglang/zig/issues/2888
pub fn readLink(pathname: []const u8, buffer: *[os.PATH_MAX]u8) ![]u8 {
return os.readlink(pathname, buffer);
}
/// Same as `readLink`, except the `pathname` parameter is null-terminated.
+/// TODO https://github.com/ziglang/zig/issues/2888
pub fn readLinkC(pathname: [*]const u8, buffer: *[os.PATH_MAX]u8) ![]u8 {
return os.readlinkC(pathname, buffer);
}