diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4961ad0..4340ce9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,8 +73,13 @@ jobs: - name: Check pkg-config path if: runner.os == 'Linux' run: | - nix develop --command cargo build -p libghostty-vt-sys - PKG_CONFIG_PATH=$(find target/debug/build -path '*/out/ghostty-install/share/pkgconfig' | head -1) - test -n "$PKG_CONFIG_PATH" - export PKG_CONFIG_PATH - nix develop --command cargo check -p libghostty-vt-sys --features pkg-config + test -f result/share/pkgconfig/libghostty-vt-static.pc + PKG_CONFIG_PATH="$PWD/result/share/pkgconfig" \ + nix develop --command env \ + -u GHOSTTY_SOURCE_DIR \ + -u GHOSTTY_ZIG_SYSTEM_DIR \ + cargo check -p libghostty-vt-sys --no-default-features --features pkg-config + + - name: Run libghostty-vt tests under Valgrind + if: matrix.name == 'Linux x86_64' + run: nix build .#checks.x86_64-linux.ci-valgrind --print-build-logs diff --git a/README.md b/README.md index 0b4771f..ef10716 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,9 @@ Vendored builds derive Zig's optimize mode from Cargo's profile: dev builds use `Debug`, size-optimized builds use `ReleaseSmall`, and other release builds use `ReleaseFast`. Set `LIBGHOSTTY_VT_SYS_OPTIMIZE` to `Debug`, `ReleaseSafe`, `ReleaseFast`, or `ReleaseSmall` to override that choice explicitly. +Set `LIBGHOSTTY_VT_SYS_SIMD=false` to pass Ghostty's `-Dsimd=false` option and +build scalar code paths only. The Nix output `.#libghostty-vt-non-simd` uses +that mode, and the Valgrind CI check combines it with Zig `Debug` builds. The `pkg-config` path is opt-in. If you enable `libghostty-vt-sys/pkg-config`, the build will prefer an installed `libghostty-vt` discovered through diff --git a/crates/libghostty-vt-sys/build.rs b/crates/libghostty-vt-sys/build.rs index 8600438..8fca46d 100644 --- a/crates/libghostty-vt-sys/build.rs +++ b/crates/libghostty-vt-sys/build.rs @@ -72,6 +72,7 @@ fn main() { let link_mode = LinkMode::current(); println!("cargo:rerun-if-env-changed=LIBGHOSTTY_VT_SYS_OPTIMIZE"); + println!("cargo:rerun-if-env-changed=LIBGHOSTTY_VT_SYS_SIMD"); println!("cargo:rerun-if-env-changed=GHOSTTY_SOURCE_DIR"); println!("cargo:rerun-if-env-changed=GHOSTTY_ZIG_SYSTEM_DIR"); println!("cargo:rerun-if-env-changed=TARGET"); @@ -126,12 +127,14 @@ fn build_vendored(link_mode: LinkMode) { let zig_global_cache_dir = out_dir.join("zig-global-cache"); let optimize = zig_optimize_mode(); + let simd = zig_simd_enabled(); let mut build = Command::new("zig"); build .arg("build") .arg("-Demit-lib-vt") .arg(format!("-Doptimize={optimize}")) + .arg(format!("-Dsimd={simd}")) .arg("-Demit-xcframework=false") .arg("-Dapp-runtime=none") .arg("--prefix") @@ -316,6 +319,24 @@ fn zig_optimize_mode() -> &'static str { } } +/// Decide whether to build Ghostty's SIMD-accelerated code paths. +/// +/// Ghostty exposes this as `zig build -Dsimd=...`. Valgrind builds should turn +/// it off so the generated library uses scalar fallbacks instead of pulling in +/// the C++ highway/simdutf implementation block. +fn zig_simd_enabled() -> bool { + match env::var("LIBGHOSTTY_VT_SYS_SIMD") { + Ok(value) => match value.as_str() { + "1" | "true" | "True" | "TRUE" => true, + "0" | "false" | "False" | "FALSE" => false, + other => { + panic!("LIBGHOSTTY_VT_SYS_SIMD must be one of true, false, 1, 0 (got '{other}')") + } + }, + Err(_) => true, + } +} + /// Clone ghostty at the pinned commit into OUT_DIR/ghostty-src. /// Reuses an existing clone if the commit matches. fn fetch_ghostty(out_dir: &Path) -> PathBuf { diff --git a/flake.nix b/flake.nix index 6dfd9a7..fc6fec4 100644 --- a/flake.nix +++ b/flake.nix @@ -114,17 +114,90 @@ }; cargoArtifacts = craneLib.buildDepsOnly commonArgs; + nonSimdCargoArtifacts = craneLib.buildDepsOnly ( + commonArgs + // { + pname = "libghostty-rs-non-simd-deps"; + LIBGHOSTTY_VT_SYS_SIMD = "false"; + } + ); + ciValgrindCargoArtifacts = craneLib.buildDepsOnly ( + commonArgs + // { + pname = "libghostty-rs-ci-valgrind-deps"; + cargoExtraArgs = "--locked -p libghostty-vt"; + cargoTestExtraArgs = "--no-run"; + CARGO_PROFILE = ""; + + # The Valgrind check must not inherit ReleaseFast Zig artifacts from + # the normal package build. Debug keeps both Rust and Zig codegen in + # Valgrind's supported instruction set and gives better reports. + LIBGHOSTTY_VT_SYS_OPTIMIZE = "Debug"; + LIBGHOSTTY_VT_SYS_SIMD = "false"; + } + ); application = craneLib.buildPackage ( commonArgs // { inherit cargoArtifacts; + postInstall = '' + ghostty_install=$(find target -path '*/out/ghostty-install' -type d | head -1) + if [ -z "$ghostty_install" ]; then + echo "expected Cargo build script to install libghostty-vt into OUT_DIR" >&2 + exit 1 + fi + ghostty_install=$(realpath "$ghostty_install") + + cp -R "$ghostty_install"/. "$out"/ + substituteInPlace "$out"/share/pkgconfig/*.pc \ + --replace-fail "prefix=$ghostty_install" "prefix=$out" + ''; + } + ); + + libghosttyVtNonSimd = craneLib.buildPackage ( + commonArgs + // { + cargoArtifacts = nonSimdCargoArtifacts; + pname = "libghostty-vt-non-simd"; + LIBGHOSTTY_VT_SYS_SIMD = "false"; + } + ); + + ciValgrind = craneLib.buildPackage ( + commonArgs + // { + cargoArtifacts = ciValgrindCargoArtifacts; + pname = "libghostty-rs-ci-valgrind"; + cargoBuildCommand = ""; + cargoTestCommand = "cargo valgrind test"; + cargoExtraArgs = "--locked -p libghostty-vt"; + cargoTestExtraArgs = "-- --test-threads=1"; + CARGO_PROFILE = ""; + LIBGHOSTTY_VT_SYS_OPTIMIZE = "Debug"; + LIBGHOSTTY_VT_SYS_SIMD = "false"; + nativeBuildInputs = commonArgs.nativeBuildInputs ++ [ + pkgs.cargo-valgrind + pkgs.valgrind + ]; + + installPhaseCommand = "mkdir -p $out"; } ); in { - packages.default = application; + packages = { + default = application; + libghostty-vt-non-simd = libghosttyVtNonSimd; + }; - checks.default = application; + checks = + { + default = application; + } + // pkgs.lib.optionalAttrs (system == "x86_64-linux") { + ci-valgrind = ciValgrind; + }; devShells.default = craneLib.devShell { packages = [ @@ -137,6 +210,9 @@ pkgs.cmake pkgs.ninja ] ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux [ + # Valgrind is Linux-only here. Keep it in the development shell so + # CI and local Linux users exercise the same memory-checking path. + pkgs.valgrind pkgs.libx11 pkgs.libxcursor pkgs.libxrandr @@ -145,6 +221,8 @@ pkgs.libGL pkgs.libxkbcommon pkgs.wayland + ] ++ pkgs.lib.optionals (system == "x86_64-linux") [ + pkgs.cargo-valgrind ]; shellHook = ''