Update to Zig 0.16.0

This commit represents the majority of the work necessary to upgrade
Ghostty to use Zig 0.16.0.

At this point, all tests pass under Linux, but more work may be
necessary to get them to build and function on other platforms.

Build still fails due to outstanding code changes and fixes necessary to
translate-c.

Co-authored-by: Leah Amelia Chen <hi@pluie.me>
pull/12726/merge^2
Chris Marchesi 2026-05-07 09:11:14 -07:00
parent 063ac3ecc5
commit dec86c1dd0
No known key found for this signature in database
GPG Key ID: E9C6A4EBE1EB7618
242 changed files with 4061 additions and 2258 deletions

2
.gitignore vendored
View File

@ -10,6 +10,7 @@
zig-cache/
.zig-cache/
zig-out/
zig-pkg/
build-cmake/
CMakeCache.txt
CMakeFiles/
@ -29,3 +30,4 @@ glad.zip
vgcore.*
/sprite_face_test*

View File

@ -25,9 +25,10 @@ pub fn build(b: *std.Build) !void {
// use that as the version source of truth. Otherwise we fall back
// to what is in the build.zig.zon.
const file_version: ?[]const u8 = if (b.build_root.handle.readFileAlloc(
b.allocator,
b.graph.io,
"VERSION",
128,
b.allocator,
std.Io.Limit.limited(128),
)) |content| std.mem.trim(
u8,
content,
@ -298,7 +299,7 @@ pub fn build(b: *std.Build) !void {
// We need to rebuild Ghostty with a baseline CPU target.
const valgrind_exe = exe: {
var valgrind_config = config;
valgrind_config.target = valgrind_config.baselineTarget();
valgrind_config.target = valgrind_config.baselineTarget(b.graph.io);
break :exe try buildpkg.GhosttyExe.init(
b,
&valgrind_config,
@ -343,7 +344,7 @@ pub fn build(b: *std.Build) !void {
.filters = test_filters,
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = config.baselineTarget(),
.target = config.baselineTarget(b.graph.io),
.optimize = .Debug,
.strip = false,
.omit_frame_pointer = false,
@ -358,7 +359,7 @@ pub fn build(b: *std.Build) !void {
// Verify our internal libghostty header.
const ghostty_h = b.addTranslateC(.{
.root_source_file = b.path("include/ghostty.h"),
.target = config.baselineTarget(),
.target = config.baselineTarget(b.graph.io),
.optimize = .Debug,
});
test_exe.root_module.addImport("ghostty.h", ghostty_h.createModule());
@ -367,6 +368,10 @@ pub fn build(b: *std.Build) !void {
const test_run = b.addRunArtifact(test_exe);
test_step.dependOn(&test_run.step);
// Check step
const check_step = b.step("check-manual", "build but don't run tests (manual version)");
check_step.dependOn(&test_exe.step);
// Normal tests always test our libghostty modules
//test_step.dependOn(test_lib_vt_step);

View File

@ -3,62 +3,61 @@
.version = "1.3.2-dev",
.paths = .{""},
.fingerprint = 0x64407a2a0b4147e5,
.minimum_zig_version = "0.15.2",
.minimum_zig_version = "0.16.0",
.dependencies = .{
// Zig libs
.libxev = .{
// mitchellh/libxev
.url = "https://deps.files.ghostty.org/libxev-34fa50878aec6e5fa8f532867001ab3c36fae23e.tar.gz",
.hash = "libxev-0.0.0-86vtc4IcEwCqEYxEYoN_3KXmc6A9VLcm22aVImfvecYs",
.url = "https://github.com/mitchellh/libxev/archive/9ce8e8e6ff89e583258a7f8e7adeeeaeae8611bf.tar.gz",
.hash = "libxev-0.0.0-86vtcwIRFADbH4hk-EjROXxlrKIRPQdA41XiTSytYO-F",
.lazy = true,
},
.vaxis = .{
// rockorager/libvaxis
.url = "https://deps.files.ghostty.org/vaxis-7dbb9fd3122e4ffad262dd7c151d80d863b68558.tar.gz",
.hash = "vaxis-0.5.1-BWNV_LosCQAGmCCNOLljCIw6j6-yt53tji6n6rwJ2BhS",
.url = "https://github.com/rockorager/libvaxis/archive/1dbbe575dff4586fe51e3217aa5c3fecdcbb6089.tar.gz",
.hash = "vaxis-0.6.0-BWNV_CrbCQCscGpzsAlR402rYQ_tV3aAl081c2iRRkka",
.lazy = true,
},
.z2d = .{
// vancluever/z2d
.url = "https://deps.files.ghostty.org/z2d-0.10.0-j5P_Hu-6FgBsZNgwphIqh17jDnj8_yPtD8yzjO6PpHRQ.tar.gz",
.hash = "z2d-0.10.0-j5P_Hu-6FgBsZNgwphIqh17jDnj8_yPtD8yzjO6PpHRQ",
.url = "https://github.com/vancluever/z2d/archive/refs/tags/v0.11.0.tar.gz",
.hash = "z2d-0.11.0-j5P_HtLzDwBGyQt49DrT0v4BuVqI_SRs6CXsuj7eBVhR",
.lazy = true,
},
.zig_objc = .{
// mitchellh/zig-objc
.url = "https://deps.files.ghostty.org/zig_objc-f356ed02833f0f1b8e84d50bed9e807bf7cdc0ae.tar.gz",
.hash = "zig_objc-0.0.0-Ir_Sp5gTAQCvxxR7oVIrPXxXwsfKgVP7_wqoOQrZjFeK",
.url = "https://github.com/mitchellh/zig-objc/archive/c8de82ff80281215ad92900866dab7103a8efa8b.tar.gz",
.hash = "zig_objc-0.0.0-Ir_Sp9gsAQCPAJc0oF5xoWePHWP6Y6tCphDeyNUThJoi",
.lazy = true,
},
.zig_js = .{
// mitchellh/zig-js
.url = "https://deps.files.ghostty.org/zig_js-04db83c617da1956ac5adc1cb9ba1e434c1cb6fd.tar.gz",
.hash = "zig_js-0.0.0-rjCAV-6GAADxFug7rDmPH-uM_XcnJ5NmuAMJCAscMjhi",
.url = "https://github.com/mitchellh/zig-js/archive/3c23860e47fdcdc5af805efb7fd0bdac5fd3e9bc.tar.gz",
.hash = "zig_js-0.0.0-rjCAV7-GAADvMTBL7lPMuvDk7xgS9PCMIZWiOUXLZSlj",
.lazy = true,
},
.uucode = .{
// jacobsandlund/uucode
.url = "https://deps.files.ghostty.org/uucode-0.2.0-ZZjBPqZVVABQepOqZHR7vV_NcaN-wats0IB6o-Exj6m9.tar.gz",
.hash = "uucode-0.2.0-ZZjBPqZVVABQepOqZHR7vV_NcaN-wats0IB6o-Exj6m9",
.url = "https://github.com/jacobsandlund/uucode/archive/2826a37a4562284fdacd8fa029d49509cc9bffcd.tar.gz",
.hash = "uucode-0.2.0-ZZjBPlK5VADj7fdoq7G8LIHzD5o6FSkcBXXrRWr4jnrA",
},
.zig_wayland = .{
// codeberg ifreund/zig-wayland
.url = "https://deps.files.ghostty.org/zig_wayland-1b5c038ec10da20ed3a15b0b2a6db1c21383e8ea.tar.gz",
.hash = "wayland-0.5.0-dev-lQa1khrMAQDJDwYFKpdH3HizherB7sHo5dKMECfvxQHe",
.url = "https://codeberg.org/ifreund/zig-wayland/archive/v0.6.0.tar.gz",
.hash = "wayland-0.6.0-lQa1kqz8AQADQmdNJsNhLoNHcnEGEUjrOaPV-dtEnEmX",
.lazy = true,
},
.zf = .{
// natecraddock/zf
.url = "https://deps.files.ghostty.org/zf-3c52637b7e937c5ae61fd679717da3e276765b23.tar.gz",
.hash = "zf-0.10.3-OIRy8RuJAACKA3Lohoumrt85nRbHwbpMcUaLES8vxDnh",
.url = "https://github.com/natecraddock/zf/archive/c35c421f84895193246db06c40683c1a30e616ef.tar.gz",
.hash = "zf-0.11.0-OIRy8X-RAAAwaRXHMYpj2uvBnuGTZWEE_3V7acqHQNtW",
.lazy = true,
},
.gobject = .{
// https://github.com/ghostty-org/zig-gobject based on zig_gobject
// Temporary until we generate them at build time automatically.
.url = "https://deps.files.ghostty.org/gobject-2025-11-08-23-1.tar.zst",
.hash = "gobject-0.3.0-Skun7ANLnwDvEfIpVmohcppXgOvg_I6YOJFmPIsKfXk-",
.url = "https://github.com/ghostty-org/zig-gobject/releases/download/0.8.0-2026-04-23-26-1/ghostty-gobject-0.8.0-2026-04-23-26-1.tar.zst",
.hash = "gobject-0.3.1-Skun7E1KnwBGMX5nslHYG1yWHaSevywxQO8oM7tTOgIp",
.lazy = true,
},

View File

@ -3,11 +3,11 @@
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1761588595,
"narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=",
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
@ -23,11 +23,11 @@
]
},
"locked": {
"lastModified": 1770586272,
"narHash": "sha256-Ucci8mu8QfxwzyfER2DQDbvW9t1BnTUJhBmY7ybralo=",
"lastModified": 1778144356,
"narHash": "sha256-dGM+QCstz/DyLB68+JK5GWyMx4QSqmOJEVgZmy63d/g=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "b1f916ba052341edc1f80d4b2399f1092a4873ca",
"rev": "e4419d3123b780d5f4c0bceeace450424387638c",
"type": "github"
},
"original": {
@ -38,11 +38,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1770537093,
"narHash": "sha256-XV30uo8tXuxdzuV8l3sojmlPRLd/8tpMsOp4lNzLGUo=",
"rev": "fef9403a3e4d31b0a23f0bacebbec52c248fbb51",
"lastModified": 1778124196,
"narHash": "sha256-Z5mLDoR8p0d7psIY4LnyaHHRykXngMcWXTZ9JWvtvPc=",
"rev": "68a8af93ff4297686cb68880845e61e5e2e41d92",
"type": "tarball",
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre942631.fef9403a3e4d/nixexprs.tar.xz"
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre993588.68a8af93ff42/nixexprs.tar.xz"
},
"original": {
"type": "tarball",
@ -88,11 +88,11 @@
]
},
"locked": {
"lastModified": 1776789209,
"narHash": "sha256-G6B7Q4TXn7MZ1mB+f9rymjsYF5PLWoSvmbxijb/99bw=",
"lastModified": 1778158259,
"narHash": "sha256-PwPyM4vM1uzxYqT5txYzauxx9Tfgji/iTPO9KwbTcUc=",
"owner": "mitchellh",
"repo": "zig-overlay",
"rev": "14fe971844e841297ddd2ce9783d6892b467af39",
"rev": "cd6faec62cb29fda88b2195b91edb2dfe6dce9c2",
"type": "github"
},
"original": {
@ -101,40 +101,18 @@
"type": "github"
}
},
"zig_2": {
"inputs": {
"nixpkgs": [
"zon2nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1777234348,
"narHash": "sha256-fKw44a4qbUuI5eTG8k0gPbqMV5TOrjYF35PBzsYgd2U=",
"ref": "refs/heads/main",
"rev": "2c781c0609ecda600ab98f98cca417bbd981bd53",
"revCount": 1677,
"type": "git",
"url": "https://codeberg.org/jcollie/zig-overlay.git"
},
"original": {
"type": "git",
"url": "https://codeberg.org/jcollie/zig-overlay.git"
}
},
"zon2nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"zig": "zig_2"
]
},
"locked": {
"lastModified": 1777314365,
"narHash": "sha256-eLxQaD0wc96Neqkln8wHS0rNq/chPODifFkhwrwilEU=",
"lastModified": 1778078000,
"narHash": "sha256-0djKIL0E8Y1W495bzIkmbCub571ubghZx3Fg7xQPNrk=",
"owner": "jcollie",
"repo": "zon2nix",
"rev": "a5a1d412ad1ab6305511997bbc92b3a9dd6cb784",
"rev": "a9f46c6fe9177135a3001e26ec76715bb998c6cf",
"type": "github"
},
"original": {

View File

@ -77,8 +77,8 @@
default = pkgs.callPackage ./nix/devShell.nix {
zig =
if pkgs.stdenv.hostPlatform.isDarwin
then zig.packages.${pkgs.stdenv.hostPlatform.system}.brew."0.15.2"
else zig.packages.${pkgs.stdenv.hostPlatform.system}."0.15.2";
then zig.packages.${pkgs.stdenv.hostPlatform.system}.brew."0.16.0"
else zig.packages.${pkgs.stdenv.hostPlatform.system}."0.16.0";
wraptest = pkgs.callPackage ./nix/pkgs/wraptest.nix {};
zon2nix = zon2nix;

View File

@ -27,7 +27,6 @@
wasmtime,
wraptest,
zig,
zig_0_15,
zip,
llvmPackages_latest,
bzip2,

View File

@ -118,16 +118,16 @@ pub fn addPaths(b: *std.Build, step: *std.Build.Step.Compile) !void {
fn findNDKPath(b: *std.Build) ?[]const u8 {
// Check if user has set the environment variable for the NDK path.
if (std.process.getEnvVarOwned(b.allocator, "ANDROID_NDK_HOME") catch null) |value| {
if (b.graph.environ_map.get("ANDROID_NDK_HOME")) |value| {
if (value.len == 0) return null;
var dir = std.fs.openDirAbsolute(value, .{}) catch return null;
defer dir.close();
var dir = std.Io.Dir.openDirAbsolute(b.graph.io, value, .{}) catch return null;
defer dir.close(b.graph.io);
return value;
}
// Check the common environment variables for the Android SDK path and look for the NDK inside it.
inline for (.{ "ANDROID_HOME", "ANDROID_SDK_ROOT" }) |env| {
if (std.process.getEnvVarOwned(b.allocator, env) catch null) |sdk| {
if (b.graph.environ_map.get(env)) |sdk| {
if (sdk.len > 0) {
if (findLatestNDK(b, sdk)) |ndk| return ndk;
}
@ -135,10 +135,9 @@ fn findNDKPath(b: *std.Build) ?[]const u8 {
}
// As a fallback, we assume the most common/default SDK path based on the OS.
const home = std.process.getEnvVarOwned(
b.allocator,
const home = b.graph.environ_map.get(
if (builtin.os.tag == .windows) "LOCALAPPDATA" else "HOME",
) catch return null;
) orelse return null;
const default_sdk_path = b.pathJoin(
&.{
@ -157,8 +156,8 @@ fn findNDKPath(b: *std.Build) ?[]const u8 {
fn findLatestNDK(b: *std.Build, sdk_path: []const u8) ?[]const u8 {
const ndk_dir = b.pathJoin(&.{ sdk_path, "ndk" });
var dir = std.fs.openDirAbsolute(ndk_dir, .{ .iterate = true }) catch return null;
defer dir.close();
var dir = std.Io.Dir.openDirAbsolute(b.graph.io, ndk_dir, .{ .iterate = true }) catch return null;
defer dir.close(b.graph.io);
var latest_: ?struct {
name: []const u8,
@ -166,7 +165,7 @@ fn findLatestNDK(b: *std.Build, sdk_path: []const u8) ?[]const u8 {
} = null;
var iterator = dir.iterate();
while (iterator.next() catch null) |file| {
while (iterator.next(b.graph.io) catch null) |file| {
if (file.kind != .directory) continue;
const version = std.SemanticVersion.parse(file.name) catch continue;
if (latest_) |latest| {

View File

@ -9,11 +9,11 @@ pub fn build(b: *std.Build) !void {
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libcpp = true,
}),
.linkage = .static,
});
lib.linkLibCpp();
lib.addIncludePath(b.path("vendor"));
lib.root_module.addIncludePath(b.path("vendor"));
if (target.result.os.tag.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, lib);
@ -23,20 +23,20 @@ pub fn build(b: *std.Build) !void {
defer flags.deinit(b.allocator);
if (b.lazyDependency("breakpad", .{})) |upstream| {
lib.addIncludePath(upstream.path("src"));
lib.addCSourceFiles(.{
lib.root_module.addIncludePath(upstream.path("src"));
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = common,
.flags = flags.items,
});
if (target.result.os.tag.isDarwin()) {
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = common_apple,
.flags = flags.items,
});
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = client_apple,
.flags = flags.items,
@ -44,19 +44,19 @@ pub fn build(b: *std.Build) !void {
switch (target.result.os.tag) {
.macos => {
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = common_mac,
.flags = flags.items,
});
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = client_mac,
.flags = flags.items,
});
},
.ios => lib.addCSourceFiles(.{
.ios => lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = client_ios,
.flags = flags.items,
@ -67,12 +67,12 @@ pub fn build(b: *std.Build) !void {
}
if (target.result.os.tag == .linux) {
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = common_linux,
.flags = flags.items,
});
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = client_linux,
.flags = flags.items,

View File

@ -22,18 +22,16 @@ pub fn build(b: *std.Build) !void {
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
// On MSVC, we must not use linkLibCpp because Zig unconditionally
// passes -nostdinc++ and then adds its bundled libc++/libc++abi
// include paths, which conflict with MSVC's own C++ runtime
// headers. The MSVC SDK include directories (added via linkLibC)
// contain both C and C++ headers, so linkLibCpp is not needed.
.link_libcpp = target.result.abi != .msvc,
}),
.linkage = .static,
});
lib.linkLibC();
// On MSVC, we must not use linkLibCpp because Zig unconditionally
// passes -nostdinc++ and then adds its bundled libc++/libc++abi
// include paths, which conflict with MSVC's own C++ runtime headers.
// The MSVC SDK include directories (added via linkLibC) contain
// both C and C++ headers, so linkLibCpp is not needed.
if (target.result.abi != .msvc) {
lib.linkLibCpp();
}
b.installArtifact(lib);
// Zig module
@ -87,8 +85,8 @@ pub fn build(b: *std.Build) !void {
// Add the core Dear Imgui source files
if (b.lazyDependency("imgui", .{})) |upstream| {
lib.addIncludePath(upstream.path(""));
lib.addCSourceFiles(.{
lib.root_module.addIncludePath(upstream.path(""));
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = &.{
"imgui_demo.cpp",
@ -107,20 +105,20 @@ pub fn build(b: *std.Build) !void {
);
if (freetype) {
lib.addCSourceFile(.{
lib.root_module.addCSourceFile(.{
.file = upstream.path("misc/freetype/imgui_freetype.cpp"),
.flags = flags.items,
});
if (b.systemIntegrationOption("freetype", .{})) {
lib.linkSystemLibrary2("freetype2", dynamic_link_opts);
lib.root_module.linkSystemLibrary("freetype2", dynamic_link_opts);
} else {
const freetype_dep = b.dependency("freetype", .{
.target = target,
.optimize = optimize,
.@"enable-libpng" = true,
});
lib.linkLibrary(freetype_dep.artifact("freetype"));
lib.root_module.linkLibrary(freetype_dep.artifact("freetype"));
if (freetype_dep.builder.lazyDependency(
"freetype",
.{},
@ -131,7 +129,7 @@ pub fn build(b: *std.Build) !void {
}
if (backend_metal) {
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path("backends"),
.files = &.{"imgui_impl_metal.mm"},
.flags = flags.items,
@ -143,7 +141,7 @@ pub fn build(b: *std.Build) !void {
);
}
if (backend_osx) {
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path("backends"),
.files = &.{"imgui_impl_osx.mm"},
.flags = flags.items,
@ -155,7 +153,7 @@ pub fn build(b: *std.Build) !void {
);
}
if (backend_opengl3) {
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path("backends"),
.files = &.{"imgui_impl_opengl3.cpp"},
.flags = flags.items,
@ -170,8 +168,8 @@ pub fn build(b: *std.Build) !void {
// Add the C bindings
if (b.lazyDependency("bindings", .{})) |upstream| {
lib.addIncludePath(upstream.path(""));
lib.addCSourceFiles(.{
lib.root_module.addIncludePath(upstream.path(""));
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = &.{
"dcimgui.cpp",
@ -179,7 +177,7 @@ pub fn build(b: *std.Build) !void {
},
.flags = flags.items,
});
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = b.path(""),
.files = &.{"ext.cpp"},
.flags = flags.items,
@ -201,7 +199,7 @@ pub fn build(b: *std.Build) !void {
}),
});
test_exe.root_module.addOptions("build_options", options);
test_exe.linkLibrary(lib);
test_exe.root_module.linkLibrary(lib);
const tests_run = b.addRunArtifact(test_exe);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&tests_run.step);

View File

@ -41,7 +41,7 @@ pub fn build(b: *std.Build) !void {
if (b.systemIntegrationOption("fontconfig", .{})) {
module.linkSystemLibrary("fontconfig", dynamic_link_opts);
test_exe.linkSystemLibrary2("fontconfig", dynamic_link_opts);
test_exe.root_module.linkSystemLibrary("fontconfig", dynamic_link_opts);
} else {
const lib = try buildLib(b, module, .{
.target = target,
@ -54,7 +54,7 @@ pub fn build(b: *std.Build) !void {
.dynamic_link_opts = dynamic_link_opts,
});
test_exe.linkLibrary(lib);
test_exe.root_module.linkLibrary(lib);
}
}
@ -71,15 +71,18 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
}),
.linkage = .static,
});
lib.linkLibC();
const dynamic_link_opts = options.dynamic_link_opts;
if (target.result.os.tag != .windows) {
lib.linkSystemLibrary("pthread");
lib.root_module.linkSystemLibrary("pthread", dynamic_link_opts);
}
lib.addIncludePath(b.path("override/include"));
lib.root_module.addIncludePath(b.path("override/include"));
module.addIncludePath(b.path("override/include"));
var flags: std.ArrayList([]const u8) = .empty;
@ -194,19 +197,17 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
}
}
const dynamic_link_opts = options.dynamic_link_opts;
// Freetype2
_ = b.systemIntegrationOption("freetype", .{}); // So it shows up in help
if (freetype_enabled) {
if (b.systemIntegrationOption("freetype", .{})) {
lib.linkSystemLibrary2("freetype2", dynamic_link_opts);
lib.root_module.linkSystemLibrary("freetype2", dynamic_link_opts);
} else {
if (b.lazyDependency(
"freetype",
.{ .target = target, .optimize = optimize },
)) |freetype_dep| {
lib.linkLibrary(freetype_dep.artifact("freetype"));
lib.root_module.linkLibrary(freetype_dep.artifact("freetype"));
}
}
}
@ -227,22 +228,22 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
}
if (b.systemIntegrationOption("libxml2", .{})) {
lib.linkSystemLibrary2("libxml-2.0", dynamic_link_opts);
lib.root_module.linkSystemLibrary("libxml-2.0", dynamic_link_opts);
} else {
if (b.lazyDependency("libxml2", .{
.target = target,
.optimize = optimize,
.iconv = libxml2_iconv_enabled,
})) |libxml2_dep| {
lib.linkLibrary(libxml2_dep.artifact("xml2"));
lib.root_module.linkLibrary(libxml2_dep.artifact("xml2"));
}
}
}
if (b.lazyDependency("fontconfig", .{})) |upstream| {
lib.addIncludePath(upstream.path(""));
lib.root_module.addIncludePath(upstream.path(""));
module.addIncludePath(upstream.path(""));
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = srcs,
.flags = flags.items,

View File

@ -39,7 +39,7 @@ pub fn build(b: *std.Build) !void {
if (b.systemIntegrationOption("freetype", .{})) {
module.linkSystemLibrary("freetype2", dynamic_link_opts);
if (test_exe) |exe| {
exe.linkSystemLibrary2("freetype2", dynamic_link_opts);
exe.root_module.linkSystemLibrary("freetype2", dynamic_link_opts);
}
} else {
const lib = try buildLib(b, module, .{
@ -52,7 +52,7 @@ pub fn build(b: *std.Build) !void {
});
if (test_exe) |exe| {
exe.linkLibrary(lib);
exe.root_module.linkLibrary(lib);
}
}
}
@ -68,10 +68,10 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
}),
.linkage = .static,
});
lib.linkLibC();
if (target.result.os.tag.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, lib);
@ -101,10 +101,10 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
// Zlib
if (b.systemIntegrationOption("zlib", .{})) {
lib.linkSystemLibrary2("zlib", dynamic_link_opts);
lib.root_module.linkSystemLibrary("zlib", dynamic_link_opts);
} else {
const zlib_dep = b.dependency("zlib", .{ .target = target, .optimize = optimize });
lib.linkLibrary(zlib_dep.artifact("z"));
lib.root_module.linkLibrary(zlib_dep.artifact("z"));
}
// Libpng
@ -113,50 +113,50 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
try flags.append(b.allocator, "-DFT_CONFIG_OPTION_USE_PNG=1");
if (b.systemIntegrationOption("libpng", .{})) {
lib.linkSystemLibrary2("libpng", dynamic_link_opts);
lib.root_module.linkSystemLibrary("libpng", dynamic_link_opts);
} else {
const libpng_dep = b.dependency(
"libpng",
.{ .target = target, .optimize = optimize },
);
lib.linkLibrary(libpng_dep.artifact("png"));
lib.root_module.linkLibrary(libpng_dep.artifact("png"));
}
}
if (b.lazyDependency("freetype", .{})) |upstream| {
lib.addIncludePath(upstream.path("include"));
lib.root_module.addIncludePath(upstream.path("include"));
module.addIncludePath(upstream.path("include"));
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = srcs,
.flags = flags.items,
});
switch (target.result.os.tag) {
.linux => lib.addCSourceFile(.{
.linux => lib.root_module.addCSourceFile(.{
.file = upstream.path("builds/unix/ftsystem.c"),
.flags = flags.items,
}),
.windows => lib.addCSourceFile(.{
.windows => lib.root_module.addCSourceFile(.{
.file = upstream.path("builds/windows/ftsystem.c"),
.flags = flags.items,
}),
else => lib.addCSourceFile(.{
else => lib.root_module.addCSourceFile(.{
.file = upstream.path("src/base/ftsystem.c"),
.flags = flags.items,
}),
}
switch (target.result.os.tag) {
.windows => {
lib.addCSourceFile(.{
lib.root_module.addCSourceFile(.{
.file = upstream.path("builds/windows/ftdebug.c"),
.flags = flags.items,
});
lib.addWin32ResourceFile(.{
lib.root_module.addWin32ResourceFile(.{
.file = upstream.path("src/base/ftver.rc"),
});
},
else => lib.addCSourceFile(.{
else => lib.root_module.addCSourceFile(.{
.file = upstream.path("src/base/ftdebug.c"),
.flags = flags.items,
}),

View File

@ -26,7 +26,7 @@ pub fn build(b: *std.Build) !void {
.optimize = optimize,
}),
});
test_exe.linkLibrary(lib);
test_exe.root_module.linkLibrary(lib);
const tests_run = b.addRunArtifact(test_exe);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&tests_run.step);
@ -47,20 +47,18 @@ fn buildGlslang(
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
// On MSVC, we must not use linkLibCpp because Zig unconditionally
// passes -nostdinc++ and then adds its bundled libc++/libc++abi
// include paths, which conflict with MSVC's own C++ runtime
// headers. The MSVC SDK include directories (added via linkLibC)
// contain both C and C++ headers, so linkLibCpp is not needed.
.link_libcpp = target.result.abi != .msvc,
}),
.linkage = .static,
});
lib.linkLibC();
// On MSVC, we must not use linkLibCpp because Zig unconditionally
// passes -nostdinc++ and then adds its bundled libc++/libc++abi
// include paths, which conflict with MSVC's own C++ runtime headers.
// The MSVC SDK include directories (added via linkLibC) contain
// both C and C++ headers, so linkLibCpp is not needed.
if (target.result.abi != .msvc) {
lib.linkLibCpp();
}
if (upstream_) |upstream| lib.addIncludePath(upstream.path(""));
lib.addIncludePath(b.path("override"));
if (upstream_) |upstream| lib.root_module.addIncludePath(upstream.path(""));
lib.root_module.addIncludePath(b.path("override"));
if (target.result.os.tag.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, lib);
@ -82,7 +80,7 @@ fn buildGlslang(
}
if (upstream_) |upstream| {
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.flags = flags.items,
.files = &.{
@ -141,7 +139,7 @@ fn buildGlslang(
});
if (target.result.os.tag != .windows) {
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.flags = flags.items,
.files = &.{
@ -149,7 +147,7 @@ fn buildGlslang(
},
});
} else {
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.flags = flags.items,
.files = &.{

View File

@ -42,6 +42,7 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
}),
});
b.installArtifact(lib);
@ -52,13 +53,12 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
const upstream = upstream_ orelse return lib;
const wayland_protocols = wayland_protocols_ orelse return lib;
lib.linkLibC();
lib.addIncludePath(upstream.path("include"));
lib.addIncludePath(upstream.path("src"));
lib.root_module.addIncludePath(upstream.path("include"));
lib.root_module.addIncludePath(upstream.path("src"));
module.addIncludePath(upstream.path("include"));
// GTK
lib.linkSystemLibrary2("gtk4", dynamic_link_opts);
lib.root_module.linkSystemLibrary("gtk4", dynamic_link_opts);
// Wayland headers and source files
{
@ -92,9 +92,9 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
const source_scanner = b.addSystemCommand(&.{ "wayland-scanner", "private-code" });
source_scanner.addFileArg(xml);
const source = source_scanner.addOutputFileArg(b.fmt("{s}.c", .{name}));
lib.addCSourceFile(.{ .file = source });
lib.root_module.addCSourceFile(.{ .file = source });
}
lib.addIncludePath(wf.getDirectory());
lib.root_module.addIncludePath(wf.getDirectory());
}
lib.installHeadersDirectory(
@ -113,7 +113,7 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
"stubbed-surface.c",
"xdg-surface-server.c",
};
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path("src"),
.files = srcs,
.flags = &.{

View File

@ -54,9 +54,9 @@ pub fn build(b: *std.Build) !void {
var it = module.import_table.iterator();
while (it.next()) |entry| test_exe.root_module.addImport(entry.key_ptr.*, entry.value_ptr.*);
if (b.systemIntegrationOption("freetype", .{})) {
test_exe.linkSystemLibrary2("freetype2", dynamic_link_opts);
test_exe.root_module.linkSystemLibrary("freetype2", dynamic_link_opts);
} else {
test_exe.linkLibrary(freetype.artifact("freetype"));
test_exe.root_module.linkLibrary(freetype.artifact("freetype"));
}
const tests_run = b.addRunArtifact(test_exe);
const test_step = b.step("test", "Run tests");
@ -65,7 +65,7 @@ pub fn build(b: *std.Build) !void {
if (b.systemIntegrationOption("harfbuzz", .{})) {
module.linkSystemLibrary("harfbuzz", dynamic_link_opts);
test_exe.linkSystemLibrary2("harfbuzz", dynamic_link_opts);
test_exe.root_module.linkSystemLibrary("harfbuzz", dynamic_link_opts);
} else {
const lib = try buildLib(b, module, .{
.target = target,
@ -77,7 +77,7 @@ pub fn build(b: *std.Build) !void {
.dynamic_link_opts = dynamic_link_opts,
});
test_exe.linkLibrary(lib);
test_exe.root_module.linkLibrary(lib);
}
}
@ -99,18 +99,16 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
// On MSVC, we must not use linkLibCpp because Zig unconditionally
// passes -nostdinc++ and then adds its bundled libc++/libc++abi
// include paths, which conflict with MSVC's own C++ runtime
// headers. The MSVC SDK include directories (added via linkLibC)
// contain both C and C++ headers, so linkLibCpp is not needed.
.link_libcpp = target.result.abi != .msvc,
}),
.linkage = .static,
});
lib.linkLibC();
// On MSVC, we must not use linkLibCpp because Zig unconditionally
// passes -nostdinc++ and then adds its bundled libc++/libc++abi
// include paths, which conflict with MSVC's own C++ runtime headers.
// The MSVC SDK include directories (added via linkLibC) contain
// both C and C++ headers, so linkLibCpp is not needed.
if (target.result.abi != .msvc) {
lib.linkLibCpp();
}
if (target.result.os.tag.isDarwin()) {
try apple_sdk.addPaths(b, lib);
@ -154,10 +152,10 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
});
if (b.systemIntegrationOption("freetype", .{})) {
lib.linkSystemLibrary2("freetype2", dynamic_link_opts);
lib.root_module.linkSystemLibrary("freetype2", dynamic_link_opts);
module.linkSystemLibrary("freetype2", dynamic_link_opts);
} else {
lib.linkLibrary(freetype.artifact("freetype"));
lib.root_module.linkLibrary(freetype.artifact("freetype"));
if (freetype.builder.lazyDependency(
"freetype",
@ -170,14 +168,14 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
if (coretext_enabled) {
try flags.appendSlice(b.allocator, &.{"-DHAVE_CORETEXT=1"});
lib.linkFramework("CoreText");
lib.root_module.linkFramework("CoreText", .{});
module.linkFramework("CoreText", .{});
}
if (b.lazyDependency("harfbuzz", .{})) |upstream| {
lib.addIncludePath(upstream.path("src"));
lib.root_module.addIncludePath(upstream.path("src"));
module.addIncludePath(upstream.path("src"));
lib.addCSourceFile(.{
lib.root_module.addCSourceFile(.{
.file = upstream.path("src/harfbuzz.cc"),
.flags = flags.items,
});

View File

@ -18,17 +18,16 @@ pub fn build(b: *std.Build) !void {
.root_source_file = b.path("src/detect.zig"),
.target = target,
.optimize = optimize,
// Our highway package is free of libc at runtime (uses no symbols)
// but does require libc headers at compile time.
.link_libc = true,
}),
.linkage = .static,
});
// Our highway package is free of libc at runtime (uses no symbols)
// but does require libc headers at compile time.
lib.linkLibC();
lib.addIncludePath(b.path("src/cpp"));
lib.root_module.addIncludePath(b.path("src/cpp"));
if (upstream_) |upstream| {
lib.addIncludePath(upstream.path(""));
lib.root_module.addIncludePath(upstream.path(""));
module.addIncludePath(upstream.path(""));
}
@ -95,7 +94,7 @@ pub fn build(b: *std.Build) !void {
});
}
lib.addCSourceFiles(.{ .flags = flags.items, .files = &.{
lib.root_module.addCSourceFiles(.{ .flags = flags.items, .files = &.{
"src/cpp/abort.cc",
"src/cpp/per_target.cc",
"src/cpp/targets.cpp",
@ -120,7 +119,7 @@ pub fn build(b: *std.Build) !void {
.optimize = optimize,
}),
});
test_exe.linkLibrary(lib);
test_exe.root_module.linkLibrary(lib);
var it = module.import_table.iterator();
while (it.next()) |entry| test_exe.root_module.addImport(entry.key_ptr.*, entry.value_ptr.*);

View File

@ -35,11 +35,11 @@ pub fn build(b: *std.Build) !void {
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
}),
.linkage = .static,
});
lib.linkLibC();
lib.addIncludePath(b.path(""));
lib.root_module.addIncludePath(b.path(""));
if (target.result.os.tag.isDarwin()) {
const apple_sdk = @import("apple_sdk");
@ -47,9 +47,9 @@ pub fn build(b: *std.Build) !void {
}
if (b.lazyDependency("gettext", .{})) |upstream| {
lib.addIncludePath(upstream.path("gettext-runtime/intl"));
lib.addIncludePath(upstream.path("gettext-runtime/intl/gnulib-lib"));
lib.addCSourceFiles(.{
lib.root_module.addIncludePath(upstream.path("gettext-runtime/intl"));
lib.root_module.addIncludePath(upstream.path("gettext-runtime/intl/gnulib-lib"));
lib.root_module.addCSourceFiles(.{
.root = upstream.path("gettext-runtime/intl"),
.files = srcs,
.flags = flags.items,

View File

@ -9,18 +9,10 @@ pub fn build(b: *std.Build) !void {
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
}),
.linkage = .static,
});
lib.linkLibC();
if (target.result.os.tag == .linux) {
lib.linkSystemLibrary("m");
}
if (target.result.os.tag.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, lib);
}
// For dynamic linking, we prefer dynamic linking and to search by
// mode first. Mode first will search all paths for a dynamic library
// before falling back to static.
@ -28,20 +20,27 @@ pub fn build(b: *std.Build) !void {
.preferred_link_mode = .dynamic,
.search_strategy = .mode_first,
};
if (target.result.os.tag == .linux) {
lib.root_module.linkSystemLibrary("m", dynamic_link_opts);
}
if (target.result.os.tag.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, lib);
}
if (b.systemIntegrationOption("zlib", .{})) {
lib.linkSystemLibrary2("zlib", dynamic_link_opts);
lib.root_module.linkSystemLibrary("zlib", dynamic_link_opts);
} else {
if (b.lazyDependency(
"zlib",
.{ .target = target, .optimize = optimize },
)) |zlib_dep| {
lib.linkLibrary(zlib_dep.artifact("z"));
lib.addIncludePath(b.path(""));
lib.root_module.linkLibrary(zlib_dep.artifact("z"));
lib.root_module.addIncludePath(b.path(""));
}
if (b.lazyDependency("libpng", .{})) |upstream| {
lib.addIncludePath(upstream.path(""));
lib.root_module.addIncludePath(upstream.path(""));
}
}
@ -61,7 +60,7 @@ pub fn build(b: *std.Build) !void {
});
}
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = srcs,
.flags = flags.items,

View File

@ -11,18 +11,25 @@ pub fn build(b: *std.Build) !void {
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
}),
.linkage = .static,
});
lib.linkLibC();
if (upstream_) |upstream| lib.addIncludePath(upstream.path("include"));
lib.addIncludePath(b.path("override/include"));
if (upstream_) |upstream| lib.root_module.addIncludePath(upstream.path("include"));
lib.root_module.addIncludePath(b.path("override/include"));
if (target.result.os.tag == .windows) {
lib.addIncludePath(b.path("override/config/win32"));
lib.linkSystemLibrary("ws2_32");
lib.root_module.addIncludePath(b.path("override/config/win32"));
// For dynamic linking, we prefer dynamic linking and to search by
// mode first. Mode first will search all paths for a dynamic library
// before falling back to static.
const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
.preferred_link_mode = .dynamic,
.search_strategy = .mode_first,
};
lib.root_module.linkSystemLibrary("ws2_32", dynamic_link_opts);
} else {
lib.addIncludePath(b.path("override/config/posix"));
lib.root_module.addIncludePath(b.path("override/config/posix"));
}
var flags: std.ArrayList([]const u8) = .empty;
@ -98,7 +105,7 @@ pub fn build(b: *std.Build) !void {
}
if (upstream_) |upstream| {
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = srcs,
.flags = flags.items,

View File

@ -21,21 +21,21 @@ pub fn build(b: *std.Build) !void {
.linkage = .static,
});
lib.addCSourceFile(.{
lib.root_module.addCSourceFile(.{
.file = b.path("os/zig_macos.c"),
.flags = &.{"-std=c99"},
});
lib.addCSourceFile(.{
lib.root_module.addCSourceFile(.{
.file = b.path("text/ext.c"),
});
lib.linkFramework("CoreFoundation");
lib.linkFramework("CoreGraphics");
lib.linkFramework("CoreText");
lib.linkFramework("CoreVideo");
lib.linkFramework("QuartzCore");
lib.linkFramework("IOSurface");
lib.root_module.linkFramework("CoreFoundation", .{});
lib.root_module.linkFramework("CoreGraphics", .{});
lib.root_module.linkFramework("CoreText", .{});
lib.root_module.linkFramework("CoreVideo", .{});
lib.root_module.linkFramework("QuartzCore", .{});
lib.root_module.linkFramework("IOSurface", .{});
if (target.result.os.tag == .macos) {
lib.linkFramework("Carbon");
lib.root_module.linkFramework("Carbon", .{});
module.linkFramework("Carbon", .{});
}
@ -63,7 +63,7 @@ pub fn build(b: *std.Build) !void {
if (target.result.os.tag.isDarwin()) {
try apple_sdk.addPaths(b, test_exe);
}
test_exe.linkLibrary(lib);
test_exe.root_module.linkLibrary(lib);
var it = module.import_table.iterator();
while (it.next()) |entry| {

View File

@ -41,7 +41,7 @@ pub fn build(b: *std.Build) !void {
module.linkSystemLibrary("oniguruma", dynamic_link_opts);
if (test_exe) |exe| {
exe.linkSystemLibrary2("oniguruma", dynamic_link_opts);
exe.root_module.linkSystemLibrary("oniguruma", dynamic_link_opts);
}
} else {
const lib = try buildLib(b, module, .{
@ -50,7 +50,7 @@ pub fn build(b: *std.Build) !void {
});
if (test_exe) |exe| {
exe.linkLibrary(lib);
exe.root_module.linkLibrary(lib);
}
}
}
@ -64,12 +64,12 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
}),
.linkage = .static,
});
const t = target.result;
const is_windows = t.os.tag == .windows;
lib.linkLibC();
if (target.result.os.tag.isDarwin()) {
const apple_sdk = @import("apple_sdk");
@ -77,10 +77,10 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
}
if (b.lazyDependency("oniguruma", .{})) |upstream| {
lib.addIncludePath(upstream.path("src"));
lib.root_module.addIncludePath(upstream.path("src"));
module.addIncludePath(upstream.path("src"));
lib.addConfigHeader(b.addConfigHeader(.{
lib.root_module.addConfigHeader(b.addConfigHeader(.{
.style = .{ .cmake = upstream.path("src/config.h.cmake.in") },
}, .{
.PACKAGE = "oniguruma",
@ -109,7 +109,7 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
"-fno-sanitize-trap=undefined",
});
}
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.flags = flags.items,
.files = &.{

View File

@ -17,10 +17,10 @@ pub fn build(b: *std.Build) !void {
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
}),
.linkage = .static,
});
lib.linkLibC();
if (target.result.os.tag.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, lib);
@ -46,9 +46,9 @@ pub fn build(b: *std.Build) !void {
if (b.lazyDependency("sentry", .{})) |upstream| {
module.addIncludePath(upstream.path("include"));
lib.addIncludePath(upstream.path("include"));
lib.addIncludePath(upstream.path("src"));
lib.addCSourceFiles(.{
lib.root_module.addIncludePath(upstream.path("include"));
lib.root_module.addIncludePath(upstream.path("src"));
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = srcs,
.flags = flags.items,
@ -56,7 +56,7 @@ pub fn build(b: *std.Build) !void {
// Linux-only
if (target.result.os.tag == .linux) {
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = &.{
"vendor/stb_sprintf.c",
@ -67,7 +67,7 @@ pub fn build(b: *std.Build) !void {
// Symbolizer + Unwinder
if (target.result.os.tag == .windows) {
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = &.{
"src/sentry_windows_dbghelp.c",
@ -78,7 +78,7 @@ pub fn build(b: *std.Build) !void {
.flags = flags.items,
});
} else {
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = &.{
"src/sentry_unix_pageallocator.c",
@ -92,7 +92,7 @@ pub fn build(b: *std.Build) !void {
// Module finder
switch (target.result.os.tag) {
.windows => lib.addCSourceFiles(.{
.windows => lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = &.{
"src/modulefinder/sentry_modulefinder_windows.c",
@ -100,7 +100,7 @@ pub fn build(b: *std.Build) !void {
.flags = flags.items,
}),
.macos, .ios => lib.addCSourceFiles(.{
.macos, .ios => lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = &.{
"src/modulefinder/sentry_modulefinder_apple.c",
@ -108,7 +108,7 @@ pub fn build(b: *std.Build) !void {
.flags = flags.items,
}),
.linux => lib.addCSourceFiles(.{
.linux => lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = &.{
"src/modulefinder/sentry_modulefinder_linux.c",
@ -126,7 +126,7 @@ pub fn build(b: *std.Build) !void {
// Transport
switch (transport) {
.curl => lib.addCSourceFiles(.{
.curl => lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = &.{
"src/transports/sentry_transport_curl.c",
@ -134,7 +134,7 @@ pub fn build(b: *std.Build) !void {
.flags = flags.items,
}),
.winhttp => lib.addCSourceFiles(.{
.winhttp => lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = &.{
"src/transports/sentry_transport_winhttp.c",
@ -142,7 +142,7 @@ pub fn build(b: *std.Build) !void {
.flags = flags.items,
}),
.none => lib.addCSourceFiles(.{
.none => lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = &.{
"src/transports/sentry_transport_none.c",
@ -153,7 +153,7 @@ pub fn build(b: *std.Build) !void {
// Backend
switch (backend) {
.crashpad => lib.addCSourceFiles(.{
.crashpad => lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = &.{
"src/backends/sentry_backend_crashpad.cpp",
@ -162,7 +162,7 @@ pub fn build(b: *std.Build) !void {
}),
.breakpad => {
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = &.{
"src/backends/sentry_backend_breakpad.cpp",
@ -174,15 +174,15 @@ pub fn build(b: *std.Build) !void {
.target = target,
.optimize = optimize,
})) |breakpad_dep| {
lib.linkLibrary(breakpad_dep.artifact("breakpad"));
lib.root_module.linkLibrary(breakpad_dep.artifact("breakpad"));
// We need to add this because Sentry includes some breakpad
// headers that include this vendored file...
lib.addIncludePath(breakpad_dep.path("vendor"));
lib.root_module.addIncludePath(breakpad_dep.path("vendor"));
}
},
.inproc => lib.addCSourceFiles(.{
.inproc => lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = &.{
"src/backends/sentry_backend_inproc.c",
@ -190,7 +190,7 @@ pub fn build(b: *std.Build) !void {
.flags = flags.items,
}),
.none => lib.addCSourceFiles(.{
.none => lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = &.{
"src/backends/sentry_backend_none.c",

View File

@ -10,26 +10,21 @@ pub fn build(b: *std.Build) !void {
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
// We link libcpp even with no_libcxx because simdutf requires
// libc++ headers at build time. But it doesn't require libc++ at
// runtime. For Ghostty itself, we have CI tests to verify this.
//
// On MSVC, we must not use linkLibCpp because Zig unconditionally
// passes -nostdinc++ and then adds its bundled libc++/libc++abi
// include paths, which conflict with MSVC's own C++ runtime
// headers. The MSVC SDK include directories (added via linkLibC)
// contain both C and C++ headers, so linkLibCpp is not needed.
.link_libcpp = target.result.abi != .msvc,
}),
.linkage = .static,
});
lib.addIncludePath(b.path("vendor"));
lib.linkLibC();
libcpp: {
if (target.result.abi == .msvc) {
// On MSVC, we must not use linkLibCpp because Zig unconditionally
// passes -nostdinc++ and then adds its bundled libc++/libc++abi
// include paths, which conflict with MSVC's own C++ runtime headers.
// The MSVC SDK include directories (added via linkLibC) contain
// both C and C++ headers, so linkLibCpp is not needed.
break :libcpp;
}
// We link libcpp even with no_libcxx because simdutf requires
// libc++ headers at build time. But it doesn't require libc++
// at runtime. For Ghostty itself, we have CI tests to verify this.
lib.linkLibCpp();
}
lib.root_module.addIncludePath(b.path("vendor"));
if (target.result.os.tag.isDarwin()) {
const apple_sdk = @import("apple_sdk");
@ -74,7 +69,7 @@ pub fn build(b: *std.Build) !void {
try flags.append(b.allocator, "-fPIC");
}
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.flags = flags.items,
.files = &.{
"vendor/simdutf.cpp",

View File

@ -34,12 +34,12 @@ pub fn build(b: *std.Build) !void {
if (b.systemIntegrationOption("spirv-cross", .{})) {
module.linkSystemLibrary("spirv-cross-c-shared", dynamic_link_opts);
if (test_exe) |exe| {
exe.linkSystemLibrary2("spirv-cross-c-shared", dynamic_link_opts);
exe.root_module.linkSystemLibrary("spirv-cross-c-shared", dynamic_link_opts);
}
} else {
const lib = try buildSpirvCross(b, module, target, optimize);
b.installArtifact(lib);
if (test_exe) |exe| exe.linkLibrary(lib);
if (test_exe) |exe| exe.root_module.linkLibrary(lib);
}
}
@ -54,18 +54,16 @@ fn buildSpirvCross(
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
// On MSVC, we must not use linkLibCpp because Zig unconditionally
// passes -nostdinc++ and then adds its bundled libc++/libc++abi
// include paths, which conflict with MSVC's own C++ runtime
// headers. The MSVC SDK include directories (added via linkLibC)
// contain both C and C++ headers, so linkLibCpp is not needed.
.link_libcpp = target.result.abi != .msvc,
}),
.linkage = .static,
});
lib.linkLibC();
// On MSVC, we must not use linkLibCpp because Zig unconditionally
// passes -nostdinc++ and then adds its bundled libc++/libc++abi
// include paths, which conflict with MSVC's own C++ runtime headers.
// The MSVC SDK include directories (added via linkLibC) contain
// both C and C++ headers, so linkLibCpp is not needed.
if (target.result.abi != .msvc) {
lib.linkLibCpp();
}
if (target.result.os.tag.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, lib);
@ -86,9 +84,9 @@ fn buildSpirvCross(
}
if (b.lazyDependency("spirv_cross", .{})) |upstream| {
lib.addIncludePath(upstream.path(""));
lib.root_module.addIncludePath(upstream.path(""));
module.addIncludePath(upstream.path(""));
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.flags = flags.items,
.files = &.{

View File

@ -15,7 +15,6 @@ pub fn build(b: *std.Build) !void {
.name = "test",
.root_module = module,
});
unit_tests.linkLibC();
var flags: std.ArrayList([]const u8) = .empty;
defer flags.deinit(b.allocator);

View File

@ -9,17 +9,17 @@ pub fn build(b: *std.Build) !void {
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
}),
.linkage = .static,
});
lib.linkLibC();
if (target.result.os.tag.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, lib);
}
if (b.lazyDependency("zlib", .{})) |upstream| {
lib.addIncludePath(upstream.path(""));
lib.root_module.addIncludePath(upstream.path(""));
lib.installHeadersDirectory(
upstream.path(""),
"",
@ -48,7 +48,7 @@ pub fn build(b: *std.Build) !void {
"-D_CRT_NONSTDC_NO_DEPRECATE",
});
}
lib.addCSourceFiles(.{
lib.root_module.addCSourceFiles(.{
.root = upstream.path(""),
.files = srcs,
.flags = flags.items,

View File

@ -56,7 +56,7 @@ font_grid_set: font.SharedGridSet,
// Used to rate limit desktop notifications. Some platforms (notably macOS) will
// run out of resources if desktop notifications are sent too fast and the OS
// will kill Ghostty.
last_notification_time: ?std.time.Instant = null,
last_notification_time: ?std.Io.Timestamp = null,
last_notification_digest: u64 = 0,
/// The conditional state of the configuration. See the equivalent field
@ -95,7 +95,7 @@ pub fn init(
self.* = .{
.alloc = alloc,
.surfaces = .{},
.surfaces = .empty,
.mailbox = .{},
.font_grid_set = font_grid_set,
.config_conditional_state = .{},

View File

@ -26,11 +26,14 @@ const TempDir = internal_os.TempDir;
const mem = std.mem;
const linux = std.os.linux;
const posix = std.posix;
const compat_dir = @import("lib/compat/dir.zig");
const compat_exec = @import("lib/compat/exec.zig");
const compat_process = @import("lib/compat/process.zig");
const debug = std.debug;
const testing = std.testing;
const Allocator = std.mem.Allocator;
const File = std.fs.File;
const EnvMap = std.process.EnvMap;
const File = std.Io.File;
const EnvMap = std.process.Environ.Map;
const apprt = @import("apprt.zig");
/// Function prototype for a function executed /in the child process/ after the
@ -65,7 +68,7 @@ env: ?*const EnvMap = null,
/// Working directory to change to in the child process. If not set, the
/// working directory of the calling process is preserved.
cwd: ?[]const u8 = null,
cwd: ?[:0]const u8 = null,
/// The file handle to set for stdin/out/err. If this isn't set, we do
/// nothing explicitly so it is up to the behavior of the operating system.
@ -106,7 +109,7 @@ pseudo_console: if (builtin.os.tag == .windows) ?windows.exp.HPCON else void =
data: ?*anyopaque = null,
/// Process ID is set after start is called.
pid: ?posix.pid_t = null,
pid: ?posix.system.pid_t = null,
/// The various methods a process may exit.
pub const Exit = if (builtin.os.tag == .windows) union(enum) {
@ -128,9 +131,9 @@ pub const Exit = if (builtin.os.tag == .windows) union(enum) {
return if (posix.W.IFEXITED(status))
Exit{ .Exited = posix.W.EXITSTATUS(status) }
else if (posix.W.IFSIGNALED(status))
Exit{ .Signal = posix.W.TERMSIG(status) }
Exit{ .Signal = @intFromEnum(posix.W.TERMSIG(status)) }
else if (posix.W.IFSTOPPED(status))
Exit{ .Stopped = posix.W.STOPSIG(status) }
Exit{ .Stopped = @intFromEnum(posix.W.STOPSIG(status)) }
else
Exit{ .Unknown = status };
}
@ -186,7 +189,7 @@ fn startPosix(self: *Command, arena: Allocator) !void {
@compileError("missing env vars");
// Fork.
const pid = try posix.fork();
const pid = try compat_process.fork();
if (pid != 0) {
// Parent, return immediately.
@ -206,7 +209,7 @@ fn startPosix(self: *Command, arena: Allocator) !void {
return error.ExecFailedInChild;
// Setup our working directory
if (self.cwd) |cwd| posix.chdir(cwd) catch {
if (self.cwd) |cwd| compat_dir.chdir(cwd) catch {
// This can fail if we don't have permission to go to
// this directory or if due to race conditions it doesn't
// exist or any various other reasons. We don't want to
@ -219,20 +222,20 @@ fn startPosix(self: *Command, arena: Allocator) !void {
global_state.rlimits.restore();
// If there are pre exec callbacks, call them now.
if (self.os_pre_exec) |f| if (f(self)) |exitcode| posix.exit(exitcode);
if (self.rt_pre_exec) |f| if (f(self)) |exitcode| posix.exit(exitcode);
if (self.os_pre_exec) |f| if (f(self)) |exitcode| posix.system.exit(exitcode);
if (self.rt_pre_exec) |f| if (f(self)) |exitcode| posix.system.exit(exitcode);
// Finally, replace our process.
// Note: we must use the "p"-variant of exec here because we
// do not guarantee our command is looked up already in the path.
const err = posix.execvpeZ(self.path, argsZ, envp);
const err = compat_exec.execvpeZ(self.path, argsZ, envp);
// If we are executing this code, the exec failed. We're in the
// child process so there isn't much we can do. We try to output
// something reasonable. Its important to note we MUST NOT return
// any other error condition from here on out.
var stderr_buf: [1024]u8 = undefined;
var stderr_writer = std.fs.File.stderr().writer(&stderr_buf);
var stderr_writer = std.Io.File.stderr().writer(std.Io.Threaded.global_single_threaded.io(), &stderr_buf);
const stderr = &stderr_writer.interface;
switch (err) {
error.FileNotFound => stderr.print(
@ -283,7 +286,7 @@ fn startWindows(self: *Command, arena: Allocator) !void {
.creation = windows.OPEN_EXISTING,
},
) else null;
defer if (null_fd) |fd| posix.close(fd);
defer if (null_fd) |fd| posix.system.close(fd);
// TODO: In the case of having FDs instead of pty, need to set up
// attributes such that the child process only inherits these handles,
@ -395,9 +398,9 @@ fn setupFd(src: File.Handle, target: i32) !void {
.freebsd, .ios, .macos => {
// Mac doesn't support dup3 so we use dup2. We purposely clear
// CLO_ON_EXEC for this fd.
const flags = try posix.fcntl(src, posix.F.GETFD, 0);
const flags = try posix.system.fcntl(src, posix.F.GETFD, 0);
if (flags & posix.FD_CLOEXEC != 0) {
_ = try posix.fcntl(src, posix.F.SETFD, flags & ~@as(u32, posix.FD_CLOEXEC));
_ = try posix.system.fcntl(src, posix.F.SETFD, flags & ~@as(u32, posix.FD_CLOEXEC));
}
try posix.dup2(src, target);
@ -425,7 +428,7 @@ pub fn wait(self: Command, block: bool) !Exit {
return .{ .Exited = exit_code };
}
const res = if (block) posix.waitpid(self.pid.?, 0) else res: {
const res = if (block) compat_process.waitpid(self.pid.?, 0) else res: {
// We specify NOHANG because its not our fault if the process we launch
// for the tty doesn't properly waitpid its children. We don't want
// to hang the terminal over it.
@ -434,7 +437,7 @@ pub fn wait(self: Command, block: bool) !Exit {
// wait call has not been performed, so we need to keep trying until we get
// a non-zero pid back, otherwise we end up with zombie processes.
while (true) {
const res = posix.waitpid(self.pid.?, std.c.W.NOHANG);
const res = compat_process.waitpid(self.pid.?, std.c.W.NOHANG);
if (res.pid != 0) break :res res;
}
};
@ -587,7 +590,7 @@ test "Command: os pre exec 1" {
fn do(_: *Command) ?u8 {
// This runs in the child, so we can exit and it won't
// kill the test runner.
posix.exit(42);
posix.system.exit(42);
}
}).do,
.rt_pre_exec = null,
@ -638,7 +641,7 @@ test "Command: rt pre exec 1" {
fn do(_: *Command) ?u8 {
// This runs in the child, so we can exit and it won't
// kill the test runner.
posix.exit(42);
posix.system.exit(42);
}
}).do,
.rt_post_fork = null,
@ -697,8 +700,8 @@ test "Command: rt post fork 1" {
try testing.expectError(error.PostForkError, cmd.testingStart());
}
fn createTestStdout(dir: std.fs.Dir) !File {
const file = try dir.createFile("stdout.txt", .{ .read = true });
fn createTestStdout(io: std.Io, dir: std.Io.Dir) !File {
const file = try dir.createFile(io, "stdout.txt", .{ .read = true });
if (builtin.os.tag == .windows) {
try windows.SetHandleInformation(
file.handle,
@ -710,8 +713,8 @@ fn createTestStdout(dir: std.fs.Dir) !File {
return file;
}
fn createTestStderr(dir: std.fs.Dir) !File {
const file = try dir.createFile("stderr.txt", .{ .read = true });
fn createTestStderr(io: std.Io, dir: std.Io.Dir) !File {
const file = try dir.createFile(io, "stderr.txt", .{ .read = true });
if (builtin.os.tag == .windows) {
try windows.SetHandleInformation(
file.handle,
@ -726,8 +729,8 @@ fn createTestStderr(dir: std.fs.Dir) !File {
test "Command: redirect stdout to file" {
var td = try TempDir.init();
defer td.deinit();
var stdout = try createTestStdout(td.dir);
defer stdout.close();
var stdout = try createTestStdout(testing.io, td.dir);
defer stdout.close(testing.io);
var cmd: Command = if (builtin.os.tag == .windows) .{
.path = "C:\\Windows\\System32\\whoami.exe",
@ -756,8 +759,13 @@ test "Command: redirect stdout to file" {
try testing.expectEqual(@as(u32, 0), @as(u32, exit.Exited));
// Read our stdout
try stdout.seekTo(0);
const contents = try stdout.readToEndAlloc(testing.allocator, 1024 * 128);
const contents = contents: {
const size = (try stdout.stat(testing.io)).size;
const data = try testing.allocator.alloc(u8, size);
errdefer testing.allocator.free(data);
try testing.expectEqual(size, try stdout.readPositionalAll(testing.io, data, 0));
break :contents data;
};
defer testing.allocator.free(contents);
try testing.expect(contents.len > 0);
}
@ -765,8 +773,8 @@ test "Command: redirect stdout to file" {
test "Command: custom env vars" {
var td = try TempDir.init();
defer td.deinit();
var stdout = try createTestStdout(td.dir);
defer stdout.close();
var stdout = try createTestStdout(testing.io, td.dir);
defer stdout.close(testing.io);
var env = EnvMap.init(testing.allocator);
defer env.deinit();
@ -801,8 +809,13 @@ test "Command: custom env vars" {
try testing.expect(exit.Exited == 0);
// Read our stdout
try stdout.seekTo(0);
const contents = try stdout.readToEndAlloc(testing.allocator, 4096);
const contents = contents: {
const size = (try stdout.stat(testing.io)).size;
const data = try testing.allocator.alloc(u8, size);
errdefer testing.allocator.free(data);
try testing.expectEqual(size, try stdout.readPositionalAll(testing.io, data, 0));
break :contents data;
};
defer testing.allocator.free(contents);
if (builtin.os.tag == .windows) {
@ -815,8 +828,8 @@ test "Command: custom env vars" {
test "Command: custom working directory" {
var td = try TempDir.init();
defer td.deinit();
var stdout = try createTestStdout(td.dir);
defer stdout.close();
var stdout = try createTestStdout(testing.io, td.dir);
defer stdout.close(testing.io);
var cmd: Command = if (builtin.os.tag == .windows) .{
.path = "C:\\Windows\\System32\\cmd.exe",
@ -847,8 +860,13 @@ test "Command: custom working directory" {
try testing.expect(exit.Exited == 0);
// Read our stdout
try stdout.seekTo(0);
const contents = try stdout.readToEndAlloc(testing.allocator, 4096);
const contents = contents: {
const size = (try stdout.stat(testing.io)).size;
const data = try testing.allocator.alloc(u8, size);
errdefer testing.allocator.free(data);
try testing.expectEqual(size, try stdout.readPositionalAll(testing.io, data, 0));
break :contents data;
};
defer testing.allocator.free(contents);
if (builtin.os.tag == .windows) {
@ -872,10 +890,10 @@ test "Command: posix fork handles execveZ failure" {
}
var td = try TempDir.init();
defer td.deinit();
var stdout = try createTestStdout(td.dir);
defer stdout.close();
var stderr = try createTestStderr(td.dir);
defer stderr.close();
var stdout = try createTestStdout(testing.io, td.dir);
defer stdout.close(testing.io);
var stderr = try createTestStderr(testing.io, td.dir);
defer stderr.close(testing.io);
var cmd: Command = .{
.path = "/not/a/binary",
@ -904,7 +922,7 @@ fn testingStart(self: *Command) !void {
self.start(testing.allocator) catch |err| {
if (err == error.ExecFailedInChild) {
// I am a child process, I must not get confused and continue running the rest of the test suite.
posix.exit(1);
posix.system.exit(1);
}
return err;
};

View File

@ -419,8 +419,11 @@ pub const Action = union(Key) {
/// Sync with: ghostty_action_u
pub const CValue = cvalue: {
const key_fields = @typeInfo(Key).@"enum".fields;
var union_fields: [key_fields.len]std.builtin.Type.UnionField = undefined;
for (key_fields, 0..) |field, i| {
var names: [key_fields.len][]const u8 = undefined;
var types: [key_fields.len]type = undefined;
var attrs: [key_fields.len]std.builtin.Type.UnionField.Attributes = undefined;
for (key_fields, &names, &types, &attrs) |field, *name, *ty, *attr| {
const action = @unionInit(Action, field.name, undefined);
const Type = t: {
const Type = @TypeOf(@field(action, field.name));
@ -429,19 +432,12 @@ pub const Action = union(Key) {
break :t Type;
};
union_fields[i] = .{
.name = field.name,
.type = Type,
.alignment = @alignOf(Type),
};
name.* = field.name;
ty.* = Type;
attr.* = .{ .@"align" = @alignOf(Type) };
}
break :cvalue @Type(.{ .@"union" = .{
.layout = .@"extern",
.tag_type = null,
.fields = &union_fields,
.decls = &.{},
} });
break :cvalue @Union(.@"extern", null, &names, &types, &attrs);
};
/// Sync with: ghostty_action_s
@ -1003,5 +999,5 @@ pub const SearchSelected = struct {
};
test {
_ = std.testing.refAllDeclsRecursive(@This());
_ = std.testing.refAllDecls(@This());
}

View File

@ -1,22 +1,16 @@
const std = @import("std");
// Until the gobject bindings are built at the same time we are building
// Ghostty, we need to import `adwaita.h` directly to ensure that the version
// macros match the version of `libadwaita` that we are building/linking
// against.
const c = @cImport({
@cInclude("adwaita.h");
});
const adw = @import("adw");
const log = std.log.scoped(.gtk);
pub const comptime_version: std.SemanticVersion = .{
.major = c.ADW_MAJOR_VERSION,
.minor = c.ADW_MINOR_VERSION,
.patch = c.ADW_MICRO_VERSION,
};
// Until the gobject bindings are built at the same time we are building
// Ghostty, we need to ensure that the version macros match the version of
// `libadwaita` that we are building/linking against.
//
// We pull this in through pkg-config, see src/build/SharedDeps.zig for more
// details.
pub const comptime_version = @import("adw_semver").version;
pub fn getRuntimeVersion() std.SemanticVersion {
return .{
@ -89,14 +83,14 @@ test "versionAtLeast" {
const funs = &.{ atLeast, runtimeAtLeast };
inline for (funs) |fun| {
try testing.expect(fun(c.ADW_MAJOR_VERSION, c.ADW_MINOR_VERSION, c.ADW_MICRO_VERSION));
try testing.expect(!fun(c.ADW_MAJOR_VERSION, c.ADW_MINOR_VERSION, c.ADW_MICRO_VERSION + 1));
try testing.expect(!fun(c.ADW_MAJOR_VERSION, c.ADW_MINOR_VERSION + 1, c.ADW_MICRO_VERSION));
try testing.expect(!fun(c.ADW_MAJOR_VERSION + 1, c.ADW_MINOR_VERSION, c.ADW_MICRO_VERSION));
try testing.expect(fun(c.ADW_MAJOR_VERSION - 1, c.ADW_MINOR_VERSION, c.ADW_MICRO_VERSION));
try testing.expect(fun(c.ADW_MAJOR_VERSION - 1, c.ADW_MINOR_VERSION + 1, c.ADW_MICRO_VERSION));
try testing.expect(fun(c.ADW_MAJOR_VERSION - 1, c.ADW_MINOR_VERSION, c.ADW_MICRO_VERSION + 1));
try testing.expect(fun(c.ADW_MAJOR_VERSION, c.ADW_MINOR_VERSION - 1, c.ADW_MICRO_VERSION + 1));
try testing.expect(fun(comptime_version.major, comptime_version.minor, comptime_version.patch));
try testing.expect(!fun(comptime_version.major, comptime_version.minor, comptime_version.patch + 1));
try testing.expect(!fun(comptime_version.major, comptime_version.minor + 1, comptime_version.patch));
try testing.expect(!fun(comptime_version.major + 1, comptime_version.minor, comptime_version.patch));
try testing.expect(fun(comptime_version.major - 1, comptime_version.minor, comptime_version.patch));
try testing.expect(fun(comptime_version.major - 1, comptime_version.minor + 1, comptime_version.patch));
try testing.expect(fun(comptime_version.major - 1, comptime_version.minor, comptime_version.patch + 1));
try testing.expect(fun(comptime_version.major, comptime_version.minor - 1, comptime_version.patch + 1));
}
}

View File

@ -7,10 +7,6 @@
const std = @import("std");
pub const c = @cImport({
@cInclude("adwaita.h");
});
pub const blueprint_compiler_help =
\\
\\When building from a Git checkout, Ghostty requires
@ -25,24 +21,21 @@ pub const blueprint_compiler_help =
\\more information on the recommended build instructions.
;
const adwaita_version = std.SemanticVersion{
.major = c.ADW_MAJOR_VERSION,
.minor = c.ADW_MINOR_VERSION,
.patch = c.ADW_MICRO_VERSION,
};
const adwaita_version = @import("adw_semver").version;
const required_blueprint_version = std.SemanticVersion{
.major = 0,
.minor = 16,
.patch = 0,
};
pub fn main() !void {
pub fn main(init: std.process.Init) !void {
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
defer _ = debug_allocator.deinit();
const alloc = debug_allocator.allocator();
// Get our args
var it = try std.process.argsWithAllocator(alloc);
var it = try init.minimal.args.iterateAllocator(alloc);
defer it.deinit();
_ = it.next(); // Skip argv0
const arg_major = it.next() orelse return error.NoMajorVersion;
@ -63,51 +56,37 @@ pub fn main() !void {
\\compile this blueprint. Please install it, ensure that it is
\\available on your PATH, and then retry building Ghostty.
, .{required_adwaita_version});
std.posix.exit(1);
std.process.exit(1);
}
// Version checks
{
var stdout: std.ArrayListUnmanaged(u8) = .empty;
defer stdout.deinit(alloc);
var stderr: std.ArrayListUnmanaged(u8) = .empty;
defer stderr.deinit(alloc);
var blueprint_compiler = std.process.Child.init(
&.{
"blueprint-compiler",
"--version",
},
alloc,
);
blueprint_compiler.stdout_behavior = .Pipe;
blueprint_compiler.stderr_behavior = .Pipe;
try blueprint_compiler.spawn();
try blueprint_compiler.collectOutput(
alloc,
&stdout,
&stderr,
std.math.maxInt(u16),
);
const term = blueprint_compiler.wait() catch |err| switch (err) {
const blueprint_compiler = std.process.run(alloc, init.io, .{
.argv = &.{ "blueprint-compiler", "--version" },
}) catch |err| switch (err) {
error.FileNotFound => {
std.debug.print(
\\`blueprint-compiler` not found.
++ blueprint_compiler_help,
.{required_blueprint_version},
);
std.posix.exit(1);
std.process.exit(1);
},
else => return err,
};
switch (term) {
.Exited => |rc| if (rc != 0) std.process.exit(1),
defer {
alloc.free(blueprint_compiler.stdout);
alloc.free(blueprint_compiler.stderr);
}
switch (blueprint_compiler.term) {
.exited => |rc| if (rc != 0) std.process.exit(1),
else => std.process.exit(1),
}
const version = try std.SemanticVersion.parse(std.mem.trim(
u8,
stdout.items,
blueprint_compiler.stdout,
&std.ascii.whitespace,
));
if (version.order(required_blueprint_version) == .lt) {
@ -116,57 +95,45 @@ pub fn main() !void {
++ blueprint_compiler_help,
.{required_blueprint_version},
);
std.posix.exit(1);
std.process.exit(1);
}
}
// Compilation
{
var stdout: std.ArrayListUnmanaged(u8) = .empty;
defer stdout.deinit(alloc);
var stderr: std.ArrayListUnmanaged(u8) = .empty;
defer stderr.deinit(alloc);
var blueprint_compiler = std.process.Child.init(
&.{
const blueprint_compiler = std.process.run(alloc, init.io, .{
.argv = &.{
"blueprint-compiler",
"compile",
"--output",
output,
input,
},
alloc,
);
blueprint_compiler.stdout_behavior = .Pipe;
blueprint_compiler.stderr_behavior = .Pipe;
try blueprint_compiler.spawn();
try blueprint_compiler.collectOutput(
alloc,
&stdout,
&stderr,
std.math.maxInt(u16),
);
const term = blueprint_compiler.wait() catch |err| switch (err) {
}) catch |err| switch (err) {
error.FileNotFound => {
std.debug.print(
\\`blueprint-compiler` not found.
++ blueprint_compiler_help,
.{required_blueprint_version},
);
std.posix.exit(1);
std.process.exit(1);
},
else => return err,
};
defer {
alloc.free(blueprint_compiler.stdout);
alloc.free(blueprint_compiler.stderr);
}
switch (term) {
.Exited => |rc| {
switch (blueprint_compiler.term) {
.exited => |rc| {
if (rc != 0) {
std.debug.print("{s}", .{stderr.items});
std.debug.print("{s}", .{blueprint_compiler.stderr});
std.process.exit(1);
}
},
else => {
std.debug.print("{s}", .{stderr.items});
std.debug.print("{s}", .{blueprint_compiler.stderr});
std.process.exit(1);
},
}

View File

@ -122,18 +122,17 @@ pub fn blueprint(comptime bp: Blueprint) [:0]const u8 {
}
}
pub fn main() !void {
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
defer _ = debug_allocator.deinit();
const alloc = debug_allocator.allocator();
pub fn main(init: std.process.Init) !void {
const alloc = init.arena.allocator();
// Collect the UI files that are passed in as arguments.
var ui_files: std.ArrayListUnmanaged([]const u8) = .empty;
var ui_files: std.ArrayList([]const u8) = .empty;
defer {
for (ui_files.items) |item| alloc.free(item);
ui_files.deinit(alloc);
}
var it = try std.process.argsWithAllocator(alloc);
var it = try init.minimal.args.iterateAllocator(alloc);
defer it.deinit();
while (it.next()) |arg| {
if (!std.mem.endsWith(u8, arg, ".ui")) continue;
@ -144,7 +143,7 @@ pub fn main() !void {
}
var buf: [4096]u8 = undefined;
var stdout = std.fs.File.stdout().writer(&buf);
var stdout = std.Io.File.stdout().writer(init.io, &buf);
const writer = &stdout.interface;
try writer.writeAll(
\\<?xml version="1.0" encoding="UTF-8"?>
@ -152,8 +151,8 @@ pub fn main() !void {
\\
);
try genRoot(writer);
try genIcons(writer);
try genRoot(init.io, writer);
try genIcons(init.io, writer);
try genUi(alloc, writer, &ui_files);
try writer.writeAll(
@ -167,19 +166,19 @@ pub fn main() !void {
/// Generate the icon resources. This works by looking up all the icons
/// specified by `icon_sizes` in `images/icons/`. They are asserted to exist
/// by trying to access the file.
fn genIcons(writer: *std.Io.Writer) !void {
fn genIcons(io: std.Io, writer: *std.Io.Writer) !void {
try writer.print(
\\ <gresource prefix="{s}/icons">
\\
, .{build_info.resource_path});
const cwd = std.fs.cwd();
const cwd: std.Io.Dir = .cwd();
inline for (icon_sizes) |size| {
// 1x
{
const alias = std.fmt.comptimePrint("{d}x{d}", .{ size, size });
const source = std.fmt.comptimePrint("images/gnome/{d}.png", .{size});
try cwd.access(source, .{});
try cwd.access(io, source, .{});
try writer.print(
\\ <file alias="{s}/apps/{s}.png">{s}</file>
\\
@ -192,7 +191,7 @@ fn genIcons(writer: *std.Io.Writer) !void {
{
const alias = std.fmt.comptimePrint("{d}x{d}@2", .{ size, size });
const source = std.fmt.comptimePrint("images/gnome/{d}.png", .{size * 2});
try cwd.access(source, .{});
try cwd.access(io, source, .{});
try writer.print(
\\ <file alias="{s}/apps/{s}.png">{s}</file>
\\
@ -209,19 +208,19 @@ fn genIcons(writer: *std.Io.Writer) !void {
}
/// Generate the resources at the root prefix.
fn genRoot(writer: *std.Io.Writer) !void {
fn genRoot(io: std.Io, writer: *std.Io.Writer) !void {
try writer.print(
\\ <gresource prefix="{s}">
\\
, .{build_info.resource_path});
const cwd = std.fs.cwd();
const cwd: std.Io.Dir = .cwd();
inline for (css) |name| {
const source = std.fmt.comptimePrint(
"{s}/{s}",
.{ css_path, name },
);
try cwd.access(source, .{});
try cwd.access(io, source, .{});
try writer.print(
\\ <file compressed="true" alias="{s}">{s}</file>
\\
@ -242,7 +241,7 @@ fn genRoot(writer: *std.Io.Writer) !void {
fn genUi(
alloc: Allocator,
writer: *std.Io.Writer,
files: *const std.ArrayListUnmanaged([]const u8),
files: *const std.ArrayList([]const u8),
) !void {
try writer.print(
\\ <gresource prefix="{s}/ui">

View File

@ -145,16 +145,24 @@ pub fn Common(
/// as the virtual method but the self parameter points to the
/// target instead of the original class.
fn ImplementFunc(comptime T: type) type {
var params: [fn_info.params.len]std.builtin.Type.Fn.Param = undefined;
@memcpy(&params, fn_info.params);
params[0].type = *ClassInstance(T);
return @Type(.{ .@"fn" = .{
.calling_convention = fn_info.calling_convention,
.is_generic = fn_info.is_generic,
.is_var_args = fn_info.is_var_args,
.return_type = fn_info.return_type,
.params = &params,
} });
var types: [fn_info.params.len]type = undefined;
var attrs: [fn_info.params.len]std.builtin.Type.Fn.Param.Attributes = undefined;
for (fn_info.params, &types, &attrs) |info, *ty, *attr| {
ty.* = info.type.?;
attr.* = .{ .@"noalias" = info.is_noalias };
}
types[0] = *ClassInstance(T);
return @Fn(
types,
&attrs,
fn_info.return_type.?,
.{
.@"callconv" = fn_info.calling_convention,
.varargs = fn_info.is_var_args,
},
);
}
};
}

View File

@ -338,7 +338,7 @@ pub const Application = extern struct {
// I'm unsure of any scenario where this happens. Because we don't
// want to litter null checks everywhere, we just exit here.
log.warn("gdk display is null, exiting", .{});
std.posix.exit(1);
std.process.exit(1);
};
// Setup our windowing protocol logic
@ -2831,7 +2831,7 @@ const Action = struct {
/// given the runtime environment or configuration.
///
/// This must be called BEFORE GTK initialization.
fn setGtkEnv(config: *const CoreConfig) error{NoSpaceLeft}!void {
fn setGtkEnv(config: *const CoreConfig) std.Io.Writer.Error!void {
assert(gtk.isInitialized() == 0);
var gdk_debug: struct {
@ -2900,8 +2900,7 @@ fn setGtkEnv(config: *const CoreConfig) error{NoSpaceLeft}!void {
{
var buf: [1024]u8 = undefined;
var fmt = std.io.fixedBufferStream(&buf);
const writer = fmt.writer();
var writer: std.Io.Writer = .fixed(&buf);
var first: bool = true;
inline for (@typeInfo(@TypeOf(gdk_debug)).@"struct".fields) |field| {
if (@field(gdk_debug, field.name)) {
@ -2911,15 +2910,14 @@ fn setGtkEnv(config: *const CoreConfig) error{NoSpaceLeft}!void {
}
}
try writer.writeByte(0);
const value = fmt.getWritten();
const value = writer.buffered();
log.warn("setting GDK_DEBUG={s}", .{value[0 .. value.len - 1]});
_ = internal_os.setenv("GDK_DEBUG", value[0 .. value.len - 1 :0]);
}
{
var buf: [1024]u8 = undefined;
var fmt = std.io.fixedBufferStream(&buf);
const writer = fmt.writer();
var writer: std.Io.Writer = .fixed(&buf);
var first: bool = true;
inline for (@typeInfo(@TypeOf(gdk_disable)).@"struct".fields) |field| {
if (@field(gdk_disable, field.name)) {
@ -2929,7 +2927,7 @@ fn setGtkEnv(config: *const CoreConfig) error{NoSpaceLeft}!void {
}
}
try writer.writeByte(0);
const value = fmt.getWritten();
const value = writer.buffered();
log.warn("setting GDK_DISABLE={s}", .{value[0 .. value.len - 1]});
_ = internal_os.setenv("GDK_DISABLE", value[0 .. value.len - 1 :0]);
}

View File

@ -176,7 +176,11 @@ test "adding actions to an object" {
_ = addAsGroup(gtk.Box, box, "test", &actions);
}
const expected = std.crypto.random.intRangeAtMost(i32, 1, std.math.maxInt(u31));
const expected = expected: {
const rng_impl: std.Random.IoSource = .{ .io = testing.io };
const rng = rng_impl.interface();
break :expected rng.intRangeAtMost(i32, 1, std.math.maxInt(u31));
};
const parameter = glib.Variant.newInt32(expected);
try testing.expect(box.as(gtk.Widget).activateActionVariant("test.test", parameter) != 0);

View File

@ -1,21 +1,16 @@
const std = @import("std");
// Until the gobject bindings are built at the same time we are building
// Ghostty, we need to import `gtk/gtk.h` directly to ensure that the version
// macros match the version of `gtk4` that we are building/linking against.
const c = @cImport({
@cInclude("gtk/gtk.h");
});
const gtk = @import("gtk");
const log = std.log.scoped(.gtk);
pub const comptime_version: std.SemanticVersion = .{
.major = c.GTK_MAJOR_VERSION,
.minor = c.GTK_MINOR_VERSION,
.patch = c.GTK_MICRO_VERSION,
};
// Until the gobject bindings are built at the same time we are building
// Ghostty, we need to ensure that the version macros match the version of
// `gtk4` that we are building/linking against.
//
// We pull this in through pkg-config, see src/build/SharedDeps.zig for more
// details.
pub const comptime_version = @import("gtk_semver").version;
pub fn getRuntimeVersion() std.SemanticVersion {
return .{
@ -105,17 +100,17 @@ test "atLeast" {
const funs = &.{ atLeast, runtimeAtLeast };
inline for (funs) |fun| {
try testing.expect(fun(c.GTK_MAJOR_VERSION, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION));
try testing.expect(fun(comptime_version.major, comptime_version.minor, comptime_version.patch));
try testing.expect(!fun(c.GTK_MAJOR_VERSION, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION + 1));
try testing.expect(!fun(c.GTK_MAJOR_VERSION, c.GTK_MINOR_VERSION + 1, c.GTK_MICRO_VERSION));
try testing.expect(!fun(c.GTK_MAJOR_VERSION + 1, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION));
try testing.expect(!fun(comptime_version.major, comptime_version.minor, comptime_version.patch + 1));
try testing.expect(!fun(comptime_version.major, comptime_version.minor + 1, comptime_version.patch));
try testing.expect(!fun(comptime_version.major + 1, comptime_version.minor, comptime_version.patch));
try testing.expect(fun(c.GTK_MAJOR_VERSION - 1, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION));
try testing.expect(fun(c.GTK_MAJOR_VERSION - 1, c.GTK_MINOR_VERSION + 1, c.GTK_MICRO_VERSION));
try testing.expect(fun(c.GTK_MAJOR_VERSION - 1, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION + 1));
try testing.expect(fun(comptime_version.major - 1, comptime_version.minor, comptime_version.patch));
try testing.expect(fun(comptime_version.major - 1, comptime_version.minor + 1, comptime_version.patch));
try testing.expect(fun(comptime_version.major - 1, comptime_version.minor, comptime_version.patch + 1));
try testing.expect(fun(c.GTK_MAJOR_VERSION, c.GTK_MINOR_VERSION - 1, c.GTK_MICRO_VERSION + 1));
try testing.expect(fun(comptime_version.major, comptime_version.minor - 1, comptime_version.patch + 1));
}
}
@ -125,16 +120,16 @@ test "runtimeUntil" {
// This is an array in case we add a comptime variant.
const funs = &.{runtimeUntil};
inline for (funs) |fun| {
try testing.expect(!fun(c.GTK_MAJOR_VERSION, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION));
try testing.expect(!fun(comptime_version.major, comptime_version.minor, comptime_version.patch));
try testing.expect(fun(c.GTK_MAJOR_VERSION, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION + 1));
try testing.expect(fun(c.GTK_MAJOR_VERSION, c.GTK_MINOR_VERSION + 1, c.GTK_MICRO_VERSION));
try testing.expect(fun(c.GTK_MAJOR_VERSION + 1, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION));
try testing.expect(fun(comptime_version.major, comptime_version.minor, comptime_version.patch + 1));
try testing.expect(fun(comptime_version.major, comptime_version.minor + 1, comptime_version.patch));
try testing.expect(fun(comptime_version.major + 1, comptime_version.minor, comptime_version.patch));
try testing.expect(!fun(c.GTK_MAJOR_VERSION - 1, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION));
try testing.expect(!fun(c.GTK_MAJOR_VERSION - 1, c.GTK_MINOR_VERSION + 1, c.GTK_MICRO_VERSION));
try testing.expect(!fun(c.GTK_MAJOR_VERSION - 1, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION + 1));
try testing.expect(!fun(comptime_version.major - 1, comptime_version.minor, comptime_version.patch));
try testing.expect(!fun(comptime_version.major - 1, comptime_version.minor + 1, comptime_version.patch));
try testing.expect(!fun(comptime_version.major - 1, comptime_version.minor, comptime_version.patch + 1));
try testing.expect(!fun(c.GTK_MAJOR_VERSION, c.GTK_MINOR_VERSION - 1, c.GTK_MICRO_VERSION + 1));
try testing.expect(!fun(comptime_version.major, comptime_version.minor - 1, comptime_version.patch + 1));
}
}

View File

@ -28,10 +28,12 @@ payload_builder: *glib.VariantBuilder,
/// Used to build the parameters for the IPC.
parameters_builder: *glib.VariantBuilder,
pub const InitError = Allocator.Error || std.Io.Writer.Error || apprt.ipc.Errors;
/// Initialize the helper.
pub fn init(alloc: Allocator, target: apprt.ipc.Target, action: [:0]const u8) (Allocator.Error || std.Io.Writer.Error || apprt.ipc.Errors)!Self {
pub fn init(alloc: Allocator, target: apprt.ipc.Target, action: [:0]const u8) InitError!Self {
var buf: [256]u8 = undefined;
var stderr_writer = std.fs.File.stderr().writer(&buf);
var stderr_writer = std.Io.File.stderr().writer(std.Io.Threaded.global_single_threaded.io(), &buf);
const stderr = &stderr_writer.interface;
// Get the appropriate bus name and object path for contacting the
@ -133,7 +135,7 @@ pub fn addParameter(self: *Self, variant: *glib.Variant) void {
/// should be done with this object other than call `deinit`.
pub fn send(self: *Self) (std.Io.Writer.Error || apprt.ipc.Errors)!void {
var buf: [256]u8 = undefined;
var stderr_writer = std.fs.File.stderr().writer(&buf);
var stderr_writer = std.Io.File.stderr().writer(std.Io.Threaded.global_single_threaded.io(), &buf);
const stderr = &stderr_writer.interface;
// finish building the parameters

View File

@ -23,7 +23,7 @@ app: *App,
dbus: ?*gio.DBusConnection = null,
/// Mutex to protect modification of the entries map or the cleanup timer.
mutex: std.Thread.Mutex = .{},
mutex: std.Io.Mutex = .init,
/// Map to store data about any in-flight calls to the portal.
entries: std.AutoArrayHashMapUnmanaged(usize, *Entry) = .empty,

View File

@ -122,8 +122,12 @@ pub const Action = union(enum) {
/// Sync with: ghostty_ipc_action_u
pub const CValue = cvalue: {
const key_fields = @typeInfo(Key).@"enum".fields;
var union_fields: [key_fields.len]std.builtin.Type.UnionField = undefined;
for (key_fields, 0..) |field, i| {
var names: [key_fields.len][]const u8 = undefined;
var types: [key_fields.len]type = undefined;
var attrs: [key_fields.len]std.builtin.Type.UnionField.Attributes = undefined;
for (key_fields, &names, &types, &attrs) |field, *name, *ty, *attr| {
const action = @unionInit(Action, field.name, undefined);
const Type = t: {
const Type = @TypeOf(@field(action, field.name));
@ -131,20 +135,12 @@ pub const Action = union(enum) {
if (Type != void and @hasDecl(Type, "C")) break :t Type.C;
break :t Type;
};
union_fields[i] = .{
.name = field.name,
.type = Type,
.alignment = @alignOf(Type),
};
name.* = field.name;
ty.* = Type;
attr.* = .{ .@"align" = @alignOf(Type) };
}
break :cvalue @Type(.{ .@"union" = .{
.layout = .@"extern",
.tag_type = null,
.fields = &union_fields,
.decls = &.{},
} });
break :cvalue @Union(.@"extern", null, &names, &types, &attrs);
};
/// Sync with: ghostty_ipc_action_s

View File

@ -64,21 +64,23 @@ pub fn run(
signpost.log.release();
};
const start = std.time.Instant.now() catch return error.BenchmarkFailed;
const start: std.Io.Timestamp = .now(std.Io.Threaded.global_single_threaded.io(), .awake);
while (true) {
// Run our step function. If it fails, we return the error.
try self.vtable.stepFn(self.ptr);
result.iterations += 1;
// Get our current monotonic time and check our exit conditions.
const now = std.time.Instant.now() catch return error.BenchmarkFailed;
const now: std.Io.Timestamp = .now(std.Io.Threaded.global_single_threaded.io(), .awake);
const elapsed = start.durationTo(now).nanoseconds;
assert(elapsed >= 0);
const exit = switch (mode) {
.once => true,
.duration => |ns| now.since(start) >= ns,
.duration => |ns| elapsed >= ns,
};
if (exit) {
result.duration = now.since(start);
result.duration = @as(u64, @intCast(std.math.clamp(elapsed, 0, std.math.maxInt(u64))));
return result;
}
}

View File

@ -20,7 +20,7 @@ const log = std.log.scoped(.@"terminal-stream-bench");
opts: Options,
/// The file, opened in the setup function.
data_f: ?std.fs.File = null,
data_f: ?std.Io.File = null,
pub const Options = struct {
/// The type of codepoint width calculation to use.
@ -93,7 +93,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void {
fn teardown(ptr: *anyopaque) void {
const self: *CodepointWidth = @ptrCast(@alignCast(ptr));
if (self.data_f) |f| {
f.close();
f.close(std.Io.Threaded.global_single_threaded.io());
self.data_f = null;
}
}
@ -114,7 +114,7 @@ fn stepWcwidth(ptr: *anyopaque) Benchmark.Error!void {
const f = self.data_f orelse return;
var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined;
var f_reader = f.reader(&read_buf);
var f_reader = f.reader(std.Io.Threaded.global_single_threaded.io(), &read_buf);
var r = &f_reader.interface;
var d: UTF8Decoder = .{};
@ -141,7 +141,7 @@ fn stepTable(ptr: *anyopaque) Benchmark.Error!void {
const f = self.data_f orelse return;
var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined;
var f_reader = f.reader(&read_buf);
var f_reader = f.reader(std.Io.Threaded.global_single_threaded.io(), &read_buf);
var r = &f_reader.interface;
var d: UTF8Decoder = .{};
@ -173,7 +173,7 @@ fn stepSimd(ptr: *anyopaque) Benchmark.Error!void {
const f = self.data_f orelse return;
var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined;
var f_reader = f.reader(&read_buf);
var f_reader = f.reader(std.Io.Threaded.global_single_threaded.io(), &read_buf);
var r = &f_reader.interface;
var d: UTF8Decoder = .{};

View File

@ -17,7 +17,7 @@ const log = std.log.scoped(.@"terminal-stream-bench");
opts: Options,
/// The file, opened in the setup function.
data_f: ?std.fs.File = null,
data_f: ?std.Io.File = null,
pub const Options = struct {
/// The type of codepoint width calculation to use.
@ -82,7 +82,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void {
fn teardown(ptr: *anyopaque) void {
const self: *GraphemeBreak = @ptrCast(@alignCast(ptr));
if (self.data_f) |f| {
f.close();
f.close(std.Io.Threaded.global_single_threaded.io());
self.data_f = null;
}
}
@ -92,7 +92,7 @@ fn stepNoop(ptr: *anyopaque) Benchmark.Error!void {
const f = self.data_f orelse return;
var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined;
var f_reader = f.reader(&read_buf);
var f_reader = f.reader(std.Io.Threaded.global_single_threaded.io(), &read_buf);
var r = &f_reader.interface;
var d: UTF8Decoder = .{};
@ -115,7 +115,7 @@ fn stepTable(ptr: *anyopaque) Benchmark.Error!void {
const f = self.data_f orelse return;
var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined;
var f_reader = f.reader(&read_buf);
var f_reader = f.reader(std.Io.Threaded.global_single_threaded.io(), &read_buf);
var r = &f_reader.interface;
var d: UTF8Decoder = .{};

View File

@ -17,7 +17,7 @@ const log = std.log.scoped(.@"is-symbol-bench");
opts: Options,
/// The file, opened in the setup function.
data_f: ?std.fs.File = null,
data_f: ?std.Io.File = null,
pub const Options = struct {
/// Which test to run.
@ -80,7 +80,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void {
fn teardown(ptr: *anyopaque) void {
const self: *IsSymbol = @ptrCast(@alignCast(ptr));
if (self.data_f) |f| {
f.close();
f.close(std.Io.Threaded.global_single_threaded.io());
self.data_f = null;
}
}
@ -90,7 +90,7 @@ fn stepUucode(ptr: *anyopaque) Benchmark.Error!void {
const f = self.data_f orelse return;
var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined;
var f_reader = f.reader(&read_buf);
var f_reader = f.reader(std.Io.Threaded.global_single_threaded.io(), &read_buf);
var r = &f_reader.interface;
var d: UTF8Decoder = .{};
@ -117,7 +117,7 @@ fn stepTable(ptr: *anyopaque) Benchmark.Error!void {
const f = self.data_f orelse return;
var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined;
var f_reader = f.reader(&read_buf);
var f_reader = f.reader(std.Io.Threaded.global_single_threaded.io(), &read_buf);
var r = &f_reader.interface;
var d: UTF8Decoder = .{};

View File

@ -13,7 +13,7 @@ const log = std.log.scoped(.@"osc-parser-bench");
opts: Options,
/// The file, opened in the setup function.
data_f: ?std.fs.File = null,
data_f: ?std.Io.File = null,
parser: Parser,
@ -70,7 +70,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void {
fn teardown(ptr: *anyopaque) void {
const self: *OscParser = @ptrCast(@alignCast(ptr));
if (self.data_f) |f| {
f.close();
f.close(std.Io.Threaded.global_single_threaded.io());
self.data_f = null;
}
}
@ -80,7 +80,7 @@ fn step(ptr: *anyopaque) Benchmark.Error!void {
const f = self.data_f orelse return;
var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined;
var r = f.reader(&read_buf);
var r = f.reader(std.Io.Threaded.global_single_threaded.io(), &read_buf);
var osc_buf: [4096]u8 align(std.atomic.cache_line) = undefined;
while (true) {

View File

@ -99,7 +99,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void {
s.nextSlice("hello");
// Setup our terminal state
const data_f: std.fs.File = (options.dataFile(
const data_f: std.Io.File = (options.dataFile(
self.opts.data,
) catch |err| {
log.warn("error opening data file err={}", .{err});
@ -110,7 +110,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void {
defer stream.deinit();
var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined;
var f_reader = data_f.reader(&read_buf);
var f_reader = data_f.reader(std.Io.Threaded.global_single_threaded.io(), &read_buf);
const r = &f_reader.interface;
var buf: [4096]u8 = undefined;

View File

@ -13,7 +13,7 @@ const log = std.log.scoped(.@"terminal-stream-bench");
opts: Options,
/// The file, opened in the setup function.
data_f: ?std.fs.File = null,
data_f: ?std.Io.File = null,
pub const Options = struct {
/// The data to read as a filepath. If this is "-" then
@ -61,7 +61,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void {
fn teardown(ptr: *anyopaque) void {
const self: *TerminalParser = @ptrCast(@alignCast(ptr));
if (self.data_f) |f| {
f.close();
f.close(std.Io.Threaded.global_single_threaded.io());
self.data_f = null;
}
}
@ -76,7 +76,7 @@ fn step(ptr: *anyopaque) Benchmark.Error!void {
// aren't currently IO bound.
const f = self.data_f orelse return;
var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined;
var f_reader = f.reader(&read_buf);
var f_reader = f.reader(std.Io.Threaded.global_single_threaded.io(), &read_buf);
var r = &f_reader.interface;
var p: terminalpkg.Parser = .init();

View File

@ -30,7 +30,7 @@ handler: Handler,
stream: Stream,
/// The file, opened in the setup function.
data_f: ?std.fs.File = null,
data_f: ?std.Io.File = null,
pub const Options = struct {
/// The size of the terminal. This affects benchmarking when
@ -99,7 +99,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void {
fn teardown(ptr: *anyopaque) void {
const self: *TerminalStream = @ptrCast(@alignCast(ptr));
if (self.data_f) |f| {
f.close();
f.close(std.Io.Threaded.global_single_threaded.io());
self.data_f = null;
}
}
@ -115,7 +115,7 @@ fn step(ptr: *anyopaque) Benchmark.Error!void {
const f = self.data_f orelse return;
var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined;
var f_reader = f.reader(&read_buf);
var f_reader = f.reader(std.Io.Threaded.global_single_threaded.io(), &read_buf);
const r = &f_reader.interface;
var buf: [4096]u8 = undefined;

View File

@ -1,6 +1,8 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const cli = @import("../cli.zig");
const compat_args = @import("../lib/compat/args.zig");
const compat_init = @import("../lib/compat/init.zig");
/// The available actions for the CLI. This is the list of available
/// benchmarks. View docs for each individual one in the predictably
@ -36,7 +38,8 @@ pub const Action = enum {
};
/// An entrypoint for the benchmark CLI.
pub fn main() !void {
pub fn main(init: std.process.Init.Minimal) !void {
compat_init.run(init);
const alloc = std.heap.c_allocator;
const action_ = try cli.action.detectArgs(Action, alloc);
const action = action_ orelse return error.NoAction;
@ -48,7 +51,7 @@ pub const Args = union(enum) {
/// The arguments passed to the CLI via argc/argv.
cli,
/// Simple string arguments, parsed via std.process.ArgIteratorGeneral.
/// Simple string arguments, parsed via ArgIteratorGeneral.
string: []const u8,
};
@ -81,7 +84,7 @@ fn mainActionImpl(
try cli.args.parse(Options, alloc, &opts, &iter);
},
.string => |str| {
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
str,
);

View File

@ -6,15 +6,15 @@ const std = @import("std");
/// across our CLI. If the path is not set then no file is returned.
/// If the path is "-", then we will return stdin. If the path is
/// a file then we will open and return the handle.
pub fn dataFile(path_: ?[]const u8) !?std.fs.File {
pub fn dataFile(path_: ?[]const u8) !?std.Io.File {
const path = path_ orelse return null;
// Stdin
if (std.mem.eql(u8, path, "-")) return .stdin();
// Normal file
const file = try std.fs.cwd().openFile(path, .{});
errdefer file.close();
const file = try std.Io.Dir.cwd().openFile(std.Io.Threaded.global_single_threaded.io(), path, .{});
errdefer file.close(std.Io.Threaded.global_single_threaded.io());
return file;
}

View File

@ -67,7 +67,7 @@ emit_unicode_table_gen: bool = false,
is_dep: bool = false,
/// Environmental properties
env: std.process.EnvMap,
env: *const std.process.Environ.Map,
pub fn init(b: *std.Build, appVersion: []const u8, libVersion: []const u8) !Config {
// Setup our standard Zig target and optimize options, i.e.
@ -124,10 +124,10 @@ pub fn init(b: *std.Build, appVersion: []const u8, libVersion: []const u8) !Conf
// defaults.
const gtk_targets = gtk.targets(b);
// We use env vars throughout the build so we grab them immediately here.
var env = try std.process.getEnvMap(b.allocator);
errdefer env.deinit();
// Grab the environment from build state
const env = &b.graph.environ_map;
// We use env vars throughout the build so we grab them immediately here.
var config: Config = .{
.optimize = optimize,
.target = target,
@ -591,7 +591,7 @@ pub fn terminalOptions(self: *const Config, artifact: TerminalBuildOptions.Artif
}
/// Returns a baseline CPU target retaining all the other CPU configs.
pub fn baselineTarget(self: *const Config) std.Build.ResolvedTarget {
pub fn baselineTarget(self: *const Config, io: std.Io) std.Build.ResolvedTarget {
// Set our cpu model as baseline. There may need to be other modifications
// we need to make such as resetting CPU features but for now this works.
var q = self.target.query;
@ -601,7 +601,7 @@ pub fn baselineTarget(self: *const Config) std.Build.ResolvedTarget {
// handle the native case.
return .{
.query = q,
.result = std.zig.system.resolveTargetQuery(q) catch
.result = std.zig.system.resolveTargetQuery(io, q) catch
@panic("unable to resolve baseline query"),
};
}

View File

@ -23,9 +23,9 @@ pub fn init(
// We always want our datagen to be fast because it
// takes awhile to run.
.optimize = .ReleaseFast,
.link_libc = true,
}),
});
exe.linkLibC();
_ = try deps.add(exe);
try steps.append(b.allocator, exe);
}
@ -39,9 +39,9 @@ pub fn init(
.target = deps.config.target,
// We always want our benchmarks to be in release mode.
.optimize = .ReleaseFast,
.link_libc = true,
}),
});
exe.linkLibC();
_ = try deps.add(exe);
try steps.append(b.allocator, exe);
}

View File

@ -237,11 +237,11 @@ pub const Resource = struct {
/// Returns true if the dist path exists at build time.
pub fn exists(self: *const Resource, b: *std.Build) bool {
if (b.build_root.handle.access(self.dist, .{})) {
if (b.build_root.handle.access(b.graph.io, self.dist, .{})) {
// If we have a ".git" directory then we're a git checkout
// and we never want to use the dist path. This shouldn't happen
// so show a warning to the user.
if (b.build_root.handle.access(".git", .{})) {
if (b.build_root.handle.access(b.graph.io, ".git", .{})) {
std.log.warn(
"dist resource '{s}' should not be in a git checkout",
.{self.dist},

View File

@ -50,7 +50,7 @@ pub fn init(
generate_markdown.root_module.addOptions("build_options", generate_markdown_options);
const generate_markdown_step = b.addRunArtifact(generate_markdown);
const markdown_output = generate_markdown_step.captureStdOut();
const markdown_output = generate_markdown_step.captureStdOut(.{});
try steps.append(b.allocator, &b.addInstallFile(
markdown_output,
@ -68,7 +68,7 @@ pub fn init(
generate_html.addFileArg(markdown_output);
try steps.append(b.allocator, &b.addInstallFile(
generate_html.captureStdOut(),
generate_html.captureStdOut(.{}),
"share/ghostty/doc/" ++ manpage.name ++ "." ++ manpage.section ++ ".html",
).step);
@ -83,7 +83,7 @@ pub fn init(
generate_manpage.addFileArg(markdown_output);
try steps.append(b.allocator, &b.addInstallFile(
generate_manpage.captureStdOut(),
generate_manpage.captureStdOut(.{}),
"share/man/man" ++ manpage.section ++ "/" ++ manpage.name ++ "." ++ manpage.section,
).step);
}

View File

@ -50,7 +50,7 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty
switch (cfg.target.result.os.tag) {
.windows => {
exe.subsystem = .Windows;
exe.addWin32ResourceFile(.{
exe.root_module.addWin32ResourceFile(.{
.file = b.path("dist/windows/ghostty.rc"),
});
},
@ -85,7 +85,7 @@ fn checkNixShell(exe: *std.Build.Step.Compile, cfg: *const Config) !void {
if (!cfg.target.query.isNativeOs()) return;
// Verify we're in NixOS
std.fs.accessAbsolute("/etc/NIXOS", .{}) catch return;
std.Io.Dir.accessAbsolute(exe.step.owner.graph.io, "/etc/NIXOS", .{}) catch return;
// If we're in a nix shell, not a problem
if (cfg.env.get("IN_NIX_SHELL") != null) return;

View File

@ -40,16 +40,16 @@ pub fn distResources(b: *std.Build) struct {
.name = "framegen",
.root_module = b.createModule(.{
.target = b.graph.host,
.link_libc = true,
}),
});
exe.addCSourceFile(.{
exe.root_module.addCSourceFile(.{
.file = b.path("src/build/framegen/main.c"),
.flags = &.{},
});
exe.linkLibC();
if (b.systemIntegrationOption("zlib", .{})) {
exe.linkSystemLibrary2("zlib", .{
exe.root_module.linkSystemLibrary("zlib", .{
.preferred_link_mode = .dynamic,
.search_strategy = .mode_first,
});
@ -58,7 +58,7 @@ pub fn distResources(b: *std.Build) struct {
.target = b.graph.host,
.optimize = .ReleaseFast,
})) |zlib_dep| {
exe.linkLibrary(zlib_dep.artifact("z"));
exe.root_module.linkLibrary(zlib_dep.artifact("z"));
}
}

View File

@ -34,7 +34,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyI18n {
msgfmt.addFileArg(b.path("po/" ++ locale ++ ".po"));
try steps.append(b.allocator, &b.addInstallFile(
msgfmt.captureStdOut(),
msgfmt.captureStdOut(.{}),
std.fmt.comptimePrint(
"share/locale/{s}/LC_MESSAGES/{s}.mo",
.{ target_locale, domain },
@ -103,14 +103,15 @@ fn createUpdateStep(b: *std.Build) !*std.Build.Step {
}
var gtk_dir = try b.build_root.handle.openDir(
b.graph.io,
"src/apprt/gtk",
.{ .iterate = true },
);
defer gtk_dir.close();
defer gtk_dir.close(b.graph.io);
var walk = try gtk_dir.walk(b.allocator);
defer walk.deinit();
while (try walk.next()) |src| {
while (try walk.next(b.graph.io)) |src| {
switch (src.kind) {
.file => if (!std.mem.endsWith(
u8,
@ -178,15 +179,15 @@ fn createUpdateStep(b: *std.Build) !*std.Build.Step {
xgettext_merge.addFileArg(gtk_pot);
const usf = b.addUpdateSourceFiles();
usf.addCopyFileToSource(
xgettext_merge.captureStdOut(),
xgettext_merge.captureStdOut(.{}),
"po/" ++ domain ++ ".pot",
);
inline for (locales) |locale| {
const msgmerge = b.addSystemCommand(&.{ "msgmerge", "--quiet", "--no-fuzzy-matching" });
msgmerge.addFileArg(b.path("po/" ++ locale ++ ".po"));
msgmerge.addFileArg(xgettext_merge.captureStdOut());
usf.addCopyFileToSource(msgmerge.captureStdOut(), "po/" ++ locale ++ ".po");
msgmerge.addFileArg(xgettext_merge.captureStdOut(.{}));
usf.addCopyFileToSource(msgmerge.captureStdOut(.{}), "po/" ++ locale ++ ".po");
}
return &usf.step;

View File

@ -29,12 +29,12 @@ pub fn initStatic(
.strip = deps.config.strip,
.omit_frame_pointer = deps.config.strip,
.unwind_tables = if (deps.config.strip) .none else .sync,
.link_libc = true,
}),
// Fails on self-hosted x86_64 on macOS
.use_llvm = true,
});
lib.linkLibC();
// These must be bundled since we're compiling into a static lib.
// Otherwise, you get undefined symbol errors.
@ -72,6 +72,14 @@ pub fn initShared(
b: *std.Build,
deps: *const SharedDeps,
) !GhosttyLib {
// For dynamic linking, we prefer dynamic linking and to search by
// mode first. Mode first will search all paths for a dynamic library
// before falling back to static.
const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
.preferred_link_mode = .dynamic,
.search_strategy = .mode_first,
};
const lib = b.addLibrary(.{
.name = "ghostty",
.linkage = .dynamic,
@ -99,12 +107,12 @@ pub fn initShared(
{
// The CRT initialization code in msvcrt.lib calls __vcrt_initialize
// and __acrt_initialize, which are in the static CRT libraries.
lib.linkSystemLibrary("libvcruntime");
lib.root_module.linkSystemLibrary("libvcruntime", dynamic_link_opts);
// ucrt.lib is in the Windows SDK 'ucrt' dir. Detect the SDK
// installation and add the UCRT library path.
const arch = deps.config.target.result.cpu.arch;
const sdk = std.zig.WindowsSdk.find(b.allocator, arch) catch null;
const sdk = std.zig.WindowsSdk.find(b.allocator, b.graph.io, arch, &b.graph.environ_map) catch null;
if (sdk) |s| {
if (s.windows10sdk) |w10| {
const arch_str: []const u8 = switch (arch) {
@ -120,11 +128,11 @@ pub fn initShared(
) catch null;
if (ucrt_lib_path) |path| {
lib.addLibraryPath(.{ .cwd_relative = path });
lib.root_module.addLibraryPath(.{ .cwd_relative = path });
}
}
}
lib.linkSystemLibrary("libucrt");
lib.root_module.linkSystemLibrary("libucrt", dynamic_link_opts);
}
// Get our debug symbols

View File

@ -21,9 +21,9 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty
.strip = false,
.omit_frame_pointer = false,
.unwind_tables = .sync,
.link_libc = true,
}),
});
build_data_exe.linkLibC();
deps.help_strings.addImport(build_data_exe);
@ -39,7 +39,7 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty
const run = b.addRunArtifact(build_data_exe);
run.addArg("+terminfo");
const wf = b.addWriteFiles();
const source = wf.addCopyFile(run.captureStdOut(), "ghostty.terminfo");
const source = wf.addCopyFile(run.captureStdOut(.{}), "ghostty.terminfo");
if (cfg.emit_terminfo) {
const source_install = b.addInstallFile(
@ -63,8 +63,8 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty
const run_step = RunStep.create(b, "infotocap");
run_step.addArg("infotocap");
run_step.addFileArg(source);
const out_source = run_step.captureStdOut();
_ = run_step.captureStdErr(); // so we don't see stderr
const out_source = run_step.captureStdOut(.{});
_ = run_step.captureStdErr(.{}); // so we don't see stderr
const cap_install = b.addInstallFile(
out_source,
@ -84,7 +84,7 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty
const path = run_step.addOutputFileArg(terminfo_share_dir);
run_step.addFileArg(source);
_ = run_step.captureStdErr(); // so we don't see stderr
_ = run_step.captureStdErr(.{}); // so we don't see stderr
// Ensure that `share/terminfo` is a directory, otherwise the `cp
// -R` will create a file named `share/terminfo`
@ -143,7 +143,7 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty
const run = b.addRunArtifact(build_data_exe);
run.addArg("+fish");
const wf = b.addWriteFiles();
_ = wf.addCopyFile(run.captureStdOut(), "ghostty.fish");
_ = wf.addCopyFile(run.captureStdOut(.{}), "ghostty.fish");
const install_step = b.addInstallDirectory(.{
.source_dir = wf.getDirectory(),
@ -158,7 +158,7 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty
const run = b.addRunArtifact(build_data_exe);
run.addArg("+zsh");
const wf = b.addWriteFiles();
_ = wf.addCopyFile(run.captureStdOut(), "_ghostty");
_ = wf.addCopyFile(run.captureStdOut(.{}), "_ghostty");
const install_step = b.addInstallDirectory(.{
.source_dir = wf.getDirectory(),
@ -173,7 +173,7 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty
const run = b.addRunArtifact(build_data_exe);
run.addArg("+bash");
const wf = b.addWriteFiles();
_ = wf.addCopyFile(run.captureStdOut(), "ghostty.bash");
_ = wf.addCopyFile(run.captureStdOut(.{}), "ghostty.bash");
const install_step = b.addInstallDirectory(.{
.source_dir = wf.getDirectory(),
@ -190,22 +190,22 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty
{
const run = b.addRunArtifact(build_data_exe);
run.addArg("+vim-syntax");
_ = wf.addCopyFile(run.captureStdOut(), "syntax/ghostty.vim");
_ = wf.addCopyFile(run.captureStdOut(.{}), "syntax/ghostty.vim");
}
{
const run = b.addRunArtifact(build_data_exe);
run.addArg("+vim-ftdetect");
_ = wf.addCopyFile(run.captureStdOut(), "ftdetect/ghostty.vim");
_ = wf.addCopyFile(run.captureStdOut(.{}), "ftdetect/ghostty.vim");
}
{
const run = b.addRunArtifact(build_data_exe);
run.addArg("+vim-ftplugin");
_ = wf.addCopyFile(run.captureStdOut(), "ftplugin/ghostty.vim");
_ = wf.addCopyFile(run.captureStdOut(.{}), "ftplugin/ghostty.vim");
}
{
const run = b.addRunArtifact(build_data_exe);
run.addArg("+vim-compiler");
_ = wf.addCopyFile(run.captureStdOut(), "compiler/ghostty.vim");
_ = wf.addCopyFile(run.captureStdOut(.{}), "compiler/ghostty.vim");
}
const vim_step = b.addInstallDirectory(.{
@ -233,7 +233,7 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty
const run = b.addRunArtifact(build_data_exe);
run.addArg("+sublime");
const wf = b.addWriteFiles();
_ = wf.addCopyFile(run.captureStdOut(), "ghostty.sublime-syntax");
_ = wf.addCopyFile(run.captureStdOut(.{}), "ghostty.sublime-syntax");
const install_step = b.addInstallDirectory(.{
.source_dir = wf.getDirectory(),
@ -358,10 +358,10 @@ fn addLinuxAppResources(
// Template output has a single header line we want to remove.
// We use `tail` to do it since its part of the POSIX standard.
const tail = b.addSystemCommand(&.{ "tail", "-n", "+2" });
tail.setStdIn(.{ .lazy_path = tpl.getOutput() });
tail.setStdIn(.{ .lazy_path = tpl.getOutputFile() });
const copy = b.addInstallFile(
tail.captureStdOut(),
tail.captureStdOut(.{}),
template[1],
);

View File

@ -40,7 +40,7 @@ pub fn init(
}
const webgen_config_step = b.addRunArtifact(webgen_config);
const webgen_config_out = webgen_config_step.captureStdOut();
const webgen_config_out = webgen_config_step.captureStdOut(.{});
try steps.append(b.allocator, &b.addInstallFile(
webgen_config_out,
@ -71,7 +71,7 @@ pub fn init(
}
const webgen_actions_step = b.addRunArtifact(webgen_actions);
const webgen_actions_out = webgen_actions_step.captureStdOut();
const webgen_actions_out = webgen_actions_step.captureStdOut(.{});
try steps.append(b.allocator, &b.addInstallFile(
webgen_actions_out,
@ -102,7 +102,7 @@ pub fn init(
}
const webgen_commands_step = b.addRunArtifact(webgen_commands);
const webgen_commands_out = webgen_commands_step.captureStdOut();
const webgen_commands_out = webgen_commands_step.captureStdOut(.{});
try steps.append(b.allocator, &b.addInstallFile(
webgen_commands_out,

View File

@ -48,21 +48,21 @@ pub fn init(
},
};
const env = try std.process.getEnvMap(b.allocator);
const env = b.graph.environ_map;
const app_path = b.fmt("macos/build/{s}/Ghostty.app", .{xc_config});
// Our step to build the Ghostty macOS app.
const build = build: {
// External environment variables can mess up xcodebuild, so
// we create a new empty environment.
const env_map = try b.allocator.create(std.process.EnvMap);
const env_map = try b.allocator.create(std.process.Environ.Map);
env_map.* = .init(b.allocator);
if (env.get("PATH")) |v| try env_map.put("PATH", v);
const step = RunStep.create(b, "xcodebuild");
step.has_side_effects = true;
step.cwd = b.path("macos");
step.env_map = env_map;
step.environ_map = env_map;
step.addArgs(&.{
"xcodebuild",
"-target",
@ -91,14 +91,14 @@ pub fn init(
};
const xctest = xctest: {
const env_map = try b.allocator.create(std.process.EnvMap);
const env_map = try b.allocator.create(std.process.Environ.Map);
env_map.* = .init(b.allocator);
if (env.get("PATH")) |v| try env_map.put("PATH", v);
const step = RunStep.create(b, "xcodebuild test");
step.has_side_effects = true;
step.cwd = b.path("macos");
step.env_map = env_map;
step.environ_map = env_map;
step.addArgs(&.{
"xcodebuild",
"test",

View File

@ -135,7 +135,7 @@ fn initVt(
deps.unicode_tables.addModuleImport(vt);
// We need uucode for grapheme break support
deps.addUucode(b, vt, cfg.target, cfg.optimize);
vt.addImport("uucode", deps.uucode_mod);
// If SIMD is enabled, add all our SIMD dependencies.
if (cfg.simd) {

View File

@ -23,7 +23,7 @@ pub fn detect(b: *std.Build) !Version {
const tmp: []u8 = b.runAllowFail(
&[_][]const u8{ "git", "-C", b.build_root.path orelse ".", "rev-parse", "--abbrev-ref", "HEAD" },
&code,
.Ignore,
.ignore,
) catch |err| switch (err) {
error.FileNotFound => return error.GitNotFound,
error.ExitCodeFailure => return error.GitNotRepository,
@ -44,19 +44,19 @@ pub fn detect(b: *std.Build) !Version {
const output = b.runAllowFail(
&[_][]const u8{ "git", "-C", b.build_root.path orelse ".", "-c", "log.showSignature=false", "log", "--pretty=format:%h", "-n", "1" },
&code,
.Ignore,
.ignore,
) catch |err| switch (err) {
error.FileNotFound => return error.GitNotFound,
else => return err,
};
break :short_hash std.mem.trimRight(u8, output, "\r\n ");
break :short_hash std.mem.trimEnd(u8, output, "\r\n ");
};
const tag = b.runAllowFail(
&[_][]const u8{ "git", "-C", b.build_root.path orelse ".", "describe", "--exact-match", "--tags" },
&code,
.Ignore,
.ignore,
) catch |err| switch (err) {
error.FileNotFound => return error.GitNotFound,
error.ExitCodeFailure => "", // expected
@ -70,7 +70,7 @@ pub fn detect(b: *std.Build) !Version {
"diff",
"--quiet",
"--exit-code",
}, &code, .Ignore) catch |err| switch (err) {
}, &code, .ignore) catch |err| switch (err) {
error.FileNotFound => return error.GitNotFound,
error.ExitCodeFailure => {}, // expected
else => return err,
@ -80,7 +80,7 @@ pub fn detect(b: *std.Build) !Version {
return .{
.short_hash = short_hash,
.changes = changes,
.tag = if (tag.len > 0) std.mem.trimRight(u8, tag, "\r\n ") else null,
.branch = std.mem.trimRight(u8, branch, "\r\n "),
.tag = if (tag.len > 0) std.mem.trimEnd(u8, tag, "\r\n ") else null,
.branch = std.mem.trimEnd(u8, branch, "\r\n "),
};
}

View File

@ -34,7 +34,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !HelpStrings {
// Generated Zig files have to end with .zig
const wf = b.addWriteFiles();
const output = wf.addCopyFile(help_run.captureStdOut(), "helpgen.zig");
const output = wf.addCopyFile(help_run.captureStdOut(.{}), "helpgen.zig");
return .{
.exe = exe,

View File

@ -9,6 +9,7 @@ const MetallibStep = @import("MetallibStep.zig");
const UnicodeTables = @import("UnicodeTables.zig");
const GhosttyFrameData = @import("GhosttyFrameData.zig");
const DistResource = @import("GhosttyDist.zig").Resource;
const gtk_helpers = @import("gtk.zig");
config: *const Config,
@ -19,6 +20,43 @@ unicode_tables: UnicodeTables,
framedata: GhosttyFrameData,
uucode_tables: std.Build.LazyPath,
/// Singleton uucode module, instantiated once in `init` and reused
/// everywhere so that ghostty and vaxis share the same compiled tables in
/// each final binary instead of each linking its own copy.
///
/// Sharing one instance is also a hard requirement (not just an
/// optimization) for Zig 0.16's strict module model. `SharedDeps.add` runs
/// many times across different (target, optimize) tuples (macos-aarch64,
/// macos-x86_64, ios-aarch64, Debug + ReleaseFast, etc.), and on each
/// call we have to wire uucode into both the step's root module and into
/// vaxis_mod (because vaxis's `Parser.zig` does `@import("uucode")` and
/// we pass `external_uucode = true` to vaxis's build.zig so vaxis doesn't
/// instantiate its own uucode dep). If those two import bindings ever
/// resolve to *different* `*Module` pointers within a single Compile
/// step's analysis, Zig fails with:
///
/// vaxis/src/Parser.zig: file exists in modules 'uucode' and 'uucode0'
///
/// because all those uucode module instances share the same physical
/// `uucode/src/root.zig` file on disk, and Zig requires every file to belong
/// to exactly one module within a Compile graph.
///
/// The natural way to keep them the same would be to call
/// `b.lazyDependency("uucode", .{ .tables_path, .build_config_path })`
/// from each call site and let Zig's dependency cache deduplicate
/// identical args. That fails because of a bug in Zig's
/// `userLazyPathsAreTheSame` (Build.zig) where the `.src_path` and
/// `.generated` equality checks are inverted: `if (std.mem.eql(...))
/// return false` instead of `if (!std.mem.eql(...)) return false`. The
/// dep cache key therefore always misses whenever any arg is a
/// `b.path(...)` LazyPath, so each call returns a fresh `*Dependency`
/// with a fresh `*Module`. Hoisting the dep into one eager
/// `b.dependency` call here sidesteps the cache entirely.
///
/// This conflict is independent of whether vaxis itself is acquired as a
/// singleton or per-target dep.
uucode_mod: *std.Build.Module,
/// Used to keep track of a list of file sources.
pub const LazyPathList = std.ArrayList(std.Build.LazyPath);
@ -31,12 +69,20 @@ pub fn init(b: *std.Build, cfg: *const Config) !SharedDeps {
break :blk uucode.namedLazyPath("tables.zig");
};
// Instantiate the singleton uucode module that both ghostty and vaxis
// import. See the doc comment on `uucode_mod`.
const uucode_mod = b.dependency("uucode", .{
.tables_path = uucode_tables,
.build_config_path = b.path("src/build/uucode_config.zig"),
}).module("uucode");
var result: SharedDeps = .{
.config = cfg,
.help_strings = try .init(b, cfg),
.unicode_tables = try .init(b, uucode_tables),
.framedata = try .init(b),
.uucode_tables = uucode_tables,
.uucode_mod = uucode_mod,
// Setup by retarget
.options = undefined,
@ -135,6 +181,9 @@ pub fn add(
// Every exe needs the terminal options
self.config.terminalOptions(.ghostty).add(b, step.root_module);
// Every exe needs the uucode module
step.root_module.addImport("uucode", self.uucode_mod);
// C imports for locale constants and functions
{
const c = b.addTranslateC(.{
@ -143,11 +192,15 @@ pub fn add(
.optimize = optimize,
});
if (target.result.os.tag.isDarwin()) {
const libc = try std.zig.LibCInstallation.findNative(.{
.allocator = b.allocator,
.target = &target.result,
.verbose = false,
});
const libc = try std.zig.LibCInstallation.findNative(
b.allocator,
b.graph.io,
.{
.environ_map = &b.graph.environ_map,
.target = &target.result,
.verbose = false,
},
);
c.addSystemIncludePath(.{ .cwd_relative = libc.sys_include_dir.? });
}
step.root_module.addImport("locale-c", c.createModule());
@ -166,11 +219,15 @@ pub fn add(
});
switch (target.result.os.tag) {
.macos => {
const libc = try std.zig.LibCInstallation.findNative(.{
.allocator = b.allocator,
.target = &target.result,
.verbose = false,
});
const libc = try std.zig.LibCInstallation.findNative(
b.allocator,
b.graph.io,
.{
.environ_map = &b.graph.environ_map,
.target = &target.result,
.verbose = false,
},
);
c.addSystemIncludePath(.{ .cwd_relative = libc.sys_include_dir.? });
},
else => {},
@ -194,10 +251,10 @@ pub fn add(
);
if (b.systemIntegrationOption("freetype", .{})) {
step.linkSystemLibrary2("bzip2", dynamic_link_opts);
step.linkSystemLibrary2("freetype2", dynamic_link_opts);
step.root_module.linkSystemLibrary("bzip2", dynamic_link_opts);
step.root_module.linkSystemLibrary("freetype2", dynamic_link_opts);
} else {
step.linkLibrary(freetype_dep.artifact("freetype"));
step.root_module.linkLibrary(freetype_dep.artifact("freetype"));
try static_libs.append(
b.allocator,
freetype_dep.artifact("freetype").getEmittedBin(),
@ -219,9 +276,9 @@ pub fn add(
harfbuzz_dep.module("harfbuzz"),
);
if (b.systemIntegrationOption("harfbuzz", .{})) {
step.linkSystemLibrary2("harfbuzz", dynamic_link_opts);
step.root_module.linkSystemLibrary("harfbuzz", dynamic_link_opts);
} else {
step.linkLibrary(harfbuzz_dep.artifact("harfbuzz"));
step.root_module.linkLibrary(harfbuzz_dep.artifact("harfbuzz"));
try static_libs.append(
b.allocator,
harfbuzz_dep.artifact("harfbuzz").getEmittedBin(),
@ -243,9 +300,9 @@ pub fn add(
);
if (b.systemIntegrationOption("fontconfig", .{})) {
step.linkSystemLibrary2("fontconfig", dynamic_link_opts);
step.root_module.linkSystemLibrary("fontconfig", dynamic_link_opts);
} else {
step.linkLibrary(fontconfig_dep.artifact("fontconfig"));
step.root_module.linkLibrary(fontconfig_dep.artifact("fontconfig"));
try static_libs.append(
b.allocator,
fontconfig_dep.artifact("fontconfig").getEmittedBin(),
@ -263,7 +320,7 @@ pub fn add(
.target = target,
.optimize = optimize,
})) |libpng_dep| {
step.linkLibrary(libpng_dep.artifact("png"));
step.root_module.linkLibrary(libpng_dep.artifact("png"));
try static_libs.append(
b.allocator,
libpng_dep.artifact("png").getEmittedBin(),
@ -277,7 +334,7 @@ pub fn add(
.target = target,
.optimize = optimize,
})) |zlib_dep| {
step.linkLibrary(zlib_dep.artifact("z"));
step.root_module.linkLibrary(zlib_dep.artifact("z"));
try static_libs.append(
b.allocator,
zlib_dep.artifact("z").getEmittedBin(),
@ -295,9 +352,9 @@ pub fn add(
oniguruma_dep.module("oniguruma"),
);
if (b.systemIntegrationOption("oniguruma", .{})) {
step.linkSystemLibrary2("oniguruma", dynamic_link_opts);
step.root_module.linkSystemLibrary("oniguruma", dynamic_link_opts);
} else {
step.linkLibrary(oniguruma_dep.artifact("oniguruma"));
step.root_module.linkLibrary(oniguruma_dep.artifact("oniguruma"));
try static_libs.append(
b.allocator,
oniguruma_dep.artifact("oniguruma").getEmittedBin(),
@ -312,13 +369,13 @@ pub fn add(
})) |glslang_dep| {
step.root_module.addImport("glslang", glslang_dep.module("glslang"));
if (b.systemIntegrationOption("glslang", .{})) {
step.linkSystemLibrary2("glslang", dynamic_link_opts);
step.linkSystemLibrary2(
step.root_module.linkSystemLibrary("glslang", dynamic_link_opts);
step.root_module.linkSystemLibrary(
"glslang-default-resource-limits",
dynamic_link_opts,
);
} else {
step.linkLibrary(glslang_dep.artifact("glslang"));
step.root_module.linkLibrary(glslang_dep.artifact("glslang"));
try static_libs.append(
b.allocator,
glslang_dep.artifact("glslang").getEmittedBin(),
@ -336,9 +393,9 @@ pub fn add(
spirv_cross_dep.module("spirv_cross"),
);
if (b.systemIntegrationOption("spirv-cross", .{})) {
step.linkSystemLibrary2("spirv-cross-c-shared", dynamic_link_opts);
step.root_module.linkSystemLibrary("spirv-cross-c-shared", dynamic_link_opts);
} else {
step.linkLibrary(spirv_cross_dep.artifact("spirv_cross"));
step.root_module.linkLibrary(spirv_cross_dep.artifact("spirv_cross"));
try static_libs.append(
b.allocator,
spirv_cross_dep.artifact("spirv_cross").getEmittedBin(),
@ -357,7 +414,7 @@ pub fn add(
"sentry",
sentry_dep.module("sentry"),
);
step.linkLibrary(sentry_dep.artifact("sentry"));
step.root_module.linkLibrary(sentry_dep.artifact("sentry"));
try static_libs.append(
b.allocator,
sentry_dep.artifact("sentry").getEmittedBin(),
@ -404,18 +461,18 @@ pub fn add(
if (step.rootModuleTarget().os.tag == .linux) {
const triple = try step.rootModuleTarget().linuxTriple(b.allocator);
const path = b.fmt("/usr/lib/{s}", .{triple});
if (std.fs.accessAbsolute(path, .{})) {
step.addLibraryPath(.{ .cwd_relative = path });
if (std.Io.Dir.accessAbsolute(b.graph.io, path, .{})) {
step.root_module.addLibraryPath(.{ .cwd_relative = path });
} else |_| {}
}
// C files
step.linkLibC();
step.addIncludePath(b.path("src/stb"));
step.root_module.link_libc = true;
step.root_module.addIncludePath(b.path("src/stb"));
// Disable ubsan for MSVC: Zig's ubsan runtime cannot be bundled
// on Windows (LNK4229), leaving __ubsan_handle_* unresolved when
// the static archive is consumed by an external linker.
step.addCSourceFiles(.{
step.root_module.addCSourceFiles(.{
.files = &.{"src/stb/stb.c"},
.flags = if (step.rootModuleTarget().abi == .msvc)
&.{ "-fno-sanitize=undefined", "-fno-sanitize-trap=undefined" }
@ -423,7 +480,7 @@ pub fn add(
&.{},
});
if (step.rootModuleTarget().os.tag == .linux) {
step.addIncludePath(b.path("src/apprt/gtk"));
step.root_module.addIncludePath(b.path("src/apprt/gtk"));
}
// libcpp is required for various dependencies. On MSVC, we must
@ -433,7 +490,7 @@ pub fn add(
// include directories (already added via linkLibC above) contain
// both C and C++ headers, so linkLibCpp is not needed.
if (step.rootModuleTarget().abi != .msvc) {
step.linkLibCpp();
step.root_module.link_libcpp = true;
}
// We always require the system SDK so that our system headers are available.
@ -452,8 +509,14 @@ pub fn add(
if (b.lazyDependency("opengl", .{})) |dep| {
step.root_module.addImport("opengl", dep.module("opengl"));
}
if (b.lazyDependency("vaxis", .{})) |dep| {
step.root_module.addImport("vaxis", dep.module("vaxis"));
if (b.lazyDependency("vaxis", .{
.target = target,
.optimize = optimize,
.external_uucode = true,
})) |dep| {
const vaxis = dep.module("vaxis");
step.root_module.addImport("vaxis", vaxis);
vaxis.addImport("uucode", self.uucode_mod);
}
if (b.lazyDependency("wuffs", .{
.target = target,
@ -473,7 +536,6 @@ pub fn add(
})) |dep| {
step.root_module.addImport("z2d", dep.module("z2d"));
}
self.addUucode(b, step.root_module, target, optimize);
if (b.lazyDependency("zf", .{
.target = target,
.optimize = optimize,
@ -502,7 +564,7 @@ pub fn add(
"macos",
macos_dep.module("macos"),
);
step.linkLibrary(
step.root_module.linkLibrary(
macos_dep.artifact("macos"),
);
try static_libs.append(
@ -512,7 +574,7 @@ pub fn add(
}
if (self.config.renderer == .opengl) {
step.linkFramework("OpenGL");
step.root_module.linkFramework("OpenGL", .{});
}
// Apple platforms do not include libc libintl so we bundle it.
@ -523,7 +585,7 @@ pub fn add(
.target = target,
.optimize = optimize,
})) |libintl_dep| {
step.linkLibrary(libintl_dep.artifact("intl"));
step.root_module.linkLibrary(libintl_dep.artifact("intl"));
try static_libs.append(
b.allocator,
libintl_dep.artifact("intl").getEmittedBin(),
@ -543,7 +605,7 @@ pub fn add(
.@"backend-opengl3" = !target.result.os.tag.isDarwin(),
})) |dep| {
step.root_module.addImport("dcimgui", dep.module("dcimgui"));
step.linkLibrary(dep.artifact("dcimgui"));
step.root_module.linkLibrary(dep.artifact("dcimgui"));
try static_libs.append(
b.allocator,
dep.artifact("dcimgui").getEmittedBin(),
@ -592,15 +654,15 @@ pub fn add(
// If we're building an exe then we have additional dependencies.
if (step.kind != .lib) {
// We always statically compile glad
step.addIncludePath(b.path("vendor/glad/include/"));
step.addCSourceFile(.{
step.root_module.addIncludePath(b.path("vendor/glad/include/"));
step.root_module.addCSourceFile(.{
.file = b.path("vendor/glad/src/gl.c"),
.flags = &.{},
});
// When we're targeting flatpak we ALWAYS link GTK so we
// get access to glib for dbus.
if (self.config.flatpak) step.linkSystemLibrary2("gtk4", dynamic_link_opts);
if (self.config.flatpak) step.root_module.linkSystemLibrary("gtk4", dynamic_link_opts);
switch (self.config.app_runtime) {
.none => {},
@ -644,11 +706,24 @@ fn addGtkNg(
}
}
step.linkSystemLibrary2("gtk4", dynamic_link_opts);
step.linkSystemLibrary2("libadwaita-1", dynamic_link_opts);
{
// Expose the build-time GTK version through options.
const opts = b.addOptions();
opts.addOption(std.SemanticVersion, "version", gtk_helpers.gtkVersion(b));
step.root_module.addOptions("gtk_semver", opts);
}
{
// Expose the build-time Adwaita version through options.
const opts = b.addOptions();
opts.addOption(std.SemanticVersion, "version", gtk_helpers.adwVersion(b));
step.root_module.addOptions("adw_semver", opts);
}
step.root_module.linkSystemLibrary("gtk4", dynamic_link_opts);
step.root_module.linkSystemLibrary("libadwaita-1", dynamic_link_opts);
if (self.config.x11) {
step.linkSystemLibrary2("X11", dynamic_link_opts);
step.root_module.linkSystemLibrary("X11", dynamic_link_opts);
if (gobject_) |gobject| {
step.root_module.addImport(
"gdk_x11",
@ -732,24 +807,24 @@ fn addGtkNg(
// IMPORTANT: gtk4-layer-shell must be linked BEFORE
// wayland-client, as it relies on shimming libwayland's APIs.
if (b.systemIntegrationOption("gtk4-layer-shell", .{})) {
step.linkSystemLibrary2("gtk4-layer-shell-0", dynamic_link_opts);
step.root_module.linkSystemLibrary("gtk4-layer-shell-0", dynamic_link_opts);
} else {
// gtk4-layer-shell *must* be dynamically linked,
// so we don't add it as a static library
const shared_lib = gtk4_layer_shell.artifact("gtk4-layer-shell");
b.installArtifact(shared_lib);
step.linkLibrary(shared_lib);
step.root_module.linkLibrary(shared_lib);
}
}
step.linkSystemLibrary2("wayland-client", dynamic_link_opts);
step.root_module.linkSystemLibrary("wayland-client", dynamic_link_opts);
}
{
// Get our gresource c/h files and add them to our build.
const dist = gtkNgDistResources(b);
step.addCSourceFile(.{ .file = dist.resources_c.path(b), .flags = &.{} });
step.addIncludePath(dist.resources_h.path(b).dirname());
step.root_module.addCSourceFile(.{ .file = dist.resources_c.path(b), .flags = &.{} });
step.root_module.addIncludePath(dist.resources_h.path(b).dirname());
}
}
@ -890,11 +965,17 @@ pub fn gtkNgDistResources(
.root_module = b.createModule(.{
.root_source_file = b.path("src/apprt/gtk/build/blueprint.zig"),
.target = b.graph.host,
.link_libc = true,
}),
});
blueprint_exe.linkLibC();
blueprint_exe.linkSystemLibrary2("gtk4", dynamic_link_opts);
blueprint_exe.linkSystemLibrary2("libadwaita-1", dynamic_link_opts);
{
// Expose the build-time Adwaita version through options.
const opts = b.addOptions();
opts.addOption(std.SemanticVersion, "version", gtk_helpers.adwVersion(b));
blueprint_exe.root_module.addOptions("adw_semver", opts);
}
blueprint_exe.root_module.linkSystemLibrary("gtk4", dynamic_link_opts);
blueprint_exe.root_module.linkSystemLibrary("libadwaita-1", dynamic_link_opts);
for (gresource.blueprints) |bp| {
const blueprint_run = b.addRunArtifact(blueprint_exe);
@ -923,7 +1004,7 @@ pub fn gtkNgDistResources(
xml_run.addFileArg(ui_file);
}
break :gresource_xml xml_run.captureStdOut();
break :gresource_xml xml_run.captureStdOut(.{});
};
const generate_c = b.addSystemCommand(&.{
@ -964,23 +1045,6 @@ pub fn gtkNgDistResources(
};
}
pub fn addUucode(
self: *const SharedDeps,
b: *std.Build,
module: *std.Build.Module,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
) void {
if (b.lazyDependency("uucode", .{
.target = target,
.optimize = optimize,
.tables_path = self.uucode_tables,
.build_config_path = b.path("src/build/uucode_config.zig"),
})) |dep| {
module.addImport("uucode", dep.module("uucode"));
}
}
// For dynamic linking, we prefer dynamic linking and to search by
// mode first. Mode first will search all paths for a dynamic library
// before falling back to static.

View File

@ -54,8 +54,8 @@ pub fn init(b: *std.Build, uucode_tables: std.Build.LazyPath) !UnicodeTables {
// Generated Zig files have to end with .zig
const wf = b.addWriteFiles();
const props_output = wf.addCopyFile(props_run.captureStdOut(), "props.zig");
const symbols_output = wf.addCopyFile(symbols_run.captureStdOut(), "symbols.zig");
const props_output = wf.addCopyFile(props_run.captureStdOut(.{}), "props.zig");
const symbols_output = wf.addCopyFile(symbols_run.captureStdOut(.{}), "symbols.zig");
return .{
.props_exe = props_exe,

View File

@ -63,8 +63,8 @@ pub fn create(b: *std.Build, opts: Options) *XCFrameworkStep {
run.addArg("-output");
run.addArg(opts.out_path);
run.expectExitCode(0);
_ = run.captureStdOut();
_ = run.captureStdErr();
_ = run.captureStdOut(.{});
_ = run.captureStdErr(.{});
break :run run;
};
run_create.step.dependOn(&run_delete.step);

13
src/build/args.zig Normal file
View File

@ -0,0 +1,13 @@
const std = @import("std");
/// Helper function for args allocation. Caller fully owns the slice and can
/// deinit as need be.
pub fn argsAlloc(alloc: std.mem.Allocator, args: std.process.Args) std.mem.Allocator.Error![][:0]const u8 {
var result: std.ArrayList([:0]const u8) = .empty;
errdefer result.deinit(alloc);
var it = args.iterate();
while (it.next()) |arg| {
try result.append(alloc, arg);
}
return try result.toOwnedSlice(alloc);
}

View File

@ -10,12 +10,15 @@
//! Usage: combine_archives <zig_exe> <output.a> <input1.a> [input2.a ...]
const std = @import("std");
const argsAlloc = @import("args.zig").argsAlloc;
pub fn main() !void {
var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
pub fn main(init: std.process.Init) !void {
var gpa: std.heap.DebugAllocator(.{}) = .init;
const alloc = gpa.allocator();
const args = try std.process.argsAlloc(alloc);
const args = try argsAlloc(alloc, init.minimal.args);
defer alloc.free(args);
if (args.len < 4) {
std.log.err("usage: combine_archives <zig_exe> <output> <input...>", .{});
std.process.exit(1);
@ -37,19 +40,20 @@ pub fn main() !void {
}
try script.appendSlice(alloc, "SAVE\nEND\n");
var child: std.process.Child = .init(&.{ zig_exe, "ar", "-M" }, alloc);
child.stdin_behavior = .Pipe;
child.stdout_behavior = .Inherit;
child.stderr_behavior = .Inherit;
var child = try std.process.spawn(init.io, .{
.argv = &.{ zig_exe, "ar", "-M" },
.stdin = .pipe,
.stdout = .inherit,
.stderr = .inherit,
});
try child.spawn();
try child.stdin.?.writeAll(script.items);
child.stdin.?.close();
try child.stdin.?.writeStreamingAll(init.io, script.items);
child.stdin.?.close(init.io);
child.stdin = null;
const term = try child.wait();
if (term.Exited != 0) {
std.log.err("zig ar -M exited with code {d}", .{term.Exited});
const term = try child.wait(init.io);
if (term.exited != 0) {
std.log.err("zig ar -M exited with code {d}", .{term.exited});
std.process.exit(1);
}
}

View File

@ -14,7 +14,7 @@ pub fn targets(b: *std.Build) Targets {
const output = b.runAllowFail(
&.{ "pkg-config", "--variable=targets", "gtk4" },
&code,
.Ignore,
.ignore,
) catch return .{};
const x11 = std.mem.indexOf(u8, output, "x11") != null;
@ -25,3 +25,26 @@ pub fn targets(b: *std.Build) Targets {
.wayland = wayland,
};
}
/// Returns the GTK build version.
pub fn gtkVersion(b: *std.Build) std.SemanticVersion {
const version_string = std.mem.trimEnd(
u8,
b.run(&.{ "pkg-config", "--modversion", "gtk4" }),
"\n",
);
return std.SemanticVersion.parse(version_string) catch unreachable;
}
/// Returns the Adwaita build version.
pub fn adwVersion(b: *std.Build) std.SemanticVersion {
// Note that we need to use sh here instead of just plain pkg-config
// because libadwaita-1 does not have a semver-conforming version. We could
// use pure Zig, but cut works just as well and it's in coreutils.
const version_string = std.mem.trimEnd(
u8,
b.run(&.{ "sh", "-c", "pkg-config --modversion libadwaita-1 | cut -f-3 -d." }),
"\n",
);
return std.SemanticVersion.parse(version_string) catch unreachable;
}

View File

@ -1,109 +1,138 @@
const std = @import("std");
const assert = std.debug.assert;
const config = @import("config.zig");
const config_x = @import("config.x.zig");
const d = config.default;
const wcwidth = config_x.wcwidth;
const grapheme_break_no_control = config_x.grapheme_break_no_control;
const Allocator = std.mem.Allocator;
fn computeWidth(
alloc: std.mem.Allocator,
cp: u21,
data: anytype,
backing: anytype,
tracking: anytype,
) Allocator.Error!void {
_ = alloc;
_ = cp;
_ = backing;
_ = tracking;
// This condition is needed as Ghostty currently has a singular concept for
// the `width` of a code point, while `uucode` splits the concept into
// `wcwidth_standalone` and `wcwidth_zero_in_grapheme`. The two cases where
// we want to use the `wcwidth_standalone` despite the code point occupying
// zero width in a grapheme (`wcwidth_zero_in_grapheme`) are emoji
// modifiers and prepend code points. For emoji modifiers we want to
// support displaying them in isolation as color patches, and if prepend
// characters were to be width 0 they would disappear from the output with
// Ghostty's current width 0 handling. Future work will take advantage of
// the new uucode `wcwidth_standalone` vs `wcwidth_zero_in_grapheme` split.
if (data.wcwidth_zero_in_grapheme and !data.is_emoji_modifier and data.grapheme_break_no_control != .prepend) {
data.width = 0;
} else {
data.width = @min(2, data.wcwidth_standalone);
}
}
const width = config.Extension{
.inputs = &.{
"wcwidth_standalone",
"wcwidth_zero_in_grapheme",
"is_emoji_modifier",
"grapheme_break_no_control",
pub const fields = &config.mergeFields(config.fields, &.{
.{ .name = "width", .type = u2 },
.{ .name = "is_symbol", .type = bool },
});
pub const build_components = &config.mergeComponents(config.build_components, &.{
.{
.Impl = WidthComponent,
.inputs = &.{
"wcwidth_standalone",
"wcwidth_zero_in_grapheme",
"is_emoji_modifier",
"grapheme_break_no_control",
},
.fields = &.{"width"},
},
.compute = &computeWidth,
.fields = &.{
.{ .name = "width", .type = u2 },
.{
.Impl = IsSymbolComponent,
.inputs = &.{ "block", "general_category" },
.fields = &.{"is_symbol"},
},
};
});
fn computeIsSymbol(
alloc: Allocator,
cp: u21,
data: anytype,
backing: anytype,
tracking: anytype,
) Allocator.Error!void {
_ = alloc;
_ = cp;
_ = backing;
_ = tracking;
const block = data.block;
data.is_symbol = data.general_category == .other_private_use or
block == .arrows or
block == .dingbats or
block == .emoticons or
block == .miscellaneous_symbols or
block == .enclosed_alphanumerics or
block == .enclosed_alphanumeric_supplement or
block == .miscellaneous_symbols_and_pictographs or
block == .transport_and_map_symbols;
}
const is_symbol = config.Extension{
.inputs = &.{ "block", "general_category" },
.compute = &computeIsSymbol,
.fields = &.{
.{ .name = "is_symbol", .type = bool },
},
};
pub const get_components: []const config.Component = &.{};
pub const tables = [_]config.Table{
.{
.name = "runtime",
.extensions = &.{},
.fields = &.{
d.field("is_emoji_presentation"),
d.field("case_folding_full"),
"is_emoji_presentation",
"case_folding_full",
},
},
.{
// Fields that libvaxis needs that aren't included in the `runtime`
// table.
.name = "libvaxis_only",
.fields = &.{
"east_asian_width",
"general_category",
"grapheme_break",
},
},
.{
.name = "buildtime",
.extensions = &.{
wcwidth,
grapheme_break_no_control,
width,
is_symbol,
},
.fields = &.{
width.field("width"),
wcwidth.field("wcwidth_zero_in_grapheme"),
grapheme_break_no_control.field("grapheme_break_no_control"),
is_symbol.field("is_symbol"),
d.field("is_emoji_vs_base"),
"width",
"wcwidth_zero_in_grapheme",
"grapheme_break_no_control",
"is_symbol",
"is_emoji_vs_base",
},
},
};
const WidthComponent = struct {
pub fn build(
comptime InputRow: type,
comptime Row: type,
allocator: std.mem.Allocator,
io: std.Io,
inputs: config.MultiSlice(InputRow),
rows: *config.MultiSlice(Row),
backing: anytype,
tracking: anytype,
) !void {
_ = allocator;
_ = io;
_ = backing;
_ = tracking;
rows.len = config.num_code_points;
const items = rows.items(.width);
const standalone = inputs.items(.wcwidth_standalone);
const zero_in_grapheme = inputs.items(.wcwidth_zero_in_grapheme);
const is_emoji_modifier = inputs.items(.is_emoji_modifier);
const grapheme_break_no_control = inputs.items(.grapheme_break_no_control);
// This condition is needed as Ghostty currently has a singular concept for
// the `width` of a code point, while `uucode` splits the concept into
// `wcwidth_standalone` and `wcwidth_zero_in_grapheme`. The two cases where
// we want to use the `wcwidth_standalone` despite the code point occupying
// zero width in a grapheme (`wcwidth_zero_in_grapheme`) are emoji
// modifiers and prepend code points. For emoji modifiers we want to
// support displaying them in isolation as color patches, and if prepend
// characters were to be width 0 they would disappear from the output with
// Ghostty's current width 0 handling. Future work will take advantage of
// the new uucode `wcwidth_standalone` vs `wcwidth_zero_in_grapheme` split.
for (0..config.num_code_points) |i| {
if (zero_in_grapheme[i] and !is_emoji_modifier[i] and grapheme_break_no_control[i] != .prepend) {
items[i] = 0;
} else {
items[i] = @min(2, standalone[i]);
}
}
}
};
const IsSymbolComponent = struct {
pub fn build(
comptime InputRow: type,
comptime Row: type,
allocator: std.mem.Allocator,
io: std.Io,
inputs: config.MultiSlice(InputRow),
rows: *config.MultiSlice(Row),
backing: anytype,
tracking: anytype,
) !void {
_ = allocator;
_ = io;
_ = backing;
_ = tracking;
rows.len = config.num_code_points;
const items = rows.items(.is_symbol);
const block = inputs.items(.block);
const general_category = inputs.items(.general_category);
for (0..config.num_code_points) |i| {
items[i] =
general_category[i] == .other_private_use or
block[i] == .arrows or
block[i] == .dingbats or
block[i] == .emoticons or
block[i] == .miscellaneous_symbols or
block[i] == .enclosed_alphanumerics or
block[i] == .enclosed_alphanumeric_supplement or
block[i] == .miscellaneous_symbols_and_pictographs or
block[i] == .transport_and_map_symbols;
}
}
};

View File

@ -11,16 +11,17 @@
const std = @import("std");
const testing = std.testing;
const Allocator = std.mem.Allocator;
const argsAlloc = @import("args.zig").argsAlloc;
pub fn main() !void {
pub fn main(init: std.process.Init) !void {
// This is a one-off patcher, so we leak all our memory on purpose
// and let the OS clean it up when we exit.
var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
const alloc = gpa.allocator();
// Parse args: program input output
const args = try std.process.argsAlloc(alloc);
defer std.process.argsFree(alloc, args);
const args = try argsAlloc(alloc, init.minimal.args);
defer alloc.free(args);
if (args.len != 3) {
std.log.err("usage: wasm_growable_table <input.wasm> <output.wasm>", .{});
std.process.exit(1);
@ -30,7 +31,8 @@ pub fn main() !void {
// Patch the file.
const output: []const u8 = try patchTableGrowable(
alloc,
try std.fs.cwd().readFileAlloc(
try std.Io.Dir.cwd().readFileAlloc(
init.io,
alloc,
args[1],
std.math.maxInt(usize),
@ -38,9 +40,9 @@ pub fn main() !void {
);
// Write our output
const out_file = try std.fs.cwd().createFile(args[2], .{});
defer out_file.close();
try out_file.writeAll(output);
const out_file = try std.Io.Dir.cwd().createFile(init.io, args[2], .{});
defer out_file.close(init.io);
try out_file.writePositionalAll(init.io, output);
}
/// Patch the WASM binary's table section to remove the maximum size

View File

@ -1,9 +1,9 @@
const std = @import("std");
const helpgen_actions = @import("../../input/helpgen_actions.zig");
pub fn main() !void {
pub fn main(init: std.process.Init) !void {
var buffer: [2048]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&buffer);
var stdout_writer = std.Io.File.stdout().writer(init.io, &buffer);
const stdout = &stdout_writer.interface;
try helpgen_actions.generate(stdout, .markdown, true, std.heap.page_allocator);
}

View File

@ -2,9 +2,9 @@ const std = @import("std");
const Action = @import("../../cli/ghostty.zig").Action;
const help_strings = @import("help_strings");
pub fn main() !void {
pub fn main(init: std.process.Init) !void {
var buffer: [2048]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&buffer);
var stdout_writer = std.Io.File.stdout().writer(init.io, &buffer);
const stdout = &stdout_writer.interface;
try genActions(stdout);
}

View File

@ -2,9 +2,9 @@ const std = @import("std");
const Config = @import("../../config/Config.zig");
const help_strings = @import("help_strings");
pub fn main() !void {
pub fn main(init: std.process.Init) !void {
var buffer: [2048]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&buffer);
var stdout_writer = std.Io.File.stdout().writer(init.io, &buffer);
const stdout = &stdout_writer.interface;
try genConfig(stdout);
}

View File

@ -15,7 +15,7 @@ child: ?std.process.Child = null,
/// The buffered file writer used for both the pager pipe and direct
/// stdout paths.
file_writer: std.fs.File.Writer = undefined,
file_writer: std.Io.File.Writer = undefined,
/// Initialize the pager. If stdout is a TTY, this spawns the pager
/// process. Otherwise, output goes directly to stdout.
@ -26,9 +26,9 @@ pub fn init(alloc: Allocator) Pager {
/// Writes to the pager process if available; otherwise, stdout.
pub fn writer(self: *Pager, buffer: []u8) *std.Io.Writer {
if (self.child) |child| {
self.file_writer = child.stdin.?.writer(buffer);
self.file_writer = child.stdin.?.writer(std.Io.Threaded.global_single_threaded.io(), buffer);
} else {
self.file_writer = std.fs.File.stdout().writer(buffer);
self.file_writer = std.Io.File.stdout().writer(std.Io.Threaded.global_single_threaded.io(), buffer);
}
return &self.file_writer.interface;
}
@ -40,18 +40,18 @@ pub fn deinit(self: *Pager) void {
// pager sees EOF, then wait for it to exit.
self.file_writer.interface.flush() catch {};
if (child.stdin) |stdin| {
stdin.close();
stdin.close(std.Io.Threaded.global_single_threaded.io());
child.stdin = null;
}
_ = child.wait() catch {};
_ = child.wait(std.Io.Threaded.global_single_threaded.io()) catch {};
}
self.* = undefined;
}
fn initPager(alloc: Allocator) ?std.process.Child {
const stdout_file: std.fs.File = .stdout();
if (!stdout_file.isTty()) return null;
const stdout_file: std.Io.File = .stdout();
if (!(stdout_file.isTty(std.Io.Threaded.global_single_threaded.io()) catch return null)) return null;
// Resolve the pager command: $GHOSTTY_PAGER > $PAGER > `less`.
// An empty value for either env var disables paging.
@ -68,13 +68,12 @@ fn initPager(alloc: Allocator) ?std.process.Child {
if (cmd == null) return null;
var child: std.process.Child = .init(&.{cmd.?}, alloc);
child.stdin_behavior = .Pipe;
child.stdout_behavior = .Inherit;
child.stderr_behavior = .Inherit;
child.spawn() catch return null;
return child;
return std.process.spawn(std.Io.Threaded.global_single_threaded.io(), .{
.argv = &.{cmd.?},
.stdin = .pipe,
.stdout = .inherit,
.stderr = .inherit,
}) catch null;
}
test "pager: non-tty" {

View File

@ -1,5 +1,6 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const compat_args = @import("../lib/compat/args.zig");
pub const DetectError = error{
/// Multiple actions were detected. You can specify at most one
@ -12,7 +13,7 @@ pub const DetectError = error{
/// Detect the action from CLI args.
pub fn detectArgs(comptime E: type, alloc: Allocator) !?E {
var iter = try std.process.argsWithAllocator(alloc);
var iter = try compat_args.getArgs().iterateAllocator(alloc);
defer iter.deinit();
return try detectIter(E, &iter);
}
@ -83,7 +84,7 @@ test "detect direct match" {
const alloc = testing.allocator;
const Enum = enum { foo, bar, baz };
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"+foo",
);
@ -97,7 +98,7 @@ test "detect invalid match" {
const alloc = testing.allocator;
const Enum = enum { foo, bar, baz };
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"+invalid",
);
@ -113,7 +114,7 @@ test "detect multiple actions" {
const alloc = testing.allocator;
const Enum = enum { foo, bar, baz };
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"+foo +bar",
);
@ -129,7 +130,7 @@ test "detect no match" {
const alloc = testing.allocator;
const Enum = enum { foo, bar, baz };
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"--some-flag",
);
@ -154,7 +155,7 @@ test "detect special case action" {
};
{
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"--special +bar",
);
@ -164,7 +165,7 @@ test "detect special case action" {
}
{
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"+bar --special",
);
@ -174,7 +175,7 @@ test "detect special case action" {
}
{
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"+bar",
);
@ -200,7 +201,7 @@ test "detect special case fallback" {
};
{
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"--special",
);
@ -210,7 +211,7 @@ test "detect special case fallback" {
}
{
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"+bar --special",
);
@ -220,7 +221,7 @@ test "detect special case fallback" {
}
{
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"--special +bar",
);
@ -246,7 +247,7 @@ test "detect special case abort_if_no_action" {
};
{
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"-e",
);
@ -256,7 +257,7 @@ test "detect special case abort_if_no_action" {
}
{
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"+foo -e",
);
@ -266,7 +267,7 @@ test "detect special case abort_if_no_action" {
}
{
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"-e +bar",
);

View File

@ -5,6 +5,7 @@ const Allocator = mem.Allocator;
const ArenaAllocator = std.heap.ArenaAllocator;
const diags = @import("diagnostics.zig");
const internal_os = @import("../os/main.zig");
const compat_args = @import("../lib/compat/args.zig");
const Diagnostic = diags.Diagnostic;
const DiagnosticList = diags.DiagnosticList;
const CommaSplitter = @import("CommaSplitter.zig");
@ -489,18 +490,13 @@ pub fn parseTaggedUnion(comptime T: type, alloc: Allocator, v: []const u8) !T {
// We need to create a struct that looks like this union field.
// This lets us use parseIntoField as if its a dedicated struct.
const Target = @Type(.{ .@"struct" = .{
.layout = .auto,
.fields = &.{.{
.name = field.name,
.type = field.type,
.default_value_ptr = null,
.is_comptime = false,
.alignment = @alignOf(field.type),
}},
.decls = &.{},
.is_tuple = false,
} });
const Target = @Struct(
.auto,
null,
&.{field.name},
&.{field.type},
&.{.{ .@"align" = @alignOf(field.type) }},
);
// Parse the value into the struct
var t: Target = undefined;
@ -677,7 +673,7 @@ test "parse: simple" {
} = .{};
defer if (data._arena) |arena| arena.deinit();
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
testing.allocator,
"--a=42 --b --b-f=false",
);
@ -689,7 +685,7 @@ test "parse: simple" {
try testing.expect(!data.@"b-f");
// Reparsing works
var iter2 = try std.process.ArgIteratorGeneral(.{}).init(
var iter2 = try compat_args.ArgIteratorGeneral(.{}).init(
testing.allocator,
"--a=84",
);
@ -711,7 +707,7 @@ test "parse: quoted value" {
} = .{};
defer if (data._arena) |arena| arena.deinit();
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
testing.allocator,
"--a=\"42\" --b=\"hello!\"",
);
@ -731,7 +727,7 @@ test "parse: empty value resets to default" {
} = .{};
defer if (data._arena) |arena| arena.deinit();
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
testing.allocator,
"--a= --b=",
);
@ -750,7 +746,7 @@ test "parse: positional arguments are invalid" {
} = .{};
defer if (data._arena) |arena| arena.deinit();
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
testing.allocator,
"--a=84 what",
);
@ -774,7 +770,7 @@ test "parse: diagnostic tracking" {
} = .{};
defer if (data._arena) |arena| arena.deinit();
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
testing.allocator,
"--what --a=42",
);
@ -858,7 +854,7 @@ test "parse: compatibility handler" {
} = .{};
defer if (data._arena) |arena| arena.deinit();
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
testing.allocator,
"--a=yuh",
);
@ -884,7 +880,7 @@ test "parse: compatibility renamed" {
} = .{};
defer if (data._arena) |arena| arena.deinit();
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
testing.allocator,
"--old=true --b=true",
);
@ -1373,7 +1369,7 @@ pub fn argsIterator(alloc_gpa: Allocator) internal_os.args.ArgIterator.InitError
test "ArgsIterator" {
const testing = std.testing;
const child = try std.process.ArgIteratorGeneral(.{}).init(
const child = try compat_args.ArgIteratorGeneral(.{}).init(
testing.allocator,
"--what +list-things --a=42",
);

View File

@ -3,6 +3,7 @@ const builtin = @import("builtin");
const args = @import("args.zig");
const Action = @import("ghostty.zig").Action;
const Allocator = std.mem.Allocator;
const compat_env = @import("../lib/compat/env.zig");
const vaxis = @import("vaxis");
const framedata = @import("framedata").compressed;
@ -173,6 +174,9 @@ const Boo = struct {
/// The `boo` command is used to display the animation from the Ghostty website in the terminal
pub fn run(gpa: Allocator) !u8 {
var env_map = try compat_env.getEnvMap(gpa);
defer env_map.deinit();
// Disable on non-desktop systems.
switch (builtin.os.tag) {
.windows, .macos, .linux, .freebsd => {},
@ -194,7 +198,7 @@ pub fn run(gpa: Allocator) !u8 {
gpa.free(decompressed_data);
}
var app = try vxfw.App.init(gpa);
var app = try vxfw.App.init(std.Io.Threaded.global_single_threaded.io(), gpa, &env_map, &.{});
defer app.deinit();
var boo: Boo = undefined;

View File

@ -39,8 +39,8 @@ pub fn run(alloc_gpa: Allocator) !u8 {
}
var buffer: [1024]u8 = undefined;
var stdout_file: std.fs.File = .stdout();
var stdout_writer = stdout_file.writer(&buffer);
var stdout_file: std.Io.File = .stdout();
var stdout_writer = stdout_file.writer(std.Io.Threaded.global_single_threaded.io(), &buffer);
const stdout = &stdout_writer.interface;
const result = runInner(alloc, &stdout_file, stdout);
@ -50,7 +50,7 @@ pub fn run(alloc_gpa: Allocator) !u8 {
fn runInner(
alloc: Allocator,
stdout_file: *std.fs.File,
stdout_file: *std.Io.File,
stdout: *std.Io.Writer,
) !u8 {
const crash_dir = try crash.defaultDir(alloc);
@ -66,7 +66,7 @@ fn runInner(
// If we have no reports, then we're done. If we have a tty then we
// print a message, otherwise we do nothing.
if (reports.items.len == 0) {
if (std.posix.isatty(stdout_file.handle)) {
if (try stdout_file.isTty(std.Io.Threaded.global_single_threaded.io())) {
try stdout.writeAll("No crash reports! 👻\n");
}
return 0;
@ -76,7 +76,7 @@ fn runInner(
for (reports.items) |report| {
var buf: [128]u8 = undefined;
const now = std.time.nanoTimestamp();
const now = std.Io.Timestamp.now(std.Io.Threaded.global_single_threaded.io(), .real).toNanoseconds();
const diff = now - report.mtime;
const since = if (diff <= 0) "now" else s: {
const d = Config.Duration{ .duration = @intCast(diff) };

View File

@ -93,7 +93,7 @@ pub const Location = union(enum) {
/// and potentially in the future structure them differently.
pub const DiagnosticList = struct {
/// The list of diagnostics.
list: std.ArrayListUnmanaged(Diagnostic) = .{},
list: std.ArrayList(Diagnostic) = .empty,
/// Precomputed data for diagnostics. This is used specifically
/// when we build libghostty so that we can precompute the messages
@ -111,7 +111,7 @@ pub const DiagnosticList = struct {
};
const Precompute = if (precompute_enabled) struct {
messages: std.ArrayListUnmanaged([:0]const u8) = .{},
messages: std.ArrayList([:0]const u8) = .empty,
pub fn clone(
self: *const Precompute,

View File

@ -6,6 +6,7 @@ const Allocator = std.mem.Allocator;
const Action = @import("ghostty.zig").Action;
const configpkg = @import("../config.zig");
const internal_os = @import("../os/main.zig");
const compat_exec = @import("../lib/compat/exec.zig");
const Config = configpkg.Config;
pub const Options = struct {
@ -48,7 +49,7 @@ pub fn run(alloc: Allocator) !u8 {
// critical where setting up the defer cleanup is a problem.
var buffer: [1024]u8 = undefined;
var stderr_writer = std.fs.File.stderr().writer(&buffer);
var stderr_writer = std.Io.File.stderr().writer(std.Io.Threaded.global_single_threaded.io(), &buffer);
const stderr = &stderr_writer.interface;
var opts: Options = .{};
@ -137,7 +138,7 @@ fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 {
}
const command = command: {
var buffer: std.io.Writer.Allocating = .init(alloc);
var buffer: std.Io.Writer.Allocating = .init(alloc);
defer buffer.deinit();
const writer = &buffer.writer;
try writer.writeAll(editor);
@ -158,7 +159,7 @@ fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 {
// so this is not a big deal.
comptime assert(builtin.link_libc);
const err = std.posix.execvpeZ(
const err = compat_exec.execvpeZ(
"/bin/sh",
&.{ "/bin/sh", "-c", command },
std.c.environ,

View File

@ -75,9 +75,9 @@ pub fn run(alloc: Allocator) !u8 {
// respective lookup. A bare positional argument tries config
// options first, then keybind actions as a fallback.
const name = keybind_name orelse option_name orelse positional orelse {
var stderr: std.fs.File = .stderr();
var stderr: std.Io.File = .stderr();
var buffer: [4096]u8 = undefined;
var stderr_writer = stderr.writer(&buffer);
var stderr_writer = stderr.writer(std.Io.Threaded.global_single_threaded.io(), &buffer);
try stderr_writer.interface.writeAll("Usage: ghostty +explain-config <option>\n");
try stderr_writer.interface.writeAll(" ghostty +explain-config --option=<option>\n");
try stderr_writer.interface.writeAll(" ghostty +explain-config --keybind=<action>\n");

View File

@ -2,6 +2,7 @@ const std = @import("std");
const Allocator = std.mem.Allocator;
const help_strings = @import("help_strings");
const actionpkg = @import("action.zig");
const compat_args = @import("../lib/compat/args.zig");
const SpecialCase = actionpkg.SpecialCase;
const list_fonts = @import("list_fonts.zig");
@ -112,7 +113,10 @@ pub const Action = enum {
if (std.mem.eql(u8, field.name, @tagName(self))) {
var buffer: [1024]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&buffer);
var stdout_writer = std.Io.File.stdout().writer(
std.Io.Threaded.global_single_threaded.io(),
&buffer,
);
const stdout = &stdout_writer.interface;
const text = @field(help_strings.Action, field.name) ++ "\n";
stdout.writeAll(text) catch |write_err| {
@ -201,7 +205,7 @@ test "parse action none" {
const testing = std.testing;
const alloc = testing.allocator;
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"--a=42 --b --b-f=false",
);
@ -215,7 +219,7 @@ test "parse action version" {
const alloc = testing.allocator;
{
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"--a=42 --b --b-f=false --version",
);
@ -225,7 +229,7 @@ test "parse action version" {
}
{
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"--version --a=42 --b --b-f=false",
);
@ -235,7 +239,7 @@ test "parse action version" {
}
{
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"--c=84 --d --version --a=42 --b --b-f=false",
);
@ -250,7 +254,7 @@ test "parse action plus" {
const alloc = testing.allocator;
{
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"--a=42 --b --b-f=false +version",
);
@ -260,7 +264,7 @@ test "parse action plus" {
}
{
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"+version --a=42 --b --b-f=false",
);
@ -270,7 +274,7 @@ test "parse action plus" {
}
{
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"--c=84 --d +version --a=42 --b --b-f=false",
);
@ -285,7 +289,7 @@ test "parse action plus ignores -e" {
const alloc = testing.allocator;
{
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"--a=42 -e +version",
);
@ -295,7 +299,7 @@ test "parse action plus ignores -e" {
}
{
var iter = try std.process.ArgIteratorGeneral(.{}).init(
var iter = try compat_args.ArgIteratorGeneral(.{}).init(
alloc,
"+list-fonts --a=42 -e +version",
);

View File

@ -31,7 +31,7 @@ pub fn run(alloc: Allocator) !u8 {
}
var buffer: [2048]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&buffer);
var stdout_writer = std.Io.File.stdout().writer(std.Io.Threaded.global_single_threaded.io(), &buffer);
const stdout = &stdout_writer.interface;
try stdout.writeAll(
\\Usage: ghostty [+action] [options]

View File

@ -37,9 +37,9 @@ pub fn run(alloc: Allocator) !u8 {
try args.parse(Options, alloc, &opts, &iter);
}
var stdout: std.fs.File = .stdout();
var stdout: std.Io.File = .stdout();
var buffer: [4096]u8 = undefined;
var stdout_writer = stdout.writer(&buffer);
var stdout_writer = stdout.writer(std.Io.Threaded.global_single_threaded.io(), &buffer);
try helpgen_actions.generate(
&stdout_writer.interface,
.plaintext,

View File

@ -6,6 +6,7 @@ const args = @import("args.zig");
const x11_color = @import("../terminal/main.zig").x11_color;
const vaxis = @import("vaxis");
const tui = @import("tui.zig");
const compat_env = @import("../lib/compat/env.zig");
pub const Options = struct {
pub fn deinit(self: Options) void {
@ -49,15 +50,14 @@ pub fn run(alloc: Allocator) !u8 {
}
}.lessThan);
// Despite being under the posix namespace, this also works on Windows as of zig 0.13.0
var stdout: std.fs.File = .stdout();
if (tui.can_pretty_print and !opts.plain and std.posix.isatty(stdout.handle)) {
var stdout: std.Io.File = .stdout();
if (tui.can_pretty_print and !opts.plain and try stdout.isTty(std.Io.Threaded.global_single_threaded.io())) {
var arena = std.heap.ArenaAllocator.init(alloc);
defer arena.deinit();
return prettyPrint(arena.allocator(), keys.items);
} else {
var buffer: [4096]u8 = undefined;
var stdout_writer = stdout.writer(&buffer);
var stdout_writer = stdout.writer(std.Io.Threaded.global_single_threaded.io(), &buffer);
const writer = &stdout_writer.interface;
for (keys.items) |name| {
const rgb = x11_color.map.get(name).?;
@ -74,11 +74,14 @@ pub fn run(alloc: Allocator) !u8 {
}
fn prettyPrint(alloc: Allocator, keys: [][]const u8) !u8 {
var env_map = try compat_env.getEnvMap(alloc);
defer env_map.deinit();
// Set up vaxis
var buf: [1024]u8 = undefined;
var tty = try vaxis.Tty.init(&buf);
var tty = try vaxis.Tty.init(std.Io.Threaded.global_single_threaded.io(), &buf);
defer tty.deinit();
var vx = try vaxis.init(alloc, .{});
var vx = try vaxis.init(std.Io.Threaded.global_single_threaded.io(), alloc, &env_map, .{});
defer vx.deinit(alloc, tty.writer());
// We know we are ghostty, so let's enable mode 2027. Vaxis normally does this but you need an
@ -97,7 +100,7 @@ fn prettyPrint(alloc: Allocator, keys: [][]const u8) !u8 {
.y_pixel = 768,
},
else => try vaxis.Tty.getWinsize(tty.fd),
else => try tty.getWinsize(),
};
try vx.resize(alloc, tty.writer(), winsize);

View File

@ -92,7 +92,7 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
}
var buffer: [2048]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&buffer);
var stdout_writer = std.Io.File.stdout().writer(std.Io.Threaded.global_single_threaded.io(), &buffer);
const stdout = &stdout_writer.interface;
// We'll be putting our fonts into a list categorized by family
@ -133,7 +133,7 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
const gop = try map.getOrPut(family);
if (!gop.found_existing) {
try families.append(alloc, family);
gop.value_ptr.* = .{};
gop.value_ptr.* = .empty;
}
try gop.value_ptr.append(alloc, full_name);
}

View File

@ -10,6 +10,7 @@ const vaxis = @import("vaxis");
const input = @import("../input.zig");
const tui = @import("tui.zig");
const Binding = input.Binding;
const compat_env = @import("../lib/compat/env.zig");
pub const Options = struct {
/// If `true`, print out the default keybinds instead of the ones configured
@ -65,11 +66,11 @@ pub fn run(alloc: Allocator) !u8 {
defer config.deinit();
var buffer: [1024]u8 = undefined;
const stdout: std.fs.File = .stdout();
var stdout_writer = stdout.writer(&buffer);
const stdout: std.Io.File = .stdout();
var stdout_writer = stdout.writer(std.Io.Threaded.global_single_threaded.io(), &buffer);
const writer = &stdout_writer.interface;
if (tui.can_pretty_print and !opts.plain and stdout.isTty()) {
if (tui.can_pretty_print and !opts.plain and try stdout.isTty(std.Io.Threaded.global_single_threaded.io())) {
var arena = std.heap.ArenaAllocator.init(alloc);
defer arena.deinit();
return prettyPrint(arena.allocator(), config.keybind);
@ -218,11 +219,14 @@ const ChordBinding = struct {
};
fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 {
var env_map = try compat_env.getEnvMap(alloc);
defer env_map.deinit();
// Set up vaxis
var buf: [1024]u8 = undefined;
var tty = try vaxis.Tty.init(&buf);
var tty = try vaxis.Tty.init(std.Io.Threaded.global_single_threaded.io(), &buf);
defer tty.deinit();
var vx = try vaxis.init(alloc, .{});
var vx = try vaxis.init(std.Io.Threaded.global_single_threaded.io(), alloc, &env_map, .{});
const writer = tty.writer();
defer vx.deinit(alloc, writer);
@ -242,7 +246,7 @@ fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 {
.y_pixel = 768,
},
else => try vaxis.Tty.getWinsize(tty.fd),
else => try tty.getWinsize(),
};
try vx.resize(alloc, writer, winsize);

View File

@ -6,6 +6,7 @@ const configpkg = @import("../config.zig");
const themepkg = @import("../config/theme.zig");
const tui = @import("tui.zig");
const global_state = &@import("../global.zig").state;
const compat_env = @import("../lib/compat/env.zig");
const vaxis = @import("vaxis");
const zf = @import("zf");
@ -117,12 +118,12 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 {
const alloc = arena.allocator();
var stdout_buf: [4096]u8 = undefined;
var stdout_file: std.fs.File = .stdout();
var stdout_writer = stdout_file.writer(&stdout_buf);
var stdout_file: std.Io.File = .stdout();
var stdout_writer = stdout_file.writer(std.Io.Threaded.global_single_threaded.io(), &stdout_buf);
const stdout = &stdout_writer.interface;
var stderr_buf: [4096]u8 = undefined;
var stderr_writer = std.fs.File.stderr().writer(&stderr_buf);
var stderr_writer = std.Io.File.stderr().writer(std.Io.Threaded.global_single_threaded.io(), &stderr_buf);
const stderr = &stderr_writer.interface;
const resources_dir = global_state.resources_dir.app();
@ -137,18 +138,22 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 {
var it: themepkg.LocationIterator = .{ .arena_alloc = arena.allocator() };
while (try it.next()) |loc| {
var dir = std.fs.cwd().openDir(loc.dir, .{ .iterate = true }) catch |err| switch (err) {
var dir = std.Io.Dir.cwd().openDir(
std.Io.Threaded.global_single_threaded.io(),
loc.dir,
.{ .iterate = true },
) catch |err| switch (err) {
error.FileNotFound => continue,
else => {
std.debug.print("error trying to open {s}: {}\n", .{ loc.dir, err });
continue;
},
};
defer dir.close();
defer dir.close(std.Io.Threaded.global_single_threaded.io());
var walker = dir.iterate();
while (try walker.next()) |entry| {
while (try walker.next(std.Io.Threaded.global_single_threaded.io())) |entry| {
switch (entry.kind) {
.file, .sym_link => {
if (std.mem.eql(u8, entry.name, ".DS_Store"))
@ -174,7 +179,7 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 {
std.mem.sortUnstable(ThemeListElement, themes.items, {}, ThemeListElement.lessThan);
if (tui.can_pretty_print and !opts.plain and stdout_file.isTty()) {
if (tui.can_pretty_print and !opts.plain and try stdout_file.isTty(std.Io.Threaded.global_single_threaded.io())) {
try preview(gpa_alloc, themes.items, opts.color);
return 0;
}
@ -210,14 +215,18 @@ fn writeAutoThemeFile(alloc: std.mem.Allocator, theme_name: []const u8) !void {
defer alloc.free(auto_path);
if (std.fs.path.dirname(auto_path)) |dir| {
try std.fs.cwd().makePath(dir);
try std.Io.Dir.cwd().createDirPath(std.Io.Threaded.global_single_threaded.io(), dir);
}
var f = try std.fs.createFileAbsolute(auto_path, .{ .truncate = true });
defer f.close();
var f = try std.Io.Dir.createFileAbsolute(
std.Io.Threaded.global_single_threaded.io(),
auto_path,
.{ .truncate = true },
);
defer f.close(std.Io.Threaded.global_single_threaded.io());
var buf: [128]u8 = undefined;
var w = f.writer(&buf);
var w = f.writer(std.Io.Threaded.global_single_threaded.io(), &buf);
try w.interface.print("theme = {s}\n", .{theme_name});
try w.interface.flush();
}
@ -256,13 +265,16 @@ const Preview = struct {
theme_filter: ColorScheme,
buf: []u8,
) !*Preview {
var env_map = try compat_env.getEnvMap(allocator);
defer env_map.deinit();
const self = try allocator.create(Preview);
self.* = .{
.allocator = allocator,
.should_quit = false,
.tty = try .init(buf),
.vx = try vaxis.init(allocator, .{}),
.tty = try .init(std.Io.Threaded.global_single_threaded.io(), buf),
.vx = try vaxis.init(std.Io.Threaded.global_single_threaded.io(), allocator, &env_map, .{}),
.mouse = null,
.themes = themes,
.filtered = try .initCapacity(allocator, themes.len),
@ -290,18 +302,12 @@ const Preview = struct {
}
pub fn run(self: *Preview) !void {
var loop: vaxis.Loop(Event) = .{
.tty = &self.tty,
.vaxis = &self.vx,
};
try loop.init();
try loop.start();
var loop: vaxis.Loop(Event) = .init(std.Io.Threaded.global_single_threaded.io(), &self.tty, &self.vx);
const writer = self.tty.writer();
try self.vx.enterAltScreen(writer);
try self.vx.setTitle(writer, "👻 Ghostty Theme Preview 👻");
try self.vx.queryTerminal(writer, 1 * std.time.ns_per_s);
try self.vx.queryTerminal(writer, .fromSeconds(1));
try self.vx.setMouseMode(writer, true);
if (self.vx.caps.color_scheme_updates)
try self.vx.subscribeToColorSchemeUpdates(writer);
@ -311,8 +317,8 @@ const Preview = struct {
defer arena.deinit();
const alloc = arena.allocator();
loop.pollEvent();
while (loop.tryEvent()) |event| {
try loop.pollEvent();
while (try loop.tryEvent()) |event| {
try self.update(event, alloc);
}
try self.draw(alloc);
@ -366,7 +372,12 @@ const Preview = struct {
if (!shouldIncludeTheme(self.theme_filter, theme_config)) continue;
theme.rank = zf.rank(theme.theme, tokens.items, .{
.to_lower = true,
// NOTE: Changed from ".to_lower = true" (the option was
// renamed). I think this is the correct analog for case
// insensitive ranking (which is what I'm guessing
// ".to_lower = true" implies, but this comment serves as a
// hint if there's a regression.
.case_sensitive = false,
.plain = true,
});
if (theme.rank != null) try self.filtered.append(self.allocator, i);
@ -620,13 +631,21 @@ const Preview = struct {
self.down(1);
}
if (theme_list.hasMouse(mouse)) |_| {
// NOTE: mouse co-ordinates can be negative (see
// https://github.com/rockorager/libvaxis/pull/276).
// Working around it in this case, but putting this here in
// case there are issues.
if (mouse.button == .left and mouse.type == .release) {
const selection = self.window + mouse.row;
const selection: usize = selection: {
var window: i32 = @min(self.window, std.math.maxInt(i32));
window += mouse.row;
break :selection @max(0, window);
};
if (selection < self.filtered.items.len) {
self.current = selection;
}
}
highlight = mouse.row;
highlight = @max(0, mouse.row);
}
}
}

View File

@ -9,6 +9,9 @@ const lib = @import("../lib/main.zig");
const homedir = @import("../os/homedir.zig");
pub const Options = struct {
/// We store an I/O implementation to make maintenance easier.
_io: std.Io,
/// This is set by the CLI parser for deinit.
_arena: ?ArenaAllocator = null,
@ -26,8 +29,18 @@ pub const Options = struct {
/// there is a "normal" config setting on the cli.
_diagnostics: diagnostics.DiagnosticList = .{},
pub const ParseManuallyHookError = error{InvalidValue} ||
homedir.ExpandError ||
std.Io.Dir.RealPathFileAllocError ||
Allocator.Error;
/// Manual parse hook, collect all of the arguments after `+new-window`.
pub fn parseManuallyHook(self: *Options, alloc: Allocator, arg: []const u8, iter: anytype) (error{InvalidValue} || homedir.ExpandError || std.fs.Dir.RealPathAllocError || Allocator.Error)!bool {
pub fn parseManuallyHook(
self: *Options,
alloc: Allocator,
arg: []const u8,
iter: anytype,
) ParseManuallyHookError!bool {
var e_seen: bool = std.mem.eql(u8, arg, "-e");
// Include the argument that triggered the manual parse hook.
@ -50,7 +63,12 @@ pub const Options = struct {
return false;
}
fn checkArg(self: *Options, alloc: Allocator, arg: []const u8) (error{InvalidValue} || homedir.ExpandError || std.fs.Dir.RealPathAllocError || Allocator.Error)!?[:0]const u8 {
const CheckArgError = error{InvalidValue} ||
homedir.ExpandError ||
std.Io.Dir.RealPathFileAllocError ||
Allocator.Error;
fn checkArg(self: *Options, alloc: Allocator, arg: []const u8) CheckArgError!?[:0]const u8 {
if (lib.cutPrefix(u8, arg, "--class=")) |rest| {
self.class = try alloc.dupeZ(u8, std.mem.trim(u8, rest, &std.ascii.whitespace));
return null;
@ -60,11 +78,11 @@ pub const Options = struct {
const stripped = std.mem.trim(u8, rest, &std.ascii.whitespace);
if (std.mem.eql(u8, stripped, "home")) return try alloc.dupeZ(u8, arg);
if (std.mem.eql(u8, stripped, "inherit")) return try alloc.dupeZ(u8, arg);
const cwd: std.fs.Dir = std.fs.cwd();
const cwd: std.Io.Dir = .cwd();
var expandhome_buf: [std.fs.max_path_bytes]u8 = undefined;
const expanded = try homedir.expandHome(stripped, &expandhome_buf);
var realpath_buf: [std.fs.max_path_bytes]u8 = undefined;
const realpath = try cwd.realpath(expanded, &realpath_buf);
const realpath = realpath_buf[0..try cwd.realPathFile(self._io, expanded, &realpath_buf)];
self._working_directory_seen = true;
return try std.fmt.allocPrintSentinel(alloc, "--working-directory={s}", .{realpath}, 0);
}
@ -151,7 +169,7 @@ pub fn run(alloc: Allocator) !u8 {
defer iter.deinit();
var buffer: [1024]u8 = undefined;
var stderr_writer = std.fs.File.stderr().writer(&buffer);
var stderr_writer = std.Io.File.stderr().writer(std.Io.Threaded.global_single_threaded.io(), &buffer);
const stderr = &stderr_writer.interface;
const result = runArgs(alloc, &iter, stderr);
@ -164,7 +182,7 @@ fn runArgs(
argsIter: anytype,
stderr: *std.Io.Writer,
) !u8 {
var opts: Options = .{};
var opts: Options = .{ ._io = std.Io.Threaded.global_single_threaded.io() };
defer opts.deinit();
args.parse(Options, alloc_gpa, &opts, argsIter) catch |err| switch (err) {
@ -195,9 +213,9 @@ fn runArgs(
if (!opts._working_directory_seen) {
const alloc = opts._arena.?.allocator();
const cwd: std.fs.Dir = std.fs.cwd();
const cwd: std.Io.Dir = .cwd();
var buf: [std.fs.max_path_bytes]u8 = undefined;
const wd = try cwd.realpath(".", &buf);
const wd = buf[0..try cwd.realPath(std.Io.Threaded.global_single_threaded.io(), &buf)];
// This should be inserted at the beginning of the list, just in case `-e` was used.
try opts._arguments.insert(alloc, 0, try std.fmt.allocPrintSentinel(alloc, "--working-directory={s}", .{wd}, 0));
}

View File

@ -66,11 +66,11 @@ pub fn run(alloc: Allocator) !u8 {
defer iter.deinit();
var stdout_buffer: [1024]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
var stdout_writer = std.Io.File.stdout().writer(std.Io.Threaded.global_single_threaded.io(), &stdout_buffer);
const stdout = &stdout_writer.interface;
var stderr_buffer: [1024]u8 = undefined;
var stderr_writer = std.fs.File.stdout().writer(&stderr_buffer);
var stderr_writer = std.Io.File.stdout().writer(std.Io.Threaded.global_single_threaded.io(), &stderr_buffer);
const stderr = &stderr_writer.interface;
const result = runArgs(

View File

@ -49,7 +49,7 @@ pub fn defaultPath(
/// This removes the cache file from disk, effectively clearing all cached
/// SSH terminfo entries.
pub fn clear(self: DiskCache) !void {
std.fs.cwd().deleteFile(self.path) catch |err| switch (err) {
std.Io.Dir.cwd().deleteFile(std.Io.Threaded.global_single_threaded.io(), self.path) catch |err| switch (err) {
error.FileNotFound => {},
else => return err,
};
@ -57,11 +57,11 @@ pub fn clear(self: DiskCache) !void {
pub const AddResult = enum { added, updated };
pub const AddError = std.fs.Dir.MakeError ||
std.fs.Dir.StatFileError ||
std.fs.File.OpenError ||
std.fs.File.ChmodError ||
std.io.Reader.LimitedAllocError ||
pub const AddError = std.Io.Dir.CreateDirError ||
std.Io.Dir.StatFileError ||
std.Io.File.OpenError ||
std.Io.File.SetPermissionsError ||
std.Io.Reader.LimitedAllocError ||
FixupPermissionsError ||
ReadEntriesError ||
WriteCacheFileError ||
@ -79,36 +79,40 @@ pub fn add(
// Create cache directory if needed
if (std.fs.path.dirname(self.path)) |dir| {
std.fs.cwd().makePath(dir) catch |err| switch (err) {
std.Io.Dir.cwd().createDirPath(std.Io.Threaded.global_single_threaded.io(), dir) catch |err| switch (err) {
error.PathAlreadyExists => {},
else => return err,
};
}
// Open or create cache file with secure permissions
const file = std.fs.createFileAbsolute(self.path, .{
const file = std.Io.Dir.createFileAbsolute(std.Io.Threaded.global_single_threaded.io(), self.path, .{
.read = true,
.truncate = false,
.mode = 0o600,
.permissions = if (std.posix.mode_t != u0) .fromMode(0o600) else .default_file,
}) catch |err| switch (err) {
error.PathAlreadyExists => blk: {
const existing_file = try std.fs.openFileAbsolute(
const existing_file = try std.Io.Dir.openFileAbsolute(
std.Io.Threaded.global_single_threaded.io(),
self.path,
.{ .mode = .read_write },
);
errdefer existing_file.close();
errdefer existing_file.close(std.Io.Threaded.global_single_threaded.io());
try fixupPermissions(existing_file);
break :blk existing_file;
},
else => return err,
};
defer file.close();
defer file.close(std.Io.Threaded.global_single_threaded.io());
// Lock
// Causes a compile failure in the Zig std library on Windows, see:
// https://github.com/ziglang/zig/issues/18430
if (comptime builtin.os.tag != .windows) _ = file.tryLock(.exclusive) catch return error.CacheIsLocked;
defer if (comptime builtin.os.tag != .windows) file.unlock();
if (comptime builtin.os.tag != .windows) _ = file.tryLock(
std.Io.Threaded.global_single_threaded.io(),
.exclusive,
) catch return error.CacheIsLocked;
defer if (comptime builtin.os.tag != .windows) file.unlock(std.Io.Threaded.global_single_threaded.io());
var entries = try readEntries(alloc, file);
defer deinitEntries(alloc, &entries);
@ -124,13 +128,13 @@ pub fn add(
gop.key_ptr.* = hostname_copy;
gop.value_ptr.* = .{
.hostname = gop.key_ptr.*,
.timestamp = std.time.timestamp(),
.timestamp = std.Io.Timestamp.now(std.Io.Threaded.global_single_threaded.io(), .real).toSeconds(),
.terminfo_version = terminfo_copy,
};
break :add .added;
} else update: {
// Update timestamp for existing entry
gop.value_ptr.timestamp = std.time.timestamp();
gop.value_ptr.timestamp = std.Io.Timestamp.now(std.Io.Threaded.global_single_threaded.io(), .real).toSeconds();
break :update .updated;
};
@ -138,7 +142,7 @@ pub fn add(
return result;
}
pub const RemoveError = std.fs.File.OpenError ||
pub const RemoveError = std.Io.File.OpenError ||
FixupPermissionsError ||
ReadEntriesError ||
WriteCacheFileError ||
@ -154,21 +158,25 @@ pub fn remove(
if (!isValidCacheKey(hostname)) return error.HostnameIsInvalid;
// Open our file
const file = std.fs.openFileAbsolute(
const file = std.Io.Dir.openFileAbsolute(
std.Io.Threaded.global_single_threaded.io(),
self.path,
.{ .mode = .read_write },
) catch |err| switch (err) {
error.FileNotFound => return,
else => return err,
};
defer file.close();
defer file.close(std.Io.Threaded.global_single_threaded.io());
try fixupPermissions(file);
// Lock
// Causes a compile failure in the Zig std library on Windows, see:
// https://github.com/ziglang/zig/issues/18430
if (comptime builtin.os.tag != .windows) _ = file.tryLock(.exclusive) catch return error.CacheIsLocked;
defer if (comptime builtin.os.tag != .windows) file.unlock();
if (comptime builtin.os.tag != .windows) _ = file.tryLock(
std.Io.Threaded.global_single_threaded.io(),
.exclusive,
) catch return error.CacheIsLocked;
defer if (comptime builtin.os.tag != .windows) file.unlock(std.Io.Threaded.global_single_threaded.io());
// Read existing entries
var entries = try readEntries(alloc, file);
@ -184,7 +192,7 @@ pub fn remove(
try self.writeCacheFile(entries, null);
}
pub const ContainsError = std.fs.File.OpenError ||
pub const ContainsError = std.Io.File.OpenError ||
ReadEntriesError ||
error{HostnameIsInvalid};
@ -198,14 +206,15 @@ pub fn contains(
if (!isValidCacheKey(hostname)) return error.HostnameIsInvalid;
// Open our file
const file = std.fs.openFileAbsolute(
const file = std.Io.Dir.openFileAbsolute(
std.Io.Threaded.global_single_threaded.io(),
self.path,
.{},
) catch |err| switch (err) {
error.FileNotFound => return false,
else => return err,
};
defer file.close();
defer file.close(std.Io.Threaded.global_single_threaded.io());
// Read existing entries
var entries = try readEntries(alloc, file);
@ -214,24 +223,24 @@ pub fn contains(
return entries.contains(hostname);
}
pub const FixupPermissionsError = (std.fs.File.StatError || std.fs.File.ChmodError);
pub const FixupPermissionsError = (std.Io.File.StatError || std.Io.File.SetPermissionsError);
fn fixupPermissions(file: std.fs.File) FixupPermissionsError!void {
fn fixupPermissions(file: std.Io.File) FixupPermissionsError!void {
// Windows does not support chmod
if (comptime builtin.os.tag == .windows) return;
// Ensure file has correct permissions (readable/writable by
// owner only)
const stat = try file.stat();
if (stat.mode & 0o777 != 0o600) {
try file.chmod(0o600);
const stat = try file.stat(std.Io.Threaded.global_single_threaded.io());
if (stat.permissions.toMode() & 0o777 != 0o600) {
try file.setPermissions(std.Io.Threaded.global_single_threaded.io(), .fromMode(0o600));
}
}
pub const WriteCacheFileError = std.fs.Dir.OpenError ||
std.fs.AtomicFile.InitError ||
std.fs.AtomicFile.FlushError ||
std.fs.AtomicFile.FinishError ||
pub const WriteCacheFileError = std.Io.Dir.OpenError ||
std.Io.Dir.CreateFileAtomicError ||
std.Io.File.Writer.Error ||
std.Io.File.Atomic.ReplaceError ||
Entry.FormatError ||
error{InvalidCachePath};
@ -243,24 +252,26 @@ fn writeCacheFile(
const cache_dir = std.fs.path.dirname(self.path) orelse return error.InvalidCachePath;
const cache_basename = std.fs.path.basename(self.path);
var dir = try std.fs.cwd().openDir(cache_dir, .{});
defer dir.close();
var dir = try std.Io.Dir.cwd().openDir(std.Io.Threaded.global_single_threaded.io(), cache_dir, .{});
defer dir.close(std.Io.Threaded.global_single_threaded.io());
var buf: [1024]u8 = undefined;
var atomic_file = try dir.atomicFile(cache_basename, .{
.mode = 0o600,
.write_buffer = &buf,
var atomic_file = try dir.createFileAtomic(std.Io.Threaded.global_single_threaded.io(), cache_basename, .{
.permissions = if (std.posix.mode_t != u0) .fromMode(0o600) else .default_file,
.replace = true,
});
defer atomic_file.deinit();
defer atomic_file.deinit(std.Io.Threaded.global_single_threaded.io());
var file_writer = atomic_file.file.writer(std.Io.Threaded.global_single_threaded.io(), &buf);
var iter = entries.iterator();
while (iter.next()) |kv| {
// Only write non-expired entries
if (kv.value_ptr.isExpired(expire_days)) continue;
try kv.value_ptr.format(&atomic_file.file_writer.interface);
try kv.value_ptr.format(&file_writer.interface);
}
try atomic_file.finish();
try file_writer.flush();
try atomic_file.replace(std.Io.Threaded.global_single_threaded.io());
}
/// List all entries in the cache.
@ -271,14 +282,15 @@ pub fn list(
alloc: Allocator,
) !std.StringHashMap(Entry) {
// Open our file
const file = std.fs.openFileAbsolute(
const file = std.Io.Dir.openFileAbsolute(
std.Io.Threaded.global_single_threaded.io(),
self.path,
.{},
) catch |err| switch (err) {
error.FileNotFound => return .init(alloc),
else => return err,
};
defer file.close();
defer file.close(std.Io.Threaded.global_single_threaded.io());
return readEntries(alloc, file);
}
@ -299,13 +311,13 @@ pub fn deinitEntries(
entries.deinit();
}
pub const ReadEntriesError = std.mem.Allocator.Error || std.io.Reader.LimitedAllocError;
pub const ReadEntriesError = std.mem.Allocator.Error || std.Io.Reader.LimitedAllocError;
fn readEntries(
alloc: Allocator,
file: std.fs.File,
file: std.Io.File,
) ReadEntriesError!std.StringHashMap(Entry) {
var reader = file.reader(&.{});
var reader = file.reader(std.Io.Threaded.global_single_threaded.io(), &.{});
const content = try reader.interface.allocRemaining(
alloc,
.limited(MAX_CACHE_SIZE),
@ -378,7 +390,7 @@ fn isValidHost(host: []const u8) bool {
// We also accept valid IP addresses. In practice, IPv4 addresses are also
// considered valid hostnames due to their overlapping syntax, so we can
// simplify this check to be IPv6-specific.
if (std.net.Address.parseIp6(host, 0)) |_| {
if (std.Io.net.IpAddress.parseIp6(host, 0)) |_| {
return true;
} else |_| {
return false;
@ -414,12 +426,12 @@ test "disk cache clear" {
defer tmp.cleanup();
var buf: [4096]u8 = undefined;
{
var file = try tmp.dir.createFile("cache", .{});
defer file.close();
var file_writer = file.writer(&buf);
var file = try tmp.dir.createFile(testing.io, "cache", .{});
defer file.close(testing.io);
var file_writer = file.writer(testing.io, &buf);
try file_writer.interface.writeAll("HELLO!");
}
const path = try tmp.dir.realpathAlloc(alloc, "cache");
const path = try tmp.dir.realPathFileAlloc(testing.io, "cache", alloc);
defer alloc.free(path);
// Setup our cache
@ -429,7 +441,7 @@ test "disk cache clear" {
// Verify the file is gone
try testing.expectError(
error.FileNotFound,
tmp.dir.openFile("cache", .{}),
tmp.dir.openFile(testing.io, "cache", .{}),
);
}
@ -442,14 +454,14 @@ test "disk cache operations" {
defer tmp.cleanup();
var buf: [4096]u8 = undefined;
{
var file = try tmp.dir.createFile("cache", .{});
defer file.close();
var file_writer = file.writer(&buf);
var file = try tmp.dir.createFile(testing.io, "cache", .{});
defer file.close(testing.io);
var file_writer = file.writer(testing.io, &buf);
const writer = &file_writer.interface;
try writer.writeAll("HELLO!");
try writer.flush();
}
const path = try tmp.dir.realpathAlloc(alloc, "cache");
const path = try tmp.dir.realPathFileAlloc(testing.io, "cache", alloc);
defer alloc.free(path);
// Setup our cache
@ -488,7 +500,7 @@ test "disk cache cleans up temp files" {
var tmp = testing.tmpDir(.{ .iterate = true });
defer tmp.cleanup();
const tmp_path = try tmp.dir.realpathAlloc(alloc, ".");
const tmp_path = try tmp.dir.realPathFileAlloc(testing.io, ".", alloc);
defer alloc.free(tmp_path);
const cache_path = try std.fs.path.join(alloc, &.{ tmp_path, "cache" });
defer alloc.free(cache_path);
@ -500,7 +512,7 @@ test "disk cache cleans up temp files" {
// Verify only the cache file exists and no temp files left behind
var count: usize = 0;
var iter = tmp.dir.iterate();
while (try iter.next()) |entry| {
while (try iter.next(testing.io)) |entry| {
count += 1;
try testing.expectEqualStrings("cache", entry.name);
}

View File

@ -44,14 +44,14 @@ pub fn format(self: Entry, writer: *std.Io.Writer) FormatError!void {
pub fn isExpired(self: Entry, expire_days_: ?u32) bool {
const expire_days = expire_days_ orelse return false;
const now = std.time.timestamp();
const now = std.Io.Timestamp.now(std.Io.Threaded.global_single_threaded.io(), .real).toSeconds();
const age_days = @divTrunc(now -| self.timestamp, std.time.s_per_day);
return age_days > expire_days;
}
test "cache entry expiration" {
const testing = std.testing;
const now = std.time.timestamp();
const now = std.Io.Timestamp.now(testing.io, .real).toSeconds();
const fresh_entry: Entry = .{
.hostname = "test.com",
@ -73,7 +73,7 @@ test "cache entry expiration" {
test "cache entry expiration exact boundary" {
const testing = std.testing;
const now = std.time.timestamp();
const now = std.Io.Timestamp.now(testing.io, .real).toSeconds();
// Exactly at expiration boundary
const boundary_entry: Entry = .{
@ -87,7 +87,7 @@ test "cache entry expiration exact boundary" {
test "cache entry expiration large timestamp" {
const testing = std.testing;
const now = std.time.timestamp();
const now = std.Io.Timestamp.now(testing.io, .real).toSeconds();
const boundary_entry: Entry = .{
.hostname = "example.com",

View File

@ -61,13 +61,13 @@ pub fn run(alloc_gpa: Allocator) !u8 {
}
var stdout_buffer: [1024]u8 = undefined;
var stdout_file: std.fs.File = .stdout();
var stdout_writer = stdout_file.writer(&stdout_buffer);
var stdout_file: std.Io.File = .stdout();
var stdout_writer = stdout_file.writer(std.Io.Threaded.global_single_threaded.io(), &stdout_buffer);
const stdout = &stdout_writer.interface;
var stderr_buffer: [1024]u8 = undefined;
var stderr_file: std.fs.File = .stderr();
var stderr_writer = stderr_file.writer(&stderr_buffer);
var stderr_file: std.Io.File = .stderr();
var stderr_writer = stderr_file.writer(std.Io.Threaded.global_single_threaded.io(), &stderr_buffer);
const stderr = &stderr_writer.interface;
const result = runInner(alloc, opts, stdout, stderr);
@ -208,7 +208,7 @@ fn listEntries(
}.lessThan);
try writer.print("Cached hosts ({d}):\n", .{items.items.len});
const now = std.time.timestamp();
const now = std.Io.Timestamp.now(std.Io.Threaded.global_single_threaded.io(), .real).toSeconds();
for (items.items) |entry| {
const age_days = @divTrunc(now - entry.timestamp, std.time.s_per_day);

View File

@ -40,7 +40,7 @@ pub fn run(alloc: std.mem.Allocator) !u8 {
}
var buffer: [1024]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&buffer);
var stdout_writer = std.Io.File.stdout().writer(std.Io.Threaded.global_single_threaded.io(), &buffer);
const stdout = &stdout_writer.interface;
const result = runInner(alloc, opts, stdout);
try stdout_writer.end();
@ -58,7 +58,11 @@ fn runInner(
// If a config path is passed, validate it, otherwise validate default configs
if (opts.@"config-file") |config_path| {
var buf: [std.fs.max_path_bytes]u8 = undefined;
const abs_path = try std.fs.cwd().realpath(config_path, &buf);
const abs_path = buf[0..try std.Io.Dir.cwd().realPathFile(
std.Io.Threaded.global_single_threaded.io(),
config_path,
&buf,
)];
try cfg.loadFile(alloc, abs_path);
try cfg.loadRecursiveFiles(alloc);
} else {

View File

@ -16,11 +16,11 @@ pub const Options = struct {};
/// either `+version` or `--version`.
pub fn run(alloc: Allocator) !u8 {
var buffer: [1024]u8 = undefined;
const stdout_file: std.fs.File = .stdout();
var stdout_writer = stdout_file.writer(&buffer);
const stdout_file: std.Io.File = .stdout();
var stdout_writer = stdout_file.writer(std.Io.Threaded.global_single_threaded.io(), &buffer);
const stdout = &stdout_writer.interface;
const tty = stdout_file.isTty();
const tty = try stdout_file.isTty(std.Io.Threaded.global_single_threaded.io());
if (tty) if (build_config.version.build) |commit_hash| {
try stdout.print(

View File

@ -20,6 +20,8 @@ const global_state = &@import("../global.zig").state;
const deepEqual = @import("../datastruct/comparison.zig").deepEqual;
const fontpkg = @import("../font/main.zig");
const inputpkg = @import("../input.zig");
const compat_env = @import("../lib/compat/env.zig");
const compat_args = @import("../lib/compat/args.zig");
const internal_os = @import("../os/main.zig");
const cli = @import("../cli.zig");
@ -3814,7 +3816,7 @@ _conditional_set: std.EnumSet(conditional.Key) = .{},
/// The steps we can use to reload the configuration after it has been loaded
/// without reopening the files. This is used in very specific cases such
/// as loadTheme which has more details on why.
_replay_steps: std.ArrayListUnmanaged(Replay.Step) = .{},
_replay_steps: std.ArrayList(Replay.Step) = .empty,
/// Set to true if Ghostty was executed as xdg-terminal-exec on Linux.
@"_xdg-terminal-exec": bool = false,
@ -3888,7 +3890,7 @@ pub fn loadIter(
/// `path` must be resolved and absolute.
pub fn loadFile(self: *Config, alloc: Allocator, path: []const u8) !void {
assert(std.fs.path.isAbsolute(path));
var file = file_load.open(path) catch |err| switch (err) {
var file = file_load.open(std.Io.Threaded.global_single_threaded.io(), path) catch |err| switch (err) {
error.NotAFile => {
log.warn(
"config-file {s}: not reading because it is not a file",
@ -3899,16 +3901,16 @@ pub fn loadFile(self: *Config, alloc: Allocator, path: []const u8) !void {
else => return err,
};
defer file.close();
defer file.close(std.Io.Threaded.global_single_threaded.io());
try self.loadFsFile(alloc, &file, path);
}
/// Load config from the given File.
fn loadFsFile(self: *Config, alloc: Allocator, file: *std.fs.File, path: []const u8) !void {
fn loadFsFile(self: *Config, alloc: Allocator, file: *std.Io.File, path: []const u8) !void {
std.log.info("reading configuration file path={s}", .{path});
var buf: [2048]u8 = undefined;
var file_reader = file.reader(&buf);
var file_reader = file.reader(std.Io.Threaded.global_single_threaded.io(), &buf);
const reader = &file_reader.interface;
try self.loadReader(alloc, reader, path);
}
@ -4001,12 +4003,12 @@ pub fn loadOptionalFile(
fn writeConfigTemplate(path: []const u8) !void {
log.info("creating template config file: path={s}", .{path});
if (std.fs.path.dirname(path)) |dir_path| {
try std.fs.cwd().makePath(dir_path);
try std.Io.Dir.cwd().createDirPath(std.Io.Threaded.global_single_threaded.io(), dir_path);
}
const file = try std.fs.createFileAbsolute(path, .{});
defer file.close();
const file = try std.Io.Dir.createFileAbsolute(std.Io.Threaded.global_single_threaded.io(), path, .{});
defer file.close(std.Io.Threaded.global_single_threaded.io());
var buf: [4096]u8 = undefined;
var file_writer = file.writer(&buf);
var file_writer = file.writer(std.Io.Threaded.global_single_threaded.io(), &buf);
const writer = &file_writer.interface;
try writer.print(
@embedFile("./config-template"),
@ -4100,7 +4102,7 @@ pub fn loadCliArgs(self: *Config, alloc_gpa: Allocator) !void {
.windows => {},
// Fast-path if we are Linux/BSD and have no args.
.linux, .freebsd => if (std.os.argv.len <= 1) return,
.linux, .freebsd => if (compat_args.argsLen() <= 1) return,
// Everything else we have to at least try because it may
// not use std.os.argv.
@ -4119,7 +4121,7 @@ pub fn loadCliArgs(self: *Config, alloc_gpa: Allocator) !void {
//
// See: https://github.com/Vladimir-csp/xdg-terminal-exec
if ((comptime builtin.os.tag == .linux) or (comptime builtin.os.tag == .freebsd)) {
if (internal_os.xdg.parseTerminalExec(std.os.argv)) |args| {
if (internal_os.xdg.parseTerminalExec(compat_args.getArgs().vector)) |args| {
const arena_alloc = self._arena.?.allocator();
// First, we add an artificial "-e" so that if we
@ -4195,7 +4197,7 @@ pub fn loadCliArgs(self: *Config, alloc_gpa: Allocator) !void {
// Any paths referenced from the CLI are relative to the current working
// directory.
var buf: [std.fs.max_path_bytes]u8 = undefined;
try self.expandPaths(try std.fs.cwd().realpath(".", &buf));
try self.expandPaths(buf[0..try std.Io.Dir.cwd().realPath(std.Io.Threaded.global_single_threaded.io(), &buf)]);
}
/// Load and parse the config files that were added in the "config-file" key.
@ -4258,7 +4260,7 @@ pub fn loadRecursiveFiles(self: *Config, alloc_gpa: Allocator) !void {
continue;
}
var file = std.fs.openFileAbsolute(path, .{}) catch |err| {
var file = std.Io.Dir.openFileAbsolute(std.Io.Threaded.global_single_threaded.io(), path, .{}) catch |err| {
if (err != error.FileNotFound or !optional) {
const diag: cli.Diagnostic = .{
.message = try std.fmt.allocPrintSentinel(
@ -4274,9 +4276,9 @@ pub fn loadRecursiveFiles(self: *Config, alloc_gpa: Allocator) !void {
}
continue;
};
defer file.close();
defer file.close(std.Io.Threaded.global_single_threaded.io());
const stat = try file.stat();
const stat = try file.stat(std.Io.Threaded.global_single_threaded.io());
switch (stat.kind) {
.file => {},
else => |kind| {
@ -4421,7 +4423,7 @@ fn loadTheme(self: *Config, theme: Theme) !void {
)) orelse return;
const path = themefile.path;
const file = themefile.file;
defer file.close();
defer file.close(std.Io.Threaded.global_single_threaded.io());
// From this point onwards, we load the theme and do a bit of a dance
// to achieve two separate goals:
@ -4443,7 +4445,7 @@ fn loadTheme(self: *Config, theme: Theme) !void {
// Load our theme
var buf: [2048]u8 = undefined;
var file_reader = file.reader(&buf);
var file_reader = file.reader(std.Io.Threaded.global_single_threaded.io(), &buf);
const reader = &file_reader.interface;
var iter: cli.args.LineIterator = .{ .r = reader, .filepath = path };
try new_config.loadIter(alloc_gpa, &iter);
@ -4580,7 +4582,7 @@ pub fn finalize(self: *Config) !void {
// read from SHELL if we're in a probable CLI environment.
if (!probable_cli) break :shell_env;
if (std.process.getEnvVarOwned(alloc, "SHELL")) |value| {
if (compat_env.getEnvVarOwned(alloc, "SHELL")) |value| {
log.info("default shell source=env value={s}", .{value});
const copy = try alloc.dupeZ(u8, value);
@ -5091,12 +5093,12 @@ fn probableCliEnvironment() bool {
// If we have TERM_PROGRAM set to a non-empty value, we assume
// a graphical terminal environment.
if (std.posix.getenv("TERM_PROGRAM")) |v| {
if (compat_env.getenv("TERM_PROGRAM")) |v| {
if (v.len > 0) return true;
}
// CLI arguments makes things probable
if (std.os.argv.len > 1) return true;
if (compat_args.argsLen() > 1) return true;
// Unlikely CLI environment
return false;
@ -5665,8 +5667,8 @@ pub const BoldColor = union(enum) {
pub const ColorList = struct {
const Self = @This();
colors: std.ArrayListUnmanaged(Color) = .{},
colors_c: std.ArrayListUnmanaged(Color.C) = .{},
colors: std.ArrayList(Color) = .empty,
colors_c: std.ArrayList(Color.C) = .empty,
/// ghostty_config_color_list_s
pub const C = extern struct {
@ -5987,7 +5989,7 @@ pub const RepeatableString = struct {
const Self = @This();
// Allocator for the list is the arena for the parent config.
list: std.ArrayListUnmanaged([:0]const u8) = .{},
list: std.ArrayList([:0]const u8) = .empty,
// If true, then the next value will clear the list and start over
// rather than append. This is a bit of a hack but is here to make
@ -6301,7 +6303,7 @@ pub const RepeatableFontVariation = struct {
const Self = @This();
// Allocator for the list is the arena for the parent config.
list: std.ArrayListUnmanaged(fontpkg.face.Variation) = .{},
list: std.ArrayList(fontpkg.face.Variation) = .empty,
pub fn parseCLI(self: *Self, alloc: Allocator, input_: ?[]const u8) !void {
const input = input_ orelse return error.ValueRequired;
@ -8584,7 +8586,7 @@ pub const FontShapingBreak = packed struct {
pub const RepeatableLink = struct {
const Self = @This();
links: std.ArrayListUnmanaged(inputpkg.Link) = .{},
links: std.ArrayList(inputpkg.Link) = .empty,
pub fn parseCLI(self: *Self, alloc: Allocator, input_: ?[]const u8) !void {
_ = self;
@ -10441,23 +10443,23 @@ test "clone can then change conditional state" {
defer td.deinit();
var buf: [4096]u8 = undefined;
{
var file = try td.dir.createFile("theme_light", .{});
defer file.close();
var writer = file.writer(&buf);
var file = try td.dir.createFile(testing.io, "theme_light", .{});
defer file.close(testing.io);
var writer = file.writer(testing.io, &buf);
try writer.interface.writeAll(@embedFile("testdata/theme_light"));
try writer.end();
}
{
var file = try td.dir.createFile("theme_dark", .{});
defer file.close();
var writer = file.writer(&buf);
var file = try td.dir.createFile(testing.io, "theme_dark", .{});
defer file.close(testing.io);
var writer = file.writer(testing.io, &buf);
try writer.interface.writeAll(@embedFile("testdata/theme_dark"));
try writer.end();
}
var light_buf: [std.fs.max_path_bytes]u8 = undefined;
const light = try td.dir.realpath("theme_light", &light_buf);
const light = light_buf[0..try td.dir.realPathFile(testing.io, "theme_light", &light_buf)];
var dark_buf: [std.fs.max_path_bytes]u8 = undefined;
const dark = try td.dir.realpath("theme_dark", &dark_buf);
const dark = dark_buf[0..try td.dir.realPathFile(testing.io, "theme_dark", &dark_buf)];
var cfg_light = try Config.default(alloc);
defer cfg_light.deinit();
@ -10600,14 +10602,14 @@ test "theme loading" {
defer td.deinit();
var buf: [4096]u8 = undefined;
{
var file = try td.dir.createFile("theme", .{});
defer file.close();
var writer = file.writer(&buf);
var file = try td.dir.createFile(testing.io, "theme", .{});
defer file.close(testing.io);
var writer = file.writer(testing.io, &buf);
try writer.interface.writeAll(@embedFile("testdata/theme_simple"));
try writer.end();
}
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
const path = try td.dir.realpath("theme", &path_buf);
const path = path_buf[0..try td.dir.realPathFile(testing.io, "theme", &path_buf)];
var cfg = try Config.default(alloc);
defer cfg.deinit();
@ -10639,14 +10641,14 @@ test "theme loading preserves conditional state" {
defer td.deinit();
var buf: [4096]u8 = undefined;
{
var file = try td.dir.createFile("theme", .{});
defer file.close();
var writer = file.writer(&buf);
var file = try td.dir.createFile(testing.io, "theme", .{});
defer file.close(testing.io);
var writer = file.writer(testing.io, &buf);
try writer.interface.writeAll(@embedFile("testdata/theme_simple"));
try writer.end();
}
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
const path = try td.dir.realpath("theme", &path_buf);
const path = path_buf[0..try td.dir.realPathFile(testing.io, "theme", &path_buf)];
var cfg = try Config.default(alloc);
defer cfg.deinit();
@ -10672,14 +10674,14 @@ test "theme priority is lower than config" {
defer td.deinit();
var buf: [4096]u8 = undefined;
{
var file = try td.dir.createFile("theme", .{});
defer file.close();
var writer = file.writer(&buf);
var file = try td.dir.createFile(testing.io, "theme", .{});
defer file.close(testing.io);
var writer = file.writer(testing.io, &buf);
try writer.interface.writeAll(@embedFile("testdata/theme_simple"));
try writer.end();
}
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
const path = try td.dir.realpath("theme", &path_buf);
const path = path_buf[0..try td.dir.realPathFile(testing.io, "theme", &path_buf)];
var cfg = try Config.default(alloc);
defer cfg.deinit();
@ -10709,23 +10711,23 @@ test "theme loading correct light/dark" {
defer td.deinit();
var buf: [4096]u8 = undefined;
{
var file = try td.dir.createFile("theme_light", .{});
defer file.close();
var writer = file.writer(&buf);
var file = try td.dir.createFile(testing.io, "theme_light", .{});
defer file.close(testing.io);
var writer = file.writer(testing.io, &buf);
try writer.interface.writeAll(@embedFile("testdata/theme_light"));
try writer.end();
}
{
var file = try td.dir.createFile("theme_dark", .{});
defer file.close();
var writer = file.writer(&buf);
var file = try td.dir.createFile(testing.io, "theme_dark", .{});
defer file.close(testing.io);
var writer = file.writer(testing.io, &buf);
try writer.interface.writeAll(@embedFile("testdata/theme_dark"));
try writer.end();
}
var light_buf: [std.fs.max_path_bytes]u8 = undefined;
const light = try td.dir.realpath("theme_light", &light_buf);
const light = light_buf[0..try td.dir.realPathFile(testing.io, "theme_light", &light_buf)];
var dark_buf: [std.fs.max_path_bytes]u8 = undefined;
const dark = try td.dir.realpath("theme_dark", &dark_buf);
const dark = dark_buf[0..try td.dir.realPathFile(testing.io, "theme_dark", &dark_buf)];
// Light
{

View File

@ -84,7 +84,7 @@ fn getValue(ptr_raw: *anyopaque, value: anytype) bool {
ptr.* = @intCast(@as(Backing, @bitCast(value)));
},
.@"union" => |_| {
.@"union" => {
if (@hasDecl(T, "cval")) {
const PtrT = @typeInfo(@TypeOf(T.cval)).@"fn".return_type.?;
const ptr: *PtrT = @ptrCast(@alignCast(ptr_raw));

View File

@ -2,6 +2,7 @@ const std = @import("std");
const Allocator = std.mem.Allocator;
const ArenaAllocator = std.heap.ArenaAllocator;
const formatterpkg = @import("formatter.zig");
const compat_args = @import("../lib/compat/args.zig");
/// A command to execute (argv0 and args).
///
@ -123,7 +124,7 @@ pub const Command = union(enum) {
/// Iterates over each argument in the command.
pub const ArgIterator = union(enum) {
shell: std.process.ArgIteratorGeneral(.{}),
shell: compat_args.ArgIteratorGeneral(.{}),
direct: struct {
i: usize = 0,
args: []const [:0]const u8,

View File

@ -39,18 +39,16 @@ pub const State = struct {
/// An enum of the available conditional configuration keys.
pub const Key = key: {
const stateInfo = @typeInfo(State).@"struct";
var fields: [stateInfo.fields.len]std.builtin.Type.EnumField = undefined;
for (stateInfo.fields, 0..) |field, i| fields[i] = .{
.name = field.name,
.value = i,
};
const TagInt = std.math.IntFittingRange(0, stateInfo.fields.len - 1);
var names: [stateInfo.fields.len][]const u8 = undefined;
var values: [stateInfo.fields.len]TagInt = undefined;
break :key @Type(.{ .@"enum" = .{
.tag_type = std.math.IntFittingRange(0, fields.len - 1),
.fields = &fields,
.decls = &.{},
.is_exhaustive = true,
} });
for (stateInfo.fields, &names, &values, 0..) |field, *name, *v, i| {
name.* = field.name;
v.* = @intCast(i);
}
break :key @Enum(TagInt, .exhaustive, &names, &values);
};
/// A single conditional that can be true or false.

View File

@ -28,11 +28,12 @@ pub fn openPath(alloc_gpa: Allocator) ![:0]const u8 {
// Create config directory recursively.
if (std.fs.path.dirname(config_path)) |config_dir| {
try std.fs.cwd().makePath(config_dir);
try std.Io.Dir.cwd().createDirPath(std.Io.Threaded.global_single_threaded.io(), config_dir);
}
// Try to create file and go on if it already exists
_ = std.fs.createFileAbsolute(
_ = std.Io.Dir.createFileAbsolute(
std.Io.Threaded.global_single_threaded.io(),
config_path,
.{ .exclusive = true },
) catch |err| {
@ -58,7 +59,7 @@ fn configPath(alloc_arena: Allocator) ![]const u8 {
// exists.
var exists: ?[]const u8 = null;
for (paths) |path| {
const f = std.fs.openFileAbsolute(path, .{}) catch |err| {
const f = std.Io.Dir.openFileAbsolute(std.Io.Threaded.global_single_threaded.io(), path, .{}) catch |err| {
switch (err) {
// File doesn't exist, continue.
error.BadPathName, error.FileNotFound => continue,
@ -67,10 +68,10 @@ fn configPath(alloc_arena: Allocator) ![]const u8 {
else => return err,
}
};
defer f.close();
defer f.close(std.Io.Threaded.global_single_threaded.io());
// We expect stat to succeed because we just opened the file.
const stat = try f.stat();
const stat = try f.stat(std.Io.Threaded.global_single_threaded.io());
// If the file is non-empty, return it.
if (stat.size > 0) return path;

Some files were not shown because too many files have changed in this diff Show More