From b26d13924441ff8d5598d1445dff39ac4a46dac0 Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Tue, 9 Sep 2025 13:51:41 -0700 Subject: [PATCH] continue to clean --- build.zig | 137 +++++++++--------------------------------------------- 1 file changed, 22 insertions(+), 115 deletions(-) diff --git a/build.zig b/build.zig index b80e636..1be803f 100644 --- a/build.zig +++ b/build.zig @@ -6,17 +6,17 @@ pub fn build(b: *std.Build) void { const vosk_dep = b.dependency("vosk", .{}); - // Create a proper build step for model download with caching // We need to use curl for this as the domain doesn't work with zig TLS const model_step = ModelDownloadStep.create(b); // Install the model to the output directory const install_model = b.addInstallDirectory(.{ - .source_dir = b.path("vosk-model-small-en-us-0.15"), + .source_dir = model_step.getOutputPath(), .install_dir = .bin, .install_subdir = "vosk-model-small-en-us-0.15", }); install_model.step.dependOn(&model_step.step); + b.getInstallStep().dependOn(&install_model.step); const exe = b.addExecutable(.{ .name = "stt", @@ -33,8 +33,6 @@ pub fn build(b: *std.Build) void { exe.linkSystemLibrary("vosk"); exe.linkSystemLibrary("asound"); - b.getInstallStep().dependOn(&install_model.step); - b.installArtifact(exe); const run_step = b.step("run", "Run the app"); @@ -65,6 +63,17 @@ const ModelDownloadStep = struct { return self; } + pub fn getOutputPath(self: *ModelDownloadStep) std.Build.LazyPath { + var hasher = std.hash.Wyhash.init(0); + hasher.update("https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip"); + const cache_hash = hasher.final(); + + var cache_dir_buf: [std.fs.max_path_bytes]u8 = undefined; + const cache_dir = std.fmt.bufPrint(&cache_dir_buf, "{s}/o/{x}/vosk-model-small-en-us-0.15", .{ self.builder.cache_root.path.?, cache_hash }) catch @panic("path too long"); + + return .{ .cwd_relative = self.builder.allocator.dupe(u8, cache_dir) catch @panic("OOM") }; + } + fn make(step: *std.Build.Step, options: std.Build.Step.MakeOptions) anyerror!void { _ = options; const self: *ModelDownloadStep = @fieldParentPtr("step", step); @@ -84,37 +93,6 @@ const ModelDownloadStep = struct { // Check if already cached if (std.fs.cwd().access(cached_model_dir, .{})) |_| { - // Copy from cached version using stdlib - std.fs.cwd().deleteTree(model_dir) catch {}; - std.fs.cwd().makePath(model_dir) catch {}; - var cached_dir = std.fs.cwd().openDir(cached_model_dir, .{ .iterate = true }) catch return error.CopyFailed; - defer cached_dir.close(); - var target_dir = std.fs.cwd().openDir(model_dir, .{}) catch return error.CopyFailed; - defer target_dir.close(); - - var iter = cached_dir.iterate(); - while (iter.next() catch return error.CopyFailed) |entry| { - switch (entry.kind) { - .file => { - cached_dir.copyFile(entry.name, target_dir, entry.name, .{}) catch return error.CopyFailed; - }, - .directory => { - target_dir.makeDir(entry.name) catch {}; - var sub_cached = cached_dir.openDir(entry.name, .{ .iterate = true }) catch return error.CopyFailed; - defer sub_cached.close(); - var sub_target = target_dir.openDir(entry.name, .{}) catch return error.CopyFailed; - defer sub_target.close(); - - var sub_iter = sub_cached.iterate(); - while (sub_iter.next() catch return error.CopyFailed) |sub_entry| { - if (sub_entry.kind == .file) { - sub_cached.copyFile(sub_entry.name, sub_target, sub_entry.name, .{}) catch return error.CopyFailed; - } - } - }, - else => {}, - } - } step.result_cached = true; return; } else |_| {} @@ -128,93 +106,22 @@ const ModelDownloadStep = struct { // Download const download_result = std.process.Child.run(.{ .allocator = self.builder.allocator, - .argv = &.{ "curl", "-s", "-L", "-o", model_zip, "https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip" }, + .argv = &.{ "curl", "-s", "-o", model_zip, "https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip" }, }) catch return error.DownloadFailed; if (download_result.term.Exited != 0) return error.DownloadFailed; - // Extract to cache - const extract_result = std.process.Child.run(.{ - .allocator = self.builder.allocator, - .argv = &.{ "unzip", "-o", model_zip, "-d", cache_dir }, - }) catch return error.UnzipFailed; - if (extract_result.term.Exited != 0) return error.UnzipFailed; + // Extract to cache using stdlib + var zip_file = std.fs.cwd().openFile(model_zip, .{}) catch return error.UnzipFailed; + defer zip_file.close(); - // Copy to working directory using stdlib - std.fs.cwd().deleteTree(model_dir) catch {}; - std.fs.cwd().makePath(model_dir) catch {}; - var cached_dir = std.fs.cwd().openDir(cached_model_dir, .{ .iterate = true }) catch return error.CopyFailed; - defer cached_dir.close(); - var target_dir = std.fs.cwd().openDir(model_dir, .{}) catch return error.CopyFailed; - defer target_dir.close(); + var cache_dir_handle = std.fs.cwd().openDir(cache_dir, .{}) catch return error.UnzipFailed; + defer cache_dir_handle.close(); - var iter = cached_dir.iterate(); - while (iter.next() catch return error.CopyFailed) |entry| { - switch (entry.kind) { - .file => { - cached_dir.copyFile(entry.name, target_dir, entry.name, .{}) catch return error.CopyFailed; - }, - .directory => { - target_dir.makeDir(entry.name) catch {}; - var sub_cached = cached_dir.openDir(entry.name, .{ .iterate = true }) catch return error.CopyFailed; - defer sub_cached.close(); - var sub_target = target_dir.openDir(entry.name, .{}) catch return error.CopyFailed; - defer sub_target.close(); + var zip_file_buffer: [4096]u8 = undefined; + var zip_file_reader = zip_file.reader(&zip_file_buffer); - var sub_iter = sub_cached.iterate(); - while (sub_iter.next() catch return error.CopyFailed) |sub_entry| { - if (sub_entry.kind == .file) { - sub_cached.copyFile(sub_entry.name, sub_target, sub_entry.name, .{}) catch return error.CopyFailed; - } - } - }, - else => {}, - } - } + std.zip.extract(cache_dir_handle, &zip_file_reader, .{}) catch return error.UnzipFailed; step.result_cached = false; } }; - -const ModelInstallStep = struct { - step: std.Build.Step, - builder: *std.Build, - model_step: *std.Build.Step, - - pub fn create(builder: *std.Build, model_step: *std.Build.Step) *ModelInstallStep { - const self = builder.allocator.create(ModelInstallStep) catch @panic("OOM"); - self.* = .{ - .step = std.Build.Step.init(.{ - .id = .custom, - .name = "install vosk-model-small-en-us-0.15/", - .owner = builder, - .makeFn = make, - }), - .builder = builder, - .model_step = model_step, - }; - self.step.dependOn(model_step); - return self; - } - - fn make(step: *std.Build.Step, options: std.Build.Step.MakeOptions) anyerror!void { - _ = options; - const self: *ModelInstallStep = @fieldParentPtr("step", step); - - const install_dir = self.builder.getInstallPath(.bin, "vosk-model-small-en-us-0.15"); - - // Inherit cache status from model step - step.result_cached = self.model_step.result_cached; - - if (step.result_cached) return; - - // Copy model to install directory - std.fs.cwd().makePath(std.fs.path.dirname(install_dir).?) catch {}; - std.fs.cwd().deleteTree(install_dir) catch {}; - - const copy_result = std.process.Child.run(.{ - .allocator = self.builder.allocator, - .argv = &.{ "cp", "-r", "vosk-model-small-en-us-0.15", install_dir }, - }) catch return error.CopyFailed; - if (copy_result.term.Exited != 0) return error.CopyFailed; - } -};