diff --git a/build.zig b/build.zig index 03a6684ec..07fbfa859 100644 --- a/build.zig +++ b/build.zig @@ -4,7 +4,7 @@ const Builder = std.build.Builder; const LibExeObjStep = std.build.LibExeObjStep; const glfw = @import("vendor/mach/glfw/build.zig"); const ft = @import("src/freetype/build.zig"); -const uv = @import("src/libuv/build.zig"); +const libuv = @import("pkg/libuv/build.zig"); const tracylib = @import("src/tracy/build.zig"); const system_sdk = @import("vendor/mach/glfw/system_sdk.zig"); @@ -33,68 +33,116 @@ pub fn build(b: *std.build.Builder) !void { "Name of the conformance app to run with 'run' option.", ); - const exe_options = b.addOptions(); - exe_options.addOption(bool, "tracy_enabled", tracy); - const exe = b.addExecutable("ghostty", "src/main.zig"); - exe.setTarget(target); - exe.setBuildMode(mode); - exe.addOptions("build_options", exe_options); - exe.install(); - exe.addIncludeDir("src/"); - exe.addCSourceFile("src/gb_math.c", &.{}); - exe.addPackagePath("glfw", "vendor/mach/glfw/src/main.zig"); - glfw.link(b, exe, .{ + + // Exe + { + const exe_options = b.addOptions(); + exe_options.addOption(bool, "tracy_enabled", tracy); + + exe.setTarget(target); + exe.setBuildMode(mode); + exe.addOptions("build_options", exe_options); + exe.install(); + + // Add the shared dependencies + try addDeps(b, exe); + + // Tracy + if (tracy) try tracylib.link(b, exe, target); + } + + // Run + { + // Build our run step, which runs the main app by default, but will + // run a conformance app if `-Dconformance` is set. + const run_exe = if (conformance) |name| blk: { + var conformance_exes = try conformanceSteps(b, target, mode); + defer conformance_exes.deinit(); + break :blk conformance_exes.get(name) orelse return error.InvalidConformance; + } else exe; + const run_cmd = run_exe.run(); + run_cmd.step.dependOn(&run_exe.step); + if (b.args) |args| { + run_cmd.addArgs(args); + } + + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + } + + // Tests + { + const test_step = b.step("test", "Run all tests"); + var test_bin_ = b.option([]const u8, "test-bin", "Emit bin to"); + var test_filter = b.option([]const u8, "test-filter", "Filter for test"); + + const main_test = b.addTest("src/main.zig"); + { + main_test.setFilter(test_filter); + if (test_bin_) |test_bin| { + main_test.name = std.fs.path.basename(test_bin); + if (std.fs.path.dirname(test_bin)) |dir| main_test.setOutputDir(dir); + } + + main_test.setTarget(target); + try addDeps(b, main_test); + + var before = b.addLog("\x1b[" ++ color_map.get("cyan").? ++ "\x1b[" ++ color_map.get("b").? ++ "[{s} tests]" ++ "\x1b[" ++ color_map.get("d").? ++ " ----" ++ "\x1b[0m", .{"ghostty"}); + var after = b.addLog("\x1b[" ++ color_map.get("d").? ++ "–––---\n\n" ++ "\x1b[0m", .{}); + test_step.dependOn(&before.step); + test_step.dependOn(&main_test.step); + test_step.dependOn(&after.step); + } + + // Named package dependencies don't have their tests run by reference, + // so we iterate through them here. We're only interested in dependencies + // we wrote (are in the "pkg/" directory). + for (main_test.packages.items) |pkg_| { + const pkg: std.build.Pkg = pkg_; + if (std.mem.eql(u8, pkg.name, "glfw")) continue; + var test_ = b.addTestSource(pkg.source); + + test_.setTarget(target); + try addDeps(b, test_); + if (pkg.dependencies) |children| { + test_.packages = std.ArrayList(std.build.Pkg).init(b.allocator); + try test_.packages.appendSlice(children); + } + + var before = b.addLog("\x1b[" ++ color_map.get("cyan").? ++ "\x1b[" ++ color_map.get("b").? ++ "[{s} tests]" ++ "\x1b[" ++ color_map.get("d").? ++ " ----" ++ "\x1b[0m", .{pkg.name}); + var after = b.addLog("\x1b[" ++ color_map.get("d").? ++ "–––---\n\n" ++ "\x1b[0m", .{}); + test_step.dependOn(&before.step); + test_step.dependOn(&test_.step); + test_step.dependOn(&after.step); + } + } +} + +/// Adds and links all of the primary dependencies for the exe. +fn addDeps( + b: *std.build.Builder, + step: *std.build.LibExeObjStep, +) !void { + step.addIncludeDir("src/"); + step.addCSourceFile("src/gb_math.c", &.{}); + step.addIncludeDir("vendor/glad/include/"); + step.addCSourceFile("vendor/glad/src/gl.c", &.{}); + + // Freetype + const ftlib = try ft.create(b, step.target, step.build_mode, .{}); + ftlib.link(step); + + // Glfw + step.addPackage(glfw.pkg); + glfw.link(b, step, .{ .metal = false, .opengl = false, // Found at runtime }); - // Tracy - if (tracy) try tracylib.link(b, exe, target); - - // GLAD - exe.addIncludeDir("vendor/glad/include/"); - exe.addCSourceFile("vendor/glad/src/gl.c", &.{}); - - const ftlib = try ft.create(b, target, mode, .{}); - ftlib.link(exe); - - const libuv = try uv.create(b, target, mode); - system_sdk.include(b, libuv.step, .{}); - libuv.link(exe); - - // stb if we need it - // exe.addIncludeDir("vendor/stb"); - // exe.addCSourceFile("src/stb/stb.c", &.{}); - - // Conformance apps - var conformance_exes = try conformanceSteps(b, target, mode); - defer conformance_exes.deinit(); - - // Build our run step, which runs the main app by default, but will - // run a conformance app if `-Dconformance` is set. - const run_exe = if (conformance) |name| - conformance_exes.get(name) orelse return error.InvalidConformance - else - exe; - const run_cmd = run_exe.run(); - run_cmd.step.dependOn(&run_exe.step); - if (b.args) |args| { - run_cmd.addArgs(args); - } - - const run_step = b.step("run", "Run the app"); - run_step.dependOn(&run_cmd.step); - - // Tests - const test_step = b.step("test", "Run all tests"); - const lib_tests = b.addTest("src/main.zig"); - ftlib.link(lib_tests); - libuv.link(lib_tests); - lib_tests.setTarget(target); - lib_tests.addIncludeDir("vendor/glad/include/"); - lib_tests.addCSourceFile("vendor/glad/src/gl.c", &.{}); - test_step.dependOn(&lib_tests.step); + // Libuv + step.addPackage(libuv.pkg); + try libuv.link(b, step); } fn conformanceSteps( @@ -139,3 +187,17 @@ fn conformanceSteps( fn root() []const u8 { return std.fs.path.dirname(@src().file) orelse unreachable; } + +/// ANSI escape codes for colored log output +const color_map = std.ComptimeStringMap([]const u8, .{ + &.{ "black", "30m" }, + &.{ "blue", "34m" }, + &.{ "b", "1m" }, + &.{ "d", "2m" }, + &.{ "cyan", "36m" }, + &.{ "green", "32m" }, + &.{ "magenta", "35m" }, + &.{ "red", "31m" }, + &.{ "white", "37m" }, + &.{ "yellow", "33m" }, +}); diff --git a/src/libuv/Async.zig b/pkg/libuv/Async.zig similarity index 100% rename from src/libuv/Async.zig rename to pkg/libuv/Async.zig diff --git a/src/libuv/Cond.zig b/pkg/libuv/Cond.zig similarity index 100% rename from src/libuv/Cond.zig rename to pkg/libuv/Cond.zig diff --git a/src/libuv/Embed.zig b/pkg/libuv/Embed.zig similarity index 100% rename from src/libuv/Embed.zig rename to pkg/libuv/Embed.zig diff --git a/src/libuv/Loop.zig b/pkg/libuv/Loop.zig similarity index 100% rename from src/libuv/Loop.zig rename to pkg/libuv/Loop.zig diff --git a/src/libuv/Mutex.zig b/pkg/libuv/Mutex.zig similarity index 100% rename from src/libuv/Mutex.zig rename to pkg/libuv/Mutex.zig diff --git a/src/libuv/Pipe.zig b/pkg/libuv/Pipe.zig similarity index 100% rename from src/libuv/Pipe.zig rename to pkg/libuv/Pipe.zig diff --git a/src/libuv/Sem.zig b/pkg/libuv/Sem.zig similarity index 100% rename from src/libuv/Sem.zig rename to pkg/libuv/Sem.zig diff --git a/src/libuv/Thread.zig b/pkg/libuv/Thread.zig similarity index 100% rename from src/libuv/Thread.zig rename to pkg/libuv/Thread.zig diff --git a/src/libuv/Timer.zig b/pkg/libuv/Timer.zig similarity index 100% rename from src/libuv/Timer.zig rename to pkg/libuv/Timer.zig diff --git a/src/libuv/Tty.zig b/pkg/libuv/Tty.zig similarity index 60% rename from src/libuv/Tty.zig rename to pkg/libuv/Tty.zig index 7c4b335ae..8756ce061 100644 --- a/src/libuv/Tty.zig +++ b/pkg/libuv/Tty.zig @@ -10,7 +10,6 @@ const errors = @import("error.zig"); const Loop = @import("Loop.zig"); const Handle = @import("handle.zig").Handle; const Stream = @import("stream.zig").Stream; -const Pty = @import("../Pty.zig"); handle: *c.uv_tty_t, @@ -28,24 +27,3 @@ pub fn deinit(self: *Tty, alloc: Allocator) void { alloc.destroy(self.handle); self.* = undefined; } - -test "Tty" { - var pty = try Pty.open(.{ - .ws_row = 20, - .ws_col = 80, - .ws_xpixel = 0, - .ws_ypixel = 0, - }); - defer pty.deinit(); - - var loop = try Loop.init(testing.allocator); - defer loop.deinit(testing.allocator); - var tty = try init(testing.allocator, loop, pty.slave); - defer tty.deinit(testing.allocator); - - try testing.expect(try tty.isReadable()); - try testing.expect(try tty.isWritable()); - - tty.close(null); - _ = try loop.run(.default); -} diff --git a/pkg/libuv/build.zig b/pkg/libuv/build.zig new file mode 100644 index 000000000..9ce738c85 --- /dev/null +++ b/pkg/libuv/build.zig @@ -0,0 +1,169 @@ +const std = @import("std"); + +/// Directories with our includes. +const root = thisDir() ++ "../../../vendor/libuv/"; +const include_path = root ++ "include"; + +pub const pkg = std.build.Pkg{ + .name = "libuv", + .source = .{ .path = thisDir() ++ "/main.zig" }, +}; + +fn thisDir() []const u8 { + return std.fs.path.dirname(@src().file) orelse "."; +} + +pub fn link(b: *std.build.Builder, step: *std.build.LibExeObjStep) !void { + const libuv = try buildLibuv(b, step); + step.linkLibrary(libuv); + step.addIncludePath(include_path); +} + +pub fn buildLibuv( + b: *std.build.Builder, + step: *std.build.LibExeObjStep, +) !*std.build.LibExeObjStep { + const lib = b.addStaticLibrary("uv", null); + lib.setTarget(step.target); + lib.setBuildMode(step.build_mode); + + const target = step.target; + + // Include dirs + lib.addIncludePath(include_path); + lib.addIncludePath(root ++ "src"); + + // Links + if (target.isWindows()) { + lib.linkSystemLibrary("psapi"); + lib.linkSystemLibrary("user32"); + lib.linkSystemLibrary("advapi32"); + lib.linkSystemLibrary("iphlpapi"); + lib.linkSystemLibrary("userenv"); + lib.linkSystemLibrary("ws2_32"); + } + if (target.isLinux()) { + lib.linkSystemLibrary("pthread"); + } + lib.linkLibC(); + + // Compilation + var flags = std.ArrayList([]const u8).init(b.allocator); + defer flags.deinit(); + // try flags.appendSlice(&.{}); + + if (!target.isWindows()) { + try flags.appendSlice(&.{ + "-D_FILE_OFFSET_BITS=64", + "-D_LARGEFILE_SOURCE", + }); + } + + if (target.isLinux()) { + try flags.appendSlice(&.{ + "-D_GNU_SOURCE", + "-D_POSIX_C_SOURCE=200112", + }); + } + + if (target.isDarwin()) { + try flags.appendSlice(&.{ + "-D_DARWIN_UNLIMITED_SELECT=1", + "-D_DARWIN_USE_64_BIT_INODE=1", + }); + } + + // C files common to all platforms + lib.addCSourceFiles(&.{ + root ++ "src/fs-poll.c", + root ++ "src/idna.c", + root ++ "src/inet.c", + root ++ "src/random.c", + root ++ "src/strscpy.c", + root ++ "src/strtok.c", + root ++ "src/threadpool.c", + root ++ "src/timer.c", + root ++ "src/uv-common.c", + root ++ "src/uv-data-getter-setters.c", + root ++ "src/version.c", + }, flags.items); + + if (!target.isWindows()) { + lib.addCSourceFiles(&.{ + root ++ "src/unix/async.c", + root ++ "src/unix/core.c", + root ++ "src/unix/dl.c", + root ++ "src/unix/fs.c", + root ++ "src/unix/getaddrinfo.c", + root ++ "src/unix/getnameinfo.c", + root ++ "src/unix/loop-watcher.c", + root ++ "src/unix/loop.c", + root ++ "src/unix/pipe.c", + root ++ "src/unix/poll.c", + root ++ "src/unix/process.c", + root ++ "src/unix/random-devurandom.c", + root ++ "src/unix/signal.c", + root ++ "src/unix/stream.c", + root ++ "src/unix/tcp.c", + root ++ "src/unix/thread.c", + root ++ "src/unix/tty.c", + root ++ "src/unix/udp.c", + }, flags.items); + } + + if (target.isLinux() or target.isDarwin()) { + lib.addCSourceFiles(&.{ + root ++ "src/unix/proctitle.c", + }, flags.items); + } + + if (target.isLinux()) { + lib.addCSourceFiles(&.{ + root ++ "src/unix/linux-core.c", + root ++ "src/unix/linux-inotify.c", + root ++ "src/unix/linux-syscalls.c", + root ++ "src/unix/procfs-exepath.c", + root ++ "src/unix/random-getrandom.c", + root ++ "src/unix/random-sysctl-linux.c", + root ++ "src/unix/epoll.c", + }, flags.items); + } + + if (target.isDarwin() or + target.isOpenBSD() or + target.isNetBSD() or + target.isFreeBSD() or + target.isDragonFlyBSD()) + { + lib.addCSourceFiles(&.{ + root ++ "src/unix/bsd-ifaddrs.c", + root ++ "src/unix/kqueue.c", + }, flags.items); + } + + if (target.isDarwin() or target.isOpenBSD()) { + lib.addCSourceFiles(&.{ + root ++ "src/unix/random-getentropy.c", + }, flags.items); + } + + if (target.isDarwin()) { + lib.addCSourceFiles(&.{ + root ++ "src/unix/darwin-proctitle.c", + root ++ "src/unix/darwin.c", + root ++ "src/unix/fsevents.c", + }, flags.items); + } + + return lib; +} + +pub fn testLibuv( + b: *std.build.Builder, + step: *std.build.LibExeObjStep, +) !*std.build.LibExeObjStep { + const lib = b.addTest(root ++ "src/main.zig"); + lib.setTarget(step.target); + try link(b, step); + return lib; +} diff --git a/src/libuv/c.zig b/pkg/libuv/c.zig similarity index 100% rename from src/libuv/c.zig rename to pkg/libuv/c.zig diff --git a/src/libuv/error.zig b/pkg/libuv/error.zig similarity index 100% rename from src/libuv/error.zig rename to pkg/libuv/error.zig diff --git a/src/libuv/handle.zig b/pkg/libuv/handle.zig similarity index 100% rename from src/libuv/handle.zig rename to pkg/libuv/handle.zig diff --git a/src/libuv/main.zig b/pkg/libuv/main.zig similarity index 100% rename from src/libuv/main.zig rename to pkg/libuv/main.zig diff --git a/src/libuv/stream.zig b/pkg/libuv/stream.zig similarity index 100% rename from src/libuv/stream.zig rename to pkg/libuv/stream.zig diff --git a/src/libuv/tests.zig b/pkg/libuv/tests.zig similarity index 100% rename from src/libuv/tests.zig rename to pkg/libuv/tests.zig diff --git a/src/App.zig b/src/App.zig index 2332c5304..eb988b28a 100644 --- a/src/App.zig +++ b/src/App.zig @@ -7,7 +7,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const glfw = @import("glfw"); const Window = @import("Window.zig"); -const libuv = @import("libuv/main.zig"); +const libuv = @import("libuv"); const tracy = @import("tracy/tracy.zig"); const Config = @import("config.zig").Config; diff --git a/src/Window.zig b/src/Window.zig index 75935e398..7bd203559 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -12,7 +12,7 @@ const Allocator = std.mem.Allocator; const Grid = @import("Grid.zig"); const glfw = @import("glfw"); const gl = @import("opengl.zig"); -const libuv = @import("libuv/main.zig"); +const libuv = @import("libuv"); const Pty = @import("Pty.zig"); const Command = @import("Command.zig"); const SegmentedPool = @import("segmented_pool.zig").SegmentedPool; diff --git a/src/libuv/build.zig b/src/libuv/build.zig deleted file mode 100644 index c4d4e88bd..000000000 --- a/src/libuv/build.zig +++ /dev/null @@ -1,165 +0,0 @@ -const std = @import("std"); - -/// This is the type returned by create. -pub const Library = struct { - step: *std.build.LibExeObjStep, - - /// statically link this library into the given step - pub fn link(self: Library, other: *std.build.LibExeObjStep) void { - self.addIncludeDirs(other); - other.linkLibrary(self.step); - } - - /// only add the include dirs to the given step. This is useful if building - /// a static library that you don't want to fully link in the code of this - /// library. - pub fn addIncludeDirs(self: Library, other: *std.build.LibExeObjStep) void { - _ = self; - other.addIncludeDir(include_dir); - } -}; - -/// Create this library. This is the primary API users of build.zig should -/// use to link this library to their application. On the resulting Library, -/// call the link function and given your own application step. -pub fn create( - b: *std.build.Builder, - target: std.zig.CrossTarget, - mode: std.builtin.Mode, -) !Library { - const ret = b.addStaticLibrary("uv", null); - ret.setTarget(target); - ret.setBuildMode(mode); - - var flags = std.ArrayList([]const u8).init(b.allocator); - defer flags.deinit(); - - // try flags.appendSlice(&.{}); - - if (!target.isWindows()) { - try flags.appendSlice(&.{ - "-D_FILE_OFFSET_BITS=64", - "-D_LARGEFILE_SOURCE", - }); - } - - if (target.isLinux()) { - try flags.appendSlice(&.{ - "-D_GNU_SOURCE", - "-D_POSIX_C_SOURCE=200112", - }); - } - - if (target.isDarwin()) { - try flags.appendSlice(&.{ - "-D_DARWIN_UNLIMITED_SELECT=1", - "-D_DARWIN_USE_64_BIT_INODE=1", - }); - } - - // C files common to all platforms - ret.addCSourceFiles(&.{ - root() ++ "src/fs-poll.c", - root() ++ "src/idna.c", - root() ++ "src/inet.c", - root() ++ "src/random.c", - root() ++ "src/strscpy.c", - root() ++ "src/strtok.c", - root() ++ "src/threadpool.c", - root() ++ "src/timer.c", - root() ++ "src/uv-common.c", - root() ++ "src/uv-data-getter-setters.c", - root() ++ "src/version.c", - }, flags.items); - - if (!target.isWindows()) { - ret.addCSourceFiles(&.{ - root() ++ "src/unix/async.c", - root() ++ "src/unix/core.c", - root() ++ "src/unix/dl.c", - root() ++ "src/unix/fs.c", - root() ++ "src/unix/getaddrinfo.c", - root() ++ "src/unix/getnameinfo.c", - root() ++ "src/unix/loop-watcher.c", - root() ++ "src/unix/loop.c", - root() ++ "src/unix/pipe.c", - root() ++ "src/unix/poll.c", - root() ++ "src/unix/process.c", - root() ++ "src/unix/random-devurandom.c", - root() ++ "src/unix/signal.c", - root() ++ "src/unix/stream.c", - root() ++ "src/unix/tcp.c", - root() ++ "src/unix/thread.c", - root() ++ "src/unix/tty.c", - root() ++ "src/unix/udp.c", - }, flags.items); - } - - if (target.isLinux() or target.isDarwin()) { - ret.addCSourceFiles(&.{ - root() ++ "src/unix/proctitle.c", - }, flags.items); - } - - if (target.isLinux()) { - ret.addCSourceFiles(&.{ - root() ++ "src/unix/linux-core.c", - root() ++ "src/unix/linux-inotify.c", - root() ++ "src/unix/linux-syscalls.c", - root() ++ "src/unix/procfs-exepath.c", - root() ++ "src/unix/random-getrandom.c", - root() ++ "src/unix/random-sysctl-linux.c", - root() ++ "src/unix/epoll.c", - }, flags.items); - } - - if (target.isDarwin() or - target.isOpenBSD() or - target.isNetBSD() or - target.isFreeBSD() or - target.isDragonFlyBSD()) - { - ret.addCSourceFiles(&.{ - root() ++ "src/unix/bsd-ifaddrs.c", - root() ++ "src/unix/kqueue.c", - }, flags.items); - } - - if (target.isDarwin() or target.isOpenBSD()) { - ret.addCSourceFiles(&.{ - root() ++ "src/unix/random-getentropy.c", - }, flags.items); - } - - if (target.isDarwin()) { - ret.addCSourceFiles(&.{ - root() ++ "src/unix/darwin-proctitle.c", - root() ++ "src/unix/darwin.c", - root() ++ "src/unix/fsevents.c", - }, flags.items); - } - - ret.addIncludeDir(include_dir); - ret.addIncludeDir(root() ++ "src"); - if (target.isWindows()) { - ret.linkSystemLibrary("psapi"); - ret.linkSystemLibrary("user32"); - ret.linkSystemLibrary("advapi32"); - ret.linkSystemLibrary("iphlpapi"); - ret.linkSystemLibrary("userenv"); - ret.linkSystemLibrary("ws2_32"); - } - if (target.isLinux()) { - ret.linkSystemLibrary("pthread"); - } - ret.linkLibC(); - - return Library{ .step = ret }; -} - -fn root() []const u8 { - return (std.fs.path.dirname(@src().file) orelse unreachable) ++ "/../../vendor/libuv/"; -} - -/// Directories with our includes. -const include_dir = root() ++ "include"; diff --git a/src/main.zig b/src/main.zig index 7ac523102..576a489f8 100644 --- a/src/main.zig +++ b/src/main.zig @@ -100,8 +100,8 @@ test { _ = @import("terminal/Terminal.zig"); // Libraries + _ = @import("libuv"); _ = @import("segmented_pool.zig"); - _ = @import("libuv/main.zig"); _ = @import("terminal/main.zig"); // TODO diff --git a/src/max_timer.zig b/src/max_timer.zig index 89e1c60b6..bacfa0b92 100644 --- a/src/max_timer.zig +++ b/src/max_timer.zig @@ -1,7 +1,7 @@ const std = @import("std"); const assert = std.debug.assert; const Allocator = std.mem.Allocator; -const libuv = @import("libuv/main.zig"); +const libuv = @import("libuv"); /// A coalescing timer that forces a run after a certain maximum time /// since the last run. This is used for example by the renderer to try