From 6ee3b798841256c8a08a50462e3ed8f140492c2d Mon Sep 17 00:00:00 2001 From: rimuspp <19101das@gmail.com> Date: Tue, 23 Jan 2024 13:51:10 -0500 Subject: [PATCH 1/5] Updated the api for the Build system, Added a step for the compilation of views --- build.zig | 133 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 81 insertions(+), 52 deletions(-) diff --git a/build.zig b/build.zig index 8d70dd9..3c21cb3 100644 --- a/build.zig +++ b/build.zig @@ -3,6 +3,8 @@ const std = @import("std"); pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); + const template_path = b.option([]const u8, "zmpl_templates_path", "Path to templates") orelse "src/app/views/"; + const manifest : []const u8 = b.pathJoin(&.{ template_path, "zmpl.manifest.zig" }); const lib = b.addStaticLibrary(.{ .name = "jetzig", @@ -22,38 +24,22 @@ pub fn build(b: *std.Build) !void { b.installArtifact(exe); - const jetzig_module = b.createModule(.{ .source_file = .{ .path = "src/jetzig.zig" } }); - exe.addModule("jetzig", jetzig_module); - lib.addModule("jetzig", jetzig_module); - try b.modules.put("jetzig", jetzig_module); + const jetzig_module = b.addModule("jetzig",.{ .root_source_file = .{ .path = "src/jetzig.zig" } }); + exe.root_module.addImport("jetzig", jetzig_module); + lib.root_module.addImport("jetzig", jetzig_module); const zmpl_dep = b.dependency( "zmpl", - .{ - .target = target, - .optimize = optimize, - .zmpl_templates_path = @as([]const u8, "src/app/views/"), - .zmpl_manifest_path = @as([]const u8, "src/app/views/zmpl.manifest.zig"), - }, + .{ .target = target, .optimize = optimize, .zmpl_templates_path = template_path, .zmpl_manifest_path = manifest}, ); - lib.addModule("zmpl", zmpl_dep.module("zmpl")); - exe.addModule("zmpl", zmpl_dep.module("zmpl")); - try b.modules.put("zmpl", zmpl_dep.module("zmpl")); - try jetzig_module.dependencies.put("zmpl", zmpl_dep.module("zmpl")); + lib.root_module.addImport("zmpl", zmpl_dep.module("zmpl")); + exe.root_module.addImport("zmpl", zmpl_dep.module("zmpl")); + jetzig_module.addImport("zmpl", zmpl_dep.module("zmpl")); - var dir = std.fs.cwd(); - var views_dir = try dir.makeOpenPath("src/app/views", .{}); - var file = try views_dir.createFile("routes.zig", .{ .truncate = true }); - try file.writeAll("pub const routes = .{\n"); - const views = try findViews(b.allocator); - for (views.items) |view| { - try file.writeAll(try std.fmt.allocPrint(b.allocator, " @import(\"{s}\"),\n", .{view.path})); - std.debug.print("[jetzig] Imported view: {s}\n", .{view.path}); - } - - try file.writeAll("};\n"); - file.close(); + // This is the way to make it look nice in the zig build script + // If we would do it the other way around, we would have to do b.dependency("jetzig",.{}).builder.dependency("zmpl",.{}).module("zmpl"); + b.modules.put("zmpl", zmpl_dep.module("zmpl")) catch @panic("Out of memory"); const run_cmd = b.addRunArtifact(exe); @@ -69,7 +55,7 @@ pub fn build(b: *std.Build) !void { .optimize = optimize, }); - main_tests.addModule("zmpl", zmpl_dep.module("zmpl")); + main_tests.root_module.addImport("zmpl", zmpl_dep.module("zmpl")); const run_main_tests = b.addRunArtifact(main_tests); const test_step = b.step("test", "Run library tests"); @@ -81,30 +67,73 @@ const ViewItem = struct { name: []const u8, }; -fn findViews(allocator: std.mem.Allocator) !std.ArrayList(*ViewItem) { - var array = std.ArrayList(*ViewItem).init(allocator); - const dir = try std.fs.cwd().openIterableDir("src/app/views", .{}); - var walker = try dir.walk(allocator); - defer walker.deinit(); - while (try walker.next()) |entry| { - if (entry.kind != .file) continue; - const extension = std.fs.path.extension(entry.path); - const basename = std.fs.path.basename(entry.path); - if (std.mem.eql(u8, basename, "routes.zig")) continue; - if (std.mem.eql(u8, basename, "zmpl.manifest.zig")) continue; - if (std.mem.startsWith(u8, basename, ".")) continue; - if (!std.mem.eql(u8, extension, ".zig")) continue; +pub const CompileViewsStepOptions = struct { + template_path: []const u8 = "src/app/views/", + max_rss: usize = 0, +}; +pub const CompileViewsStep = struct { + step: std.Build.Step, + template_path: []const u8, - var sanitized_array = std.ArrayList(u8).init(allocator); - for (entry.path) |char| { - if (std.mem.indexOfAny(u8, &[_]u8{char}, "abcdefghijklmnopqrstuvwxyz")) |_| try sanitized_array.append(char); - } - const ptr = try allocator.create(ViewItem); - ptr.* = .{ - .path = try allocator.dupe(u8, entry.path), - .name = try allocator.dupe(u8, sanitized_array.items), - }; - try array.append(ptr); + fn make(step: *std.Build.Step, prog_node: *std.Progress.Node) !void { + const self = @fieldParentPtr(CompileViewsStep, "step", step); + try compileViews(step.owner, self.template_path); + prog_node.completeOne(); } - return array; -} + + pub fn create(owner: *std.Build, options: CompileViewsStepOptions) *CompileViewsStep { + const step = std.Build.Step.init(.{ + .id = std.Build.Step.Id.custom, + .name = "Compile views", + .owner = owner, + .max_rss = options.max_rss, + .makeFn = &make, + }); + const compile_step_view = owner.allocator.create(CompileViewsStep) catch @panic("Out of memory"); + compile_step_view.* = .{ .step = step, .template_path = options.template_path, }; + return compile_step_view; + } + + fn findViews(allocator: std.mem.Allocator, template_path: []const u8) !std.ArrayList(*ViewItem) { + var array = std.ArrayList(*ViewItem).init(allocator); + const dir = try std.fs.cwd().openDir(template_path, .{ .iterate = true }); + var walker = try dir.walk(allocator); + defer walker.deinit(); + while (try walker.next()) |entry| { + if (entry.kind != .file) continue; + const extension = std.fs.path.extension(entry.path); + const basename = std.fs.path.basename(entry.path); + if (std.mem.eql(u8, basename, "routes.zig")) continue; + if (std.mem.eql(u8, basename, "zmpl.manifest.zig")) continue; + if (std.mem.startsWith(u8, basename, ".")) continue; + if (!std.mem.eql(u8, extension, ".zig")) continue; + + var sanitized_array = std.ArrayList(u8).init(allocator); + for (entry.path) |char| { + if (std.mem.indexOfAny(u8, &[_]u8{char}, "abcdefghijklmnopqrstuvwxyz")) |_| try sanitized_array.append(char); + } + const ptr = try allocator.create(ViewItem); + ptr.* = .{ + .path = try allocator.dupe(u8, entry.path), + .name = try allocator.dupe(u8, sanitized_array.items), + }; + try array.append(ptr); + } + return array; + } + + fn compileViews(b: *std.Build, template_path: []const u8) !void { + var dir = b.build_root.handle; + var views_dir = try dir.makeOpenPath(template_path, .{}); + var file = try views_dir.createFile("routes.zig", .{ .truncate = true }); + try file.writeAll("pub const routes = .{\n"); + const views = try findViews(b.allocator, template_path); + for (views.items) |view| { + try file.writeAll(try std.fmt.allocPrint(b.allocator, " @import(\"{s}\"),\n", .{view.path})); + std.debug.print("[jetzig] Imported view: {s}\n", .{view.path}); + } + + try file.writeAll("};\n"); + file.close(); + } +}; From 885037646eeed778954bd1d9268984703de681e7 Mon Sep 17 00:00:00 2001 From: rimuspp <19101das@gmail.com> Date: Tue, 23 Jan 2024 13:52:51 -0500 Subject: [PATCH 2/5] Init with the new step thing --- src/init/build.zig | 90 +++++++++++++++++++++++++++++++----------- src/init/build.zig.zon | 4 -- 2 files changed, 66 insertions(+), 28 deletions(-) diff --git a/src/init/build.zig b/src/init/build.zig index f0f5e6d..d64e70f 100644 --- a/src/init/build.zig +++ b/src/init/build.zig @@ -1,54 +1,96 @@ const std = @import("std"); - -pub fn build(b: *std.Build) !void { +const jetzig_build = @import("jetzig"); +// Although this function looks imperative, note that its job is to +// declaratively construct a build graph that will be executed by an external +// runner. +pub fn build(b: *std.Build) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. const target = b.standardTargetOptions(.{}); + // Standard optimization options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not + // set a preferred release mode, allowing the user to decide how to optimize. const optimize = b.standardOptimizeOption(.{}); + const jetzig_dep = b.dependency("jetzig", .{ .optimize = optimize, .target = target }); + const compile_view_step = jetzig_build.CompileViewsStep.create(b, .{ .template_path = "src/app/views/" }); - const exe = b.addExecutable(.{ + const lib = b.addStaticLibrary(.{ .name = "%%project_name%%", - .root_source_file = .{ .path = "src/main.zig" }, + // In this case the main source file is merely a path, however, in more + // complicated build scripts, this could be a generated file. + .root_source_file = .{ .path = "src/root.zig" }, .target = target, .optimize = optimize, }); + // This declares intent for the library to be installed into the standard + // location when the user invokes the "install" step (the default step when + // running `zig build`). + b.installArtifact(lib); + + const exe = b.addExecutable(.{ + .name = "jetzig-demo", + .root_source_file = .{ .path = "src/main.zig" }, + .target = target, + .optimize = optimize, + }); + exe.root_module.addImport("jetzig", jetzig_dep.module("jetzig")); + + exe.root_module.addImport("zmpl", jetzig_dep.module("zmpl")); + // This declares intent for the executable to be installed into the + // standard location when the user invokes the "install" step (the default + // step when running `zig build`). b.installArtifact(exe); + exe.step.dependOn(&compile_view_step.step); - const jetzig = b.dependency("jetzig", .{ .optimize = optimize, .target = target }); - exe.addModule("jetzig", jetzig.module("jetzig")); - try b.modules.put("jetzig", jetzig.module("jetzig")); - - const zmpl_dep = b.dependency( - "zmpl", - .{ - .target = target, - .optimize = optimize, - .zmpl_templates_path = @as([]const u8, "src/app/views/"), - .zmpl_manifest_path = @as([]const u8, "src/app/views/zmpl.manifest.zig"), - }, - ); - - exe.addModule("zmpl", zmpl_dep.module("zmpl")); - try b.modules.put("zmpl", zmpl_dep.module("zmpl")); - + // This *creates* a Run step in the build graph, to be executed when another + // step is evaluated that depends on it. The next line below will establish + // such a dependency. const run_cmd = b.addRunArtifact(exe); + + // By making the run step depend on the install step, it will be run from the + // installation directory rather than directly from within the cache directory. + // This is not necessary, however, if the application depends on other installed + // files, this ensures they will be present and in the expected location. run_cmd.step.dependOn(b.getInstallStep()); + // This allows the user to pass arguments to the application in the build + // command itself, like this: `zig build run -- arg1 arg2 etc` if (b.args) |args| { run_cmd.addArgs(args); } + // This creates a build step. It will be visible in the `zig build --help` menu, + // and can be selected like this: `zig build run` + // This will evaluate the `run` step rather than the default, which is "install". const run_step = b.step("run", "Run the app"); run_step.dependOn(&run_cmd.step); - const unit_tests = b.addTest(.{ + // Creates a step for unit testing. This only builds the test executable + // but does not run it. + const lib_unit_tests = b.addTest(.{ + .root_source_file = .{ .path = "src/root.zig" }, + .target = target, + .optimize = optimize, + }); + + const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); + + const exe_unit_tests = b.addTest(.{ .root_source_file = .{ .path = "src/main.zig" }, .target = target, .optimize = optimize, }); - const run_unit_tests = b.addRunArtifact(unit_tests); + const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + // Similar to creating the run step earlier, this exposes a `test` step to + // the `zig build --help` menu, providing a way for the user to request + // running the unit tests. const test_step = b.step("test", "Run unit tests"); - test_step.dependOn(&run_unit_tests.step); + test_step.dependOn(&run_lib_unit_tests.step); + test_step.dependOn(&run_exe_unit_tests.step); } diff --git a/src/init/build.zig.zon b/src/init/build.zig.zon index 6dfea91..ae51345 100644 --- a/src/init/build.zig.zon +++ b/src/init/build.zig.zon @@ -5,10 +5,6 @@ .jetzig = .{ .url = "https://github.com/jetzig-framework/jetzig/archive/refs/tags/dev.tar.gz", }, - .zmpl = .{ - .url = "https://github.com/jetzig-framework/zmpl/archive/refs/tags/0.0.1.tar.gz", - .hash = "12204256376f262a58935d66a2a0b41ac0447299b7e63a4c6ff160ddcef6572cd3c7", - }, }, .paths = .{ From 7e529e2c55531fadf212555eb71e16d7fe87a524 Mon Sep 17 00:00:00 2001 From: rimuspp <19101das@gmail.com> Date: Tue, 23 Jan 2024 13:53:28 -0500 Subject: [PATCH 3/5] Incomplete method set, Decided to just throw an unsupported error --- src/jetzig/http/Request.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/jetzig/http/Request.zig b/src/jetzig/http/Request.zig index 781c0fb..b18accf 100644 --- a/src/jetzig/http/Request.zig +++ b/src/jetzig/http/Request.zig @@ -35,6 +35,7 @@ pub fn init( .CONNECT => Method.CONNECT, .OPTIONS => Method.OPTIONS, .TRACE => Method.TRACE, + _ => return error.jetzig_unsupported_http_method, }; var it = std.mem.splitScalar(u8, response.request.target, '/'); From 454208d6b599c3c6d8724b21796ea58ff53e363c Mon Sep 17 00:00:00 2001 From: rimuspp <19101das@gmail.com> Date: Tue, 23 Jan 2024 13:54:27 -0500 Subject: [PATCH 4/5] Change in how the std.http.Server works, do() -> send(), init doesn't take an allocator anymore --- src/jetzig/http/Server.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/jetzig/http/Server.zig b/src/jetzig/http/Server.zig index 635c95d..ec52d75 100644 --- a/src/jetzig/http/Server.zig +++ b/src/jetzig/http/Server.zig @@ -30,7 +30,7 @@ pub fn init( routes: []jetzig.views.Route, templates: []jetzig.TemplateFn, ) Self { - const server = std.http.Server.init(allocator, .{ .reuse_address = true }); + const server = std.http.Server.init( .{ .reuse_address = true }); return .{ .server = server, @@ -106,7 +106,7 @@ fn processNextRequest(self: *Self, response: *std.http.Server.Response) !void { inline else => |status_code| @field(std.http.Status, @tagName(status_code)), }; - try response.do(); + try response.send(); try response.writeAll(result.value.content); try response.finish(); @@ -307,7 +307,7 @@ fn matchStaticResource(self: *Self, request: *jetzig.http.Request) !?StaticResou if (request.path.len < 2) return null; if (request.method != .GET) return null; - var iterable_dir = std.fs.cwd().openIterableDir("public", .{}) catch |err| { + var iterable_dir = std.fs.cwd().openDir("public", .{.iterate = true}) catch |err| { switch (err) { error.FileNotFound => return null, else => return err, @@ -319,7 +319,7 @@ fn matchStaticResource(self: *Self, request: *jetzig.http.Request) !?StaticResou if (std.mem.eql(u8, file.path, request.path[1..])) { return .{ - .content = try iterable_dir.dir.readFileAlloc( + .content = try iterable_dir.readFileAlloc( request.allocator, file.path, jetzig.config.max_bytes_static_content, From 81d63d6a708275fdcd324e498bf276635eeecfba Mon Sep 17 00:00:00 2001 From: rimuspp <19101das@gmail.com> Date: Sat, 27 Jan 2024 12:18:49 -0500 Subject: [PATCH 5/5] Updated zimpl to latest --- build.zig.zon | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 4135458..6c1cab9 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -21,9 +21,8 @@ // `hash`, otherwise you are communicating that you expect to find the old hash at // the new URL. // - .url = "https://github.com/jetzig-framework/zmpl/archive/refs/tags/0.0.1.tar.gz", - .hash = "12204256376f262a58935d66a2a0b41ac0447299b7e63a4c6ff160ddcef6572cd3c7", - + .url = "https://github.com/jetzig-framework/zmpl/archive/abe0d2b27f22b449eb021bcf8f5722fa38229c6a.tar.gz", + .hash = "122007e90e8db4781ef886cc6c11b4fba6d9b0cf79d0df0e616894c7f72ac1f5f4cb", // This is computed from the file contents of the directory of files that is // obtained after fetching `url` and applying the inclusion rules given by // `paths`.