diff --git a/build.zig b/build.zig index 1cb3e55..f54d3fe 100644 --- a/build.zig +++ b/build.zig @@ -5,6 +5,9 @@ pub const GenerateMimeTypes = @import("src/GenerateMimeTypes.zig"); const zmpl_build = @import("zmpl"); const Environment = enum { development, testing, production }; +const builtin = @import("builtin"); + +const use_llvm_default = builtin.os.tag != .linux; pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); @@ -36,6 +39,7 @@ pub fn build(b: *std.Build) !void { .{ .target = target, .optimize = optimize, + .use_llvm = b.option(bool, "use_llvm", "Use LLVM") orelse use_llvm_default, .zmpl_templates_paths = templates_paths, .zmpl_auto_build = false, .zmpl_markdown_fragments = try generateMarkdownFragments(b), @@ -46,6 +50,11 @@ pub fn build(b: *std.Build) !void { }, ); + const zmpl_steps = zmpl_dep.builder.top_level_steps; + const zmpl_compile_step = zmpl_steps.get("compile").?; + const compile_step = b.step("compile", "Compile Zmpl templates"); + compile_step.dependOn(&zmpl_compile_step.step); + const zmpl_module = zmpl_dep.module("zmpl"); const jetkv_dep = b.dependency("jetkv", .{ .target = target, .optimize = optimize }); @@ -58,18 +67,15 @@ pub fn build(b: *std.Build) !void { const jetcommon_dep = b.dependency("jetcommon", .{ .target = target, .optimize = optimize }); const zmd_dep = b.dependency("zmd", .{ .target = target, .optimize = optimize }); const httpz_dep = b.dependency("httpz", .{ .target = target, .optimize = optimize }); - const pg_dep = b.dependency("pg", .{ .target = target, .optimize = optimize }); // 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"); b.modules.put("zmd", zmd_dep.module("zmd")) catch @panic("Out of memory"); - b.modules.put("pg", pg_dep.module("pg")) catch @panic("Out of memory"); b.modules.put("jetquery", jetquery_dep.module("jetquery")) catch @panic("Out of memory"); b.modules.put("jetcommon", jetcommon_dep.module("jetcommon")) catch @panic("Out of memory"); b.modules.put("jetquery_migrate", jetquery_dep.module("jetquery_migrate")) catch @panic("Out of memory"); - jetquery_dep.module("jetquery").addImport("pg", pg_dep.module("pg")); const smtp_client_dep = b.dependency("smtp_client", .{ .target = target, @@ -132,6 +138,8 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn const target = exe.root_module.resolved_target orelse @panic("Unable to detect compile target."); const optimize = exe.root_module.optimize orelse .Debug; + exe.use_llvm = exe.use_llvm orelse use_llvm_default; + if (optimize != .Debug) exe.linkLibC(); const environment = b.option( @@ -172,7 +180,6 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn const jetzig_module = jetzig_dep.module("jetzig"); const zmpl_module = jetzig_dep.module("zmpl"); const zmd_module = jetzig_dep.module("zmd"); - const pg_module = jetzig_dep.module("pg"); const jetquery_module = jetzig_dep.module("jetquery"); const jetcommon_module = jetzig_dep.module("jetcommon"); const jetquery_migrate_module = jetzig_dep.module("jetquery_migrate"); @@ -187,7 +194,6 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn exe.root_module.addImport("jetzig", jetzig_module); exe.root_module.addImport("zmpl", zmpl_module); exe.root_module.addImport("zmd", zmd_module); - exe.root_module.addImport("pg", pg_module); if (b.option(bool, "jetzig_runner", "Used internally by `jetzig server` command.")) |jetzig_runner| { if (jetzig_runner) { @@ -197,7 +203,10 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn } } - const root_path = b.build_root.path orelse try std.fs.cwd().realpathAlloc(b.allocator, "."); + const root_path = if (b.build_root.path) |build_root_path| + try std.fs.path.join(b.allocator, &.{ build_root_path, "." }) + else + try std.fs.cwd().realpathAlloc(b.allocator, "."); const templates_path: []const u8 = try std.fs.path.join( b.allocator, &[_][]const u8{ root_path, "src", "app" }, @@ -220,6 +229,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn .root_source_file = jetzig_dep.path("src/routes_file.zig"), .target = target, .optimize = optimize, + .use_llvm = exe.use_llvm, }); exe_routes_file.root_module.addImport("jetzig", jetzig_module); @@ -247,6 +257,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn .root_source_file = jetzig_dep.path("src/compile_static_routes.zig"), .target = target, .optimize = optimize, + .use_llvm = exe.use_llvm, }); const main_module = b.createModule(.{ .root_source_file = b.path("src/main.zig") }); @@ -355,6 +366,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn .root_source_file = jetzig_dep.path("src/commands/routes.zig"), .target = target, .optimize = optimize, + .use_llvm = exe.use_llvm, }); const auth_user_create_step = b.step("jetzig:auth:user:create", "List all routes in your app"); @@ -363,6 +375,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn .root_source_file = jetzig_dep.path("src/commands/auth.zig"), .target = target, .optimize = optimize, + .use_llvm = exe.use_llvm, }); exe_auth.root_module.addImport("jetquery", jetquery_module); exe_auth.root_module.addImport("jetzig", jetzig_module); @@ -384,6 +397,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn .root_source_file = jetzig_dep.path("src/commands/database.zig"), .target = target, .optimize = optimize, + .use_llvm = exe.use_llvm, }); exe_database.root_module.addImport("jetquery", jetquery_module); exe_database.root_module.addImport("jetzig", jetzig_module); @@ -395,7 +409,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn registerDatabaseSteps(b, exe_database); const option_db_exe = b.option(bool, "database_exe", "option to install 'database' executable default is false") orelse false; - if(option_db_exe) b.installArtifact(exe_database); + if (option_db_exe) b.installArtifact(exe_database); exe_routes.root_module.addImport("jetzig", jetzig_module); exe_routes.root_module.addImport("routes", routes_module); diff --git a/build.zig.zon b/build.zig.zon index 8f2467a..2fb196d 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,42 +1,39 @@ .{ - .name = "jetzig", + .name = .jetzig, .version = "0.0.0", + .fingerprint = 0x93ad8bfa2d209022, .dependencies = .{ - .zmd = .{ - .url = "https://github.com/jetzig-framework/zmd/archive/d6c8aa9a9cde99674ccb096d8f94ed09cba8dab.tar.gz", - .hash = "1220d0e8734628fd910a73146e804d10a3269e3e7d065de6bb0e3e88d5ba234eb163", + .jetquery = .{ + .url = "https://github.com/jetzig-framework/jetquery/archive/795136c43f6d5bd216c136748bafd22892277725.tar.gz", + .hash = "jetquery-0.0.0-TNf3zqZFBgDS2CaIG4DvLwaiGeAxxbfAi-DnEnSKx-mf", }, .jetkv = .{ .url = "https://github.com/jetzig-framework/jetkv/archive/9d754e552e7569239a900ed9e0f313a0554ed2d3.tar.gz", .hash = "122013f8596bc615990fd7771c833cab4d2959ecac8d05c4f6c973aa46624e43afea", }, - .args = .{ - .url = "https://github.com/ikskuh/zig-args/archive/968258dc1b1230493d8f1677097c832a3d7e0bd8.tar.gz", - .hash = "1220bdedf1a993d852d8aebcd63922a8fb163fac37b9c6ff72d187b2847a4a3a4248", - }, .jetcommon = .{ - .url = "https://github.com/jetzig-framework/jetcommon/archive/5be57d534b3d469f5570cd4b373b8d61032b1b8b.tar.gz", - .hash = "122079c6ceb28fa93163c2f95e2f175bb8f93f3075fa34af63045671ab7dd824e756", + .url = "https://github.com/jetzig-framework/jetcommon/archive/fb4edc13759d87bfcd9b1f5fcefdf93f8c9c62dd.tar.gz", + .hash = "jetcommon-0.1.0-jPY_DS1HAAAP8xp5HSWB_ZY7m9JEYUmm8adQFrse0lwB", }, - .pg = .{ - .url = "https://github.com/karlseguin/pg.zig/archive/0110cfdf387403a5a326115b5184861c4604d711.tar.gz", - .hash = "12205019ce2bc2e08c76352ea37a14600d412e5e0ecdd7ddd27b4e83a62f37d8ba94", + .zmpl = .{ + .url = "https://github.com/jetzig-framework/zmpl/archive/3ec11289fdee2e0c70975cb5dd85d3041d723912.tar.gz", + .hash = "zmpl-0.0.1-SYFGBuZoAwAMuvHNkO_1BbutpWhg7CdSgYd8t4OaaZeR", + }, + .zmd = .{ + .url = "https://github.com/jetzig-framework/zmd/archive/d6c8aa9a9cde99674ccb096d8f94ed09cba8dab.tar.gz", + .hash = "1220d0e8734628fd910a73146e804d10a3269e3e7d065de6bb0e3e88d5ba234eb163", }, .httpz = .{ - .url = "https://github.com/karlseguin/http.zig/archive/f16b296a2772be97e48a57259c004aa6584a02c6.tar.gz", - .hash = "1220e524a72c18aa2585f326902066fda544c5df0cf6618eea885b04041204dc5d7f", + .url = "https://github.com/karlseguin/http.zig/archive/eced2d8c37f921a2dfc8ab639964cdc9b505a888.tar.gz", + .hash = "httpz-0.0.0-AAAAAL6qBgAeyws7FZLTv3e_Sbsjg4PKfB1Fg6AOHctf", }, .smtp_client = .{ .url = "https://github.com/karlseguin/smtp_client.zig/archive/5163c66cc42cdd93176a6b1cad45f3db3a291a6a.tar.gz", - .hash = "1220a7807b5161550cb0cba772689c4872bfeee8305a26c3cd0e12a8ccde1d546910", + .hash = "smtp_client-0.0.1-AAAAAIJkAQCngHtRYVUMsMuncmicSHK_7ugwWibDzQ4S", }, - .jetquery = .{ - .url = "https://github.com/jetzig-framework/jetquery/archive/55ebed84ad80d3d6c6e026c06e37b7de22168e7b.tar.gz", - .hash = "1220715b9064087cdc114e1a6a087e56d242cea56bc5759b740f4c2d5c1765822add", - }, - .zmpl = .{ - .url = "https://github.com/jetzig-framework/zmpl/archive/04d9fa7c3f6369790aac1fc64625e91222072ebc.tar.gz", - .hash = "122078b3cd8ac2be7ba7b122312de7abee6de6be905287e2b3d80b09e5481a39e70f", + .args = .{ + .url = "https://github.com/bobf/zig-args/archive/88cbade9a517a4014824f8f53f3c48c8a0b2ffe1.tar.gz", + .hash = "zig_args-0.0.0-jqtN6P_NAAC97fGpk9hS2K681jkiqPsWP6w3ucb_ctGH", }, }, diff --git a/cli/build.zig.zon b/cli/build.zig.zon index e8fbdc8..75d1776 100644 --- a/cli/build.zig.zon +++ b/cli/build.zig.zon @@ -1,16 +1,17 @@ .{ - .name = "jetzig-cli", + .name = .jetzig_cli, + .fingerprint = 0x73894a3e0616c96a, .version = "0.0.0", .minimum_zig_version = "0.12.0", .dependencies = .{ .args = .{ - .url = "https://github.com/ikskuh/zig-args/archive/968258dc1b1230493d8f1677097c832a3d7e0bd8.tar.gz", - .hash = "1220bdedf1a993d852d8aebcd63922a8fb163fac37b9c6ff72d187b2847a4a3a4248", + .url = "https://github.com/bobf/zig-args/archive/88cbade9a517a4014824f8f53f3c48c8a0b2ffe1.tar.gz", + .hash = "zig_args-0.0.0-jqtN6P_NAAC97fGpk9hS2K681jkiqPsWP6w3ucb_ctGH", }, .jetquery = .{ - .url = "https://github.com/jetzig-framework/jetquery/archive/55ebed84ad80d3d6c6e026c06e37b7de22168e7b.tar.gz", - .hash = "1220715b9064087cdc114e1a6a087e56d242cea56bc5759b740f4c2d5c1765822add", + .url = "https://github.com/jetzig-framework/jetquery/archive/795136c43f6d5bd216c136748bafd22892277725.tar.gz", + .hash = "jetquery-0.0.0-TNf3zqZFBgDS2CaIG4DvLwaiGeAxxbfAi-DnEnSKx-mf", }, }, .paths = .{ diff --git a/cli/commands/bundle.zig b/cli/commands/bundle.zig index cf4f0af..86d44cf 100644 --- a/cli/commands/bundle.zig +++ b/cli/commands/bundle.zig @@ -149,13 +149,13 @@ pub fn run( const tmpdir_real_path = try tmpdir.realpathAlloc(allocator, "."); defer allocator.free(tmpdir_real_path); - try util.runCommandInDir(allocator, tar_argv.items, .{ .path = tmpdir_real_path }); + try util.runCommandInDir(allocator, tar_argv.items, .{ .path = tmpdir_real_path }, .{}); switch (builtin.os.tag) { .windows => {}, else => std.debug.print("Bundle `bundle.tar.gz` generated successfully.", .{}), } - util.printSuccess(); + util.printSuccess(null); } fn locateMarkdownFiles(allocator: std.mem.Allocator, dir: std.fs.Dir, views_path: []const u8, paths: *std.ArrayList([]const u8)) !void { @@ -215,7 +215,7 @@ fn zig_build_install(allocator: std.mem.Allocator, path: []const u8, options: Op defer project_dir.close(); project_dir.makePath(".bundle") catch {}; - try util.runCommandInDir(allocator, install_argv.items, .{ .path = path }); + try util.runCommandInDir(allocator, install_argv.items, .{ .path = path }, .{}); const install_bin_path = try std.fs.path.join(allocator, &[_][]const u8{ ".bundle", "bin" }); defer allocator.free(install_bin_path); diff --git a/cli/commands/init.zig b/cli/commands/init.zig index 9556661..42ae81e 100644 --- a/cli/commands/init.zig +++ b/cli/commands/init.zig @@ -202,6 +202,7 @@ pub fn run( github_url, }, .{ .dir = install_dir }, + .{}, ); // TODO: Use arg or interactive prompt to do Git setup in net project, default to no. @@ -260,7 +261,7 @@ fn copySourceFile( util.printFailure(); return err; }; - util.printSuccess(); + util.printSuccess(null); } // Read a file from Jetzig source code. @@ -333,6 +334,7 @@ fn gitSetup(allocator: std.mem.Allocator, install_dir: *std.fs.Dir) !void { ".", }, .{ .path = install_dir }, + .{}, ); try util.runCommandInDir( @@ -343,6 +345,7 @@ fn gitSetup(allocator: std.mem.Allocator, install_dir: *std.fs.Dir) !void { ".", }, .{ .path = install_dir }, + .{}, ); try util.runCommandInDir( @@ -354,5 +357,6 @@ fn gitSetup(allocator: std.mem.Allocator, install_dir: *std.fs.Dir) !void { "Initialize Jetzig project", }, .{ .path = install_dir }, + .{}, ); } diff --git a/cli/commands/server.zig b/cli/commands/server.zig index e51c29f..36cd777 100644 --- a/cli/commands/server.zig +++ b/cli/commands/server.zig @@ -78,11 +78,7 @@ pub fn run( }); while (true) { - util.runCommandInDir( - allocator, - argv.items, - .{ .path = realpath }, - ) catch { + util.runCommandInDir(allocator, argv.items, .{ .path = realpath }, .{}) catch { std.debug.print("Build failed, waiting for file change...\n", .{}); try awaitFileChange(allocator, cwd, &mtime); std.debug.print("Changes detected, restarting server...\n", .{}); diff --git a/cli/util.zig b/cli/util.zig index 0f2b925..1cba0f1 100644 --- a/cli/util.zig +++ b/cli/util.zig @@ -23,8 +23,8 @@ const icons = .{ }; /// Print a success confirmation. -pub fn printSuccess() void { - std.debug.print(" " ++ icons.check ++ "\n", .{}); +pub fn printSuccess(message: ?[]const u8) void { + std.debug.print(" " ++ icons.check ++ " {s}\n", .{message orelse ""}); } /// Print a failure confirmation. @@ -153,7 +153,7 @@ pub fn runCommandStreaming(allocator: std.mem.Allocator, install_path: []const u pub fn runCommand(allocator: std.mem.Allocator, argv: []const []const u8) !void { var dir = try detectJetzigProjectDir(); defer dir.close(); - try runCommandInDir(allocator, argv, .{ .dir = dir }); + try runCommandInDir(allocator, argv, .{ .dir = dir }, .{}); } const Dir = union(enum) { @@ -161,34 +161,53 @@ const Dir = union(enum) { dir: std.fs.Dir, }; +pub const RunOptions = struct { + output: enum { stream, capture } = .capture, + wait: bool = true, +}; + /// Runs a command as a child process in the given directory and verifies successful exit code. -pub fn runCommandInDir(allocator: std.mem.Allocator, argv: []const []const u8, dir: Dir) !void { +pub fn runCommandInDir(allocator: std.mem.Allocator, argv: []const []const u8, dir: Dir, options: RunOptions) !void { const cwd_path = switch (dir) { .path => |capture| capture, .dir => |capture| try capture.realpathAlloc(allocator, "."), }; defer if (dir == .dir) allocator.free(cwd_path); - const result = std.process.Child.run(.{ - .allocator = allocator, - .argv = argv, - .cwd = cwd_path, - }) catch |err| { - switch (err) { - error.FileNotFound => { - printFailure(); - const cmd_str = try std.mem.join(allocator, " ", argv); - defer allocator.free(cmd_str); - std.debug.print( - \\Error: Could not execute command - executable '{s}' not found - \\Command: {s} - \\Working directory: {s} - \\ - , .{ argv[0], cmd_str, cwd_path }); - return error.JetzigCommandError; - }, - else => return err, - } + const output_behaviour: std.process.Child.StdIo = switch (options.output) { + .stream => .Inherit, + .capture => .Pipe, + }; + + var child = std.process.Child.init(argv, allocator); + child.stdin_behavior = .Ignore; + child.stdout_behavior = output_behaviour; + child.stderr_behavior = output_behaviour; + if (options.output == .stream) { + child.stdout = std.io.getStdOut(); + child.stderr = std.io.getStdErr(); + } + child.cwd = cwd_path; + + var stdout = try std.ArrayListUnmanaged(u8).initCapacity(allocator, 0); + var stderr = try std.ArrayListUnmanaged(u8).initCapacity(allocator, 0); + errdefer { + stdout.deinit(allocator); + stderr.deinit(allocator); + } + + try child.spawn(); + switch (options.output) { + .capture => try collectOutput(child, allocator, &stdout, &stderr), + .stream => {}, + } + + if (!options.wait) return; + + const result = std.process.Child.RunResult{ + .term = try child.wait(), + .stdout = try stdout.toOwnedSlice(allocator), + .stderr = try stderr.toOwnedSlice(allocator), }; defer allocator.free(result.stdout); defer allocator.free(result.stderr); @@ -203,10 +222,55 @@ pub fn runCommandInDir(allocator: std.mem.Allocator, argv: []const []const u8, d } return error.JetzigCommandError; } else { - printSuccess(); + printSuccess(try std.mem.join(allocator, " ", argv)); } } +fn collectOutput( + child: std.process.Child, + allocator: std.mem.Allocator, + stdout: *std.ArrayListUnmanaged(u8), + stderr: *std.ArrayListUnmanaged(u8), +) !void { + const max_output_bytes = 50 * 1024; + std.debug.assert(child.stdout_behavior == .Pipe); + std.debug.assert(child.stderr_behavior == .Pipe); + var poller = std.io.poll(allocator, enum { stdout, stderr }, .{ + .stdout = child.stdout.?, + .stderr = child.stderr.?, + }); + defer poller.deinit(); + + std.debug.print("(working) .", .{}); + + while (try poller.poll()) { + if (poller.fifo(.stdout).count > max_output_bytes) + return error.StdoutStreamTooLong; + if (poller.fifo(.stderr).count > max_output_bytes) + return error.StderrStreamTooLong; + std.debug.print(".", .{}); + } + + std.debug.print(" (done)\n", .{}); + + try writeFifoDataToArrayList(allocator, stdout, poller.fifo(.stdout)); + try writeFifoDataToArrayList(allocator, stderr, poller.fifo(.stderr)); +} + +// Borrowed from `std.process.Child.writeFifoDataToArrayList` - non-public but needed in our +// modified `collectOutput` +fn writeFifoDataToArrayList(allocator: std.mem.Allocator, list: *std.ArrayListUnmanaged(u8), fifo: *std.io.PollFifo) !void { + if (fifo.head != 0) fifo.realign(); + if (list.capacity == 0) { + list.* = .{ + .items = fifo.buf[0..fifo.count], + .capacity = fifo.buf.len, + }; + fifo.* = std.io.PollFifo.init(fifo.allocator); + } else { + try list.appendSlice(allocator, fifo.buf[0..fifo.count]); + } +} /// Generate a full GitHub URL for passing to `zig fetch`. pub fn githubUrl(allocator: std.mem.Allocator) ![]const u8 { var client = std.http.Client{ .allocator = allocator }; diff --git a/demo/.gitignore b/demo/.gitignore index 1ddcd8a..0d1d8f7 100644 --- a/demo/.gitignore +++ b/demo/.gitignore @@ -4,3 +4,4 @@ static/ src/app/views/**/.*.zig .DS_Store log/ +src/routes.zig diff --git a/demo/build.zig.zon b/demo/build.zig.zon index 321158c..a6e4283 100644 --- a/demo/build.zig.zon +++ b/demo/build.zig.zon @@ -1,7 +1,8 @@ .{ - .name = "jetzig-demo", + .name = .jetzig_demo, .version = "0.0.0", .minimum_zig_version = "0.12.0", + .fingerprint = 0x3877c19710a92a5c, .dependencies = .{ .jetzig = .{ .path = "../", diff --git a/demo/src/app/views/channels/index.zmpl b/demo/src/app/views/channels/index.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/demo/src/app/views/channels/index.zmpl @@ -0,0 +1,3 @@ +