From 66e8ae3495734d06f35d3e66df4037bf7a29cdc7 Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Wed, 17 Sep 2025 15:17:42 -0700 Subject: [PATCH] holy crap it builds --- LICENSE | 21 +++++++++ build.zig | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..506dadd --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Emil Lerch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/build.zig b/build.zig index af96aa6..07fba4c 100644 --- a/build.zig +++ b/build.zig @@ -22,8 +22,57 @@ pub fn build(b: *std.Build) void { // in this directory. // We need to use curl for this as the domain doesn't work with zig TLS - const model_step = DownloadStep("https://www.link.cs.cmu.edu/link/ftp-site/link-grammar/link-4.1b/unix/link-4.1b.tar.gz").create(b); + const download_link_step = DownloadStep("https://www.link.cs.cmu.edu/link/ftp-site/link-grammar/link-4.1b/unix/link-4.1b.tar.gz").create(b); + const upstream = download_link_step.dependency(b, .{}); + + const lib = b.addLibrary(.{ + .name = "link", + .linkage = .static, + .root_module = b.createModule(.{ + .target = target, + .optimize = optimize, + .link_libc = true, + }), + }); + + lib.addIncludePath(upstream.path("include")); + + lib.addCSourceFiles(.{ + .root = upstream.path("src"), + .files = &.{ + "analyze-linkage.c", + "and.c", + "api-example.c", + "api.c", + "build-disjuncts.c", + "command-line.c", + "constituents.c", + "count.c", + "error.c", + "extract-links.c", + "fast-match.c", + "idiom.c", + "linkset.c", + "massage.c", + "parse.c", + "post-process.c", + "pp_knowledge.c", + "pp_lexer.c", + "pp_linkset.c", + "preparation.c", + "print-util.c", + "print.c", + "prune.c", + "read-dict.c", + "resources.c", + "string-set.c", + "tokenize.c", + "utilities.c", + "word-file.c", + "www-parse.c", + }, + }); // This creates a module, which represents a collection of source files alongside // some compilation options, such as optimization mode and linked system libraries. // Zig modules are the preferred way of making Zig code available to consumers. @@ -43,6 +92,8 @@ pub fn build(b: *std.Build) void { // which requires us to specify a target. .target = target, }); + mod.linkLibrary(lib); + mod.addIncludePath(upstream.path("include")); // Here we define an executable. An executable needs to have a root module // which needs to expose a `main` function. While we could add a main function @@ -86,7 +137,7 @@ pub fn build(b: *std.Build) void { }), }); - exe.step.dependOn(&model_step.step); + exe.step.dependOn(&download_link_step.step); // This declares intent for the executable to be installed into the // install prefix when running `zig build` (i.e. when executing the default // step). By default the install prefix is `zig-out/` but can be overridden @@ -125,7 +176,7 @@ pub fn build(b: *std.Build) void { const mod_tests = b.addTest(.{ .root_module = mod, }); - exe.step.dependOn(&model_step.step); + mod_tests.step.dependOn(&download_link_step.step); // A run step that will run the test executable. const run_mod_tests = b.addRunArtifact(mod_tests); @@ -174,6 +225,23 @@ fn DownloadStep(comptime link: []const u8) type { const Self = @This(); + const Dependency = struct { + build_root: []const u8, + build: *std.Build, + download: *Self, + + pub fn path(self: Dependency, sub_path: []const u8) std.Build.LazyPath { + const cache_path = (self.download.getOutputPath() catch @panic("OOM")).path; + const full_path = std.fs.path.join(self.build.allocator, &.{ cache_path, sub_path }) catch @panic("OOM"); + return .{ + .src_path = .{ + .owner = self.build, + .sub_path = full_path, + }, + }; + } + }; + fn fileName(uri: std.Uri) []const u8 { const path = switch (uri.path) { .raw => |r| r, @@ -205,7 +273,7 @@ fn DownloadStep(comptime link: []const u8) type { return self; } - pub fn getOutputPath(self: *Self) std.Build.LazyPath { + pub fn getOutputPath(self: *Self) !struct { path: []const u8, hash: u64 } { var hasher = std.hash.Wyhash.init(0); hasher.update(download_link); const cache_hash = hasher.final(); @@ -213,7 +281,10 @@ fn DownloadStep(comptime link: []const u8) type { var cache_dir_buf: [std.fs.max_path_bytes]u8 = undefined; const cache_dir = std.fmt.bufPrint(&cache_dir_buf, "{s}/o/{x}/{s}", .{ self.builder.cache_root.path.?, cache_hash, fileNameNoExtension() }) catch @panic("path too long"); - return .{ .cwd_relative = self.builder.allocator.dupe(u8, cache_dir) catch @panic("OOM") }; + return .{ + .path = try self.builder.allocator.dupe(u8, cache_dir), + .hash = cache_hash, + }; } fn make(step: *std.Build.Step, options: std.Build.Step.MakeOptions) anyerror!void { @@ -277,9 +348,50 @@ fn DownloadStep(comptime link: []const u8) type { step.result_cached = false; }, .targz => { - @compileError("tar.gz extraction not yet implemented"); + var archive_file = std.fs.cwd().openFile(archive, .{}) catch return error.ExtractFailed; + defer archive_file.close(); + + var buf: [4096]u8 = undefined; + var file_reader = archive_file.reader(&buf); + const reader = &file_reader.interface; + + var cache_dir_handle = std.fs.cwd().openDir(cache_dir, .{}) catch return error.ExtractFailed; + defer cache_dir_handle.close(); + + var gz_buf: [std.compress.flate.max_window_len]u8 = undefined; + var decompress = std.compress.flate.Decompress.init(reader, .gzip, &gz_buf); + std.tar.pipeToFileSystem( + cache_dir_handle, + &decompress.reader, + .{ .mode_mode = .ignore }, + ) catch return error.ExtractFailed; }, } } + + pub fn dependency( + self: *Self, + b: *std.Build, + args: anytype, + ) *Dependency { + _ = args; + const output = self.getOutputPath() catch @panic("cannot get output path"); + const dep = b.allocator.create(Dependency) catch @panic("OOM"); + + dep.* = .{ + .download = self, + .build = b, + .build_root = output.path, + }; + return dep; + } + const UserValue = union(enum) { + flag: void, + scalar: []const u8, + list: std.array_list.Managed([]const u8), + map: std.StringHashMap(*const UserValue), + lazy_path: std.Build.LazyPath, + lazy_path_list: std.array_list.Managed(std.Build.LazyPath), + }; }; }