Recursively copy directory using std?

Is there a way to cp -r with Zig’s stdlib? I see there’s Dir.deleteTree, but I don’t see anything for copying.

3 Likes

https://ziglang.org/documentation/master/std/#A;std?fs%20copy

It doesn’t seem there’s anything particularly interesting in fs. Maybe there’s a related syscall that just hasn’t been wired into the high-level interface?

https://ziglang.org/documentation/master/std/#A;std?os%20copy

Nope, nothing interesting under os either.

I guess the first question is whether the OS gives us a good tool for the job or if we would have to implement everything ourselves.

Looking at a cp -r strace it seems that it’s entirely up to us to implement this functionality though.

So, I guess a PR would be welcome :^)

4 Likes

A basic/naive approach (with no special handling of the dest existing, metadata copying, etc) would be:

const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer std.debug.assert(gpa.deinit() == .ok);
    const allocator = gpa.allocator();

    var src_dir = try std.fs.cwd().openIterableDir("src_dir", .{});
    defer src_dir.close();

    var dest_dir = try std.fs.cwd().makeOpenPath("dest_dir", .{});
    defer dest_dir.close();

    var walker = try src_dir.walk(allocator);
    defer walker.deinit();

    while (try walker.next()) |entry| {
        switch (entry.kind) {
            .file => {
                try entry.dir.copyFile(entry.basename, dest_dir, entry.path, .{});
            },
            .directory => {
                try dest_dir.makeDir(entry.path);
            },
            else => return error.UnexpectedEntryKind,
        }
    }
}

Out of curiosity, I benchmarked it on Windows against xcopy using a giant nested directory (the result of npx create-react-app my-app) and the Zig version loses, so there’s potential for optimization:

> hyperfine "xcopy /E /Q /I /Y my-app my-app-copy" "copydir.exe" --prepare="rm -rf my-app-copy"
Benchmark 1: xcopy /E /Q /I /Y my-app my-app-copy
  Time (mean ± σ):     23.462 s ±  0.625 s    [User: 1.557 s, System: 19.337 s]
  Range (min … max):   22.704 s … 24.793 s    10 runs

Benchmark 2: copydir.exe
  Time (mean ± σ):     27.017 s ±  0.307 s    [User: 1.063 s, System: 24.863 s]
  Range (min … max):   26.502 s … 27.508 s    10 runs

Summary
  'xcopy /E /Q /I /Y my-app my-app-copy' ran
    1.15 ± 0.03 times faster than 'copydir.exe'

(similar results with the c_allocator, so it’s not the GPA that’s causing the difference)

10 Likes