mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
cmake: fix Windows libghostty build support (#11756)
On Windows, shared libraries (DLLs) require an import library (.lib) for linking, and the DLL itself is placed in bin/ rather than lib/ by the Zig build. The CMake wrapper was missing IMPORTED_IMPLIB on the shared imported target, causing link failures, and assumed the shared library was always in lib/. Add GHOSTTY_VT_IMPLIB for the import library name, set IMPORTED_IMPLIB on the ghostty-vt target, and fix the shared library path to use bin/ on Windows. Install the DLL and PDB to bin/ and the import library to lib/ following standard Windows conventions. Apply the same fixes to ghostty-vt-config.cmake.in for the find_package path.
This commit is contained in:
25
.github/workflows/test.yml
vendored
25
.github/workflows/test.yml
vendored
@@ -87,6 +87,7 @@ jobs:
|
||||
- build-dist
|
||||
- build-examples-zig
|
||||
- build-examples-cmake
|
||||
- build-examples-cmake-windows
|
||||
- build-cmake
|
||||
- build-flatpak
|
||||
- build-libghostty-vt
|
||||
@@ -263,6 +264,30 @@ jobs:
|
||||
nix develop -c cmake -B build -DFETCHCONTENT_SOURCE_DIR_GHOSTTY=${{ github.workspace }}
|
||||
nix develop -c cmake --build build
|
||||
|
||||
build-examples-cmake-windows:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
dir: ${{ fromJSON(needs.list-examples.outputs.cmake) }}
|
||||
name: Example ${{ matrix.dir }} (Windows)
|
||||
runs-on: windows-2025
|
||||
continue-on-error: true
|
||||
timeout-minutes: 45
|
||||
needs: [test, list-examples]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Install zig
|
||||
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
|
||||
|
||||
- name: Build Example
|
||||
shell: pwsh
|
||||
run: |
|
||||
cd example/${{ matrix.dir }}
|
||||
cmake -B build -DFETCHCONTENT_SOURCE_DIR_GHOSTTY=${{ github.workspace }}
|
||||
cmake --build build
|
||||
|
||||
build-cmake:
|
||||
runs-on: namespace-profile-ghostty-sm
|
||||
needs: test
|
||||
|
||||
@@ -81,16 +81,27 @@ if(APPLE)
|
||||
elseif(WIN32)
|
||||
set(GHOSTTY_VT_LIBNAME "ghostty-vt.dll")
|
||||
set(GHOSTTY_VT_REALNAME "ghostty-vt.dll")
|
||||
set(GHOSTTY_VT_IMPLIB "ghostty-vt.lib")
|
||||
else()
|
||||
set(GHOSTTY_VT_LIBNAME "${CMAKE_SHARED_LIBRARY_PREFIX}ghostty-vt${CMAKE_SHARED_LIBRARY_SUFFIX}")
|
||||
set(GHOSTTY_VT_SONAME "${CMAKE_SHARED_LIBRARY_PREFIX}ghostty-vt${CMAKE_SHARED_LIBRARY_SUFFIX}.0")
|
||||
set(GHOSTTY_VT_REALNAME "${CMAKE_SHARED_LIBRARY_PREFIX}ghostty-vt${CMAKE_SHARED_LIBRARY_SUFFIX}.0.1.0")
|
||||
endif()
|
||||
|
||||
set(GHOSTTY_VT_SHARED_LIBRARY "${ZIG_OUT_DIR}/lib/${GHOSTTY_VT_REALNAME}")
|
||||
if(WIN32)
|
||||
set(GHOSTTY_VT_SHARED_LIBRARY "${ZIG_OUT_DIR}/bin/${GHOSTTY_VT_REALNAME}")
|
||||
else()
|
||||
set(GHOSTTY_VT_SHARED_LIBRARY "${ZIG_OUT_DIR}/lib/${GHOSTTY_VT_REALNAME}")
|
||||
endif()
|
||||
|
||||
# Static library name.
|
||||
set(GHOSTTY_VT_STATIC_REALNAME "${CMAKE_STATIC_LIBRARY_PREFIX}ghostty-vt${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||
# On Windows, the static lib is named "ghostty-vt-static.lib" to avoid
|
||||
# colliding with the DLL import library "ghostty-vt.lib".
|
||||
if(WIN32)
|
||||
set(GHOSTTY_VT_STATIC_REALNAME "ghostty-vt-static.lib")
|
||||
else()
|
||||
set(GHOSTTY_VT_STATIC_REALNAME "libghostty-vt.a")
|
||||
endif()
|
||||
set(GHOSTTY_VT_STATIC_LIBRARY "${ZIG_OUT_DIR}/lib/${GHOSTTY_VT_STATIC_REALNAME}")
|
||||
|
||||
# Ensure the output directories exist so CMake doesn't reject the
|
||||
@@ -122,7 +133,11 @@ if(APPLE)
|
||||
set_target_properties(ghostty-vt PROPERTIES
|
||||
IMPORTED_SONAME "@rpath/${GHOSTTY_VT_SONAME}"
|
||||
)
|
||||
elseif(NOT WIN32)
|
||||
elseif(WIN32)
|
||||
set_target_properties(ghostty-vt PROPERTIES
|
||||
IMPORTED_IMPLIB "${ZIG_OUT_DIR}/lib/${GHOSTTY_VT_IMPLIB}"
|
||||
)
|
||||
else()
|
||||
set_target_properties(ghostty-vt PROPERTIES
|
||||
IMPORTED_SONAME "${GHOSTTY_VT_SONAME}"
|
||||
)
|
||||
@@ -145,6 +160,14 @@ set_target_properties(ghostty-vt-static PROPERTIES
|
||||
IMPORTED_LOCATION "${GHOSTTY_VT_STATIC_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${ZIG_OUT_DIR}/include"
|
||||
)
|
||||
if(WIN32)
|
||||
# On Windows, the Zig standard library uses NT API functions
|
||||
# (NtClose, NtCreateSection, etc.) and kernel32 functions that
|
||||
# consumers must link when using the static library.
|
||||
set_target_properties(ghostty-vt-static PROPERTIES
|
||||
INTERFACE_LINK_LIBRARIES "ntdll;kernel32"
|
||||
)
|
||||
endif()
|
||||
add_dependencies(ghostty-vt-static zig_build_lib_vt)
|
||||
|
||||
# --- Install ------------------------------------------------------------------
|
||||
@@ -152,8 +175,12 @@ add_dependencies(ghostty-vt-static zig_build_lib_vt)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Install shared library
|
||||
install(FILES "${GHOSTTY_VT_SHARED_LIBRARY}" TYPE LIB)
|
||||
if(NOT WIN32)
|
||||
if(WIN32)
|
||||
# On Windows, install the DLL and PDB to bin/ and the import library to lib/
|
||||
install(FILES "${GHOSTTY_VT_SHARED_LIBRARY}" "${ZIG_OUT_DIR}/bin/ghostty-vt.pdb" TYPE BIN)
|
||||
install(FILES "${ZIG_OUT_DIR}/lib/${GHOSTTY_VT_IMPLIB}" TYPE LIB)
|
||||
else()
|
||||
install(FILES "${GHOSTTY_VT_SHARED_LIBRARY}" TYPE LIB)
|
||||
# Install symlinks
|
||||
install(CODE "
|
||||
execute_process(COMMAND \${CMAKE_COMMAND} -E create_symlink
|
||||
|
||||
10
build.zig
10
build.zig
@@ -111,17 +111,21 @@ pub fn build(b: *std.Build) !void {
|
||||
b,
|
||||
&mod,
|
||||
);
|
||||
|
||||
if (config.is_dep) {
|
||||
// If we're a dependency, we need to install everything as-is
|
||||
// so that dep.artifact("ghostty-vt-static") works.
|
||||
libghostty_vt_static.install(b.getInstallStep());
|
||||
} else {
|
||||
// If we're not a dependency, we rename the static lib to
|
||||
// be idiomatic.
|
||||
// be idiomatic. On Windows, we use a distinct name to avoid
|
||||
// colliding with the DLL import library (ghostty-vt.lib).
|
||||
const static_lib_name = if (config.target.result.os.tag == .windows)
|
||||
"ghostty-vt-static.lib"
|
||||
else
|
||||
"libghostty-vt.a";
|
||||
b.getInstallStep().dependOn(&b.addInstallLibFile(
|
||||
libghostty_vt_static.output,
|
||||
"libghostty-vt.a",
|
||||
static_lib_name,
|
||||
).step);
|
||||
}
|
||||
|
||||
|
||||
20
dist/cmake/ghostty-vt-config.cmake.in
vendored
20
dist/cmake/ghostty-vt-config.cmake.in
vendored
@@ -8,10 +8,17 @@ set(_ghostty_vt_libdir "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_LIBDIR@")
|
||||
if(NOT TARGET ghostty-vt::ghostty-vt)
|
||||
add_library(ghostty-vt::ghostty-vt SHARED IMPORTED)
|
||||
|
||||
if(WIN32)
|
||||
set(_ghostty_vt_shared_location "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_BINDIR@/@GHOSTTY_VT_REALNAME@")
|
||||
else()
|
||||
set(_ghostty_vt_shared_location "${_ghostty_vt_libdir}/@GHOSTTY_VT_REALNAME@")
|
||||
endif()
|
||||
|
||||
set_target_properties(ghostty-vt::ghostty-vt PROPERTIES
|
||||
IMPORTED_LOCATION "${_ghostty_vt_libdir}/@GHOSTTY_VT_REALNAME@"
|
||||
IMPORTED_LOCATION "${_ghostty_vt_shared_location}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_INCLUDEDIR@"
|
||||
)
|
||||
unset(_ghostty_vt_shared_location)
|
||||
|
||||
if(APPLE)
|
||||
set_target_properties(ghostty-vt::ghostty-vt PROPERTIES
|
||||
@@ -22,7 +29,11 @@ if(NOT TARGET ghostty-vt::ghostty-vt)
|
||||
set_property(TARGET ghostty-vt::ghostty-vt APPEND PROPERTY
|
||||
INTERFACE_LINK_OPTIONS "LINKER:-rpath,${_ghostty_vt_libdir}"
|
||||
)
|
||||
elseif(NOT WIN32)
|
||||
elseif(WIN32)
|
||||
set_target_properties(ghostty-vt::ghostty-vt PROPERTIES
|
||||
IMPORTED_IMPLIB "${_ghostty_vt_libdir}/@GHOSTTY_VT_IMPLIB@"
|
||||
)
|
||||
else()
|
||||
set_target_properties(ghostty-vt::ghostty-vt PROPERTIES
|
||||
IMPORTED_SONAME "@GHOSTTY_VT_SONAME@"
|
||||
)
|
||||
@@ -42,6 +53,11 @@ if(NOT TARGET ghostty-vt::ghostty-vt-static)
|
||||
IMPORTED_LOCATION "${_ghostty_vt_libdir}/@GHOSTTY_VT_STATIC_REALNAME@"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_INCLUDEDIR@"
|
||||
)
|
||||
if(WIN32)
|
||||
set_target_properties(ghostty-vt::ghostty-vt-static PROPERTIES
|
||||
INTERFACE_LINK_LIBRARIES "ntdll;kernel32"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
unset(_ghostty_vt_libdir)
|
||||
|
||||
@@ -20,7 +20,15 @@ pub fn build(b: *std.Build) !void {
|
||||
}),
|
||||
.linkage = .static,
|
||||
});
|
||||
lib.linkLibCpp();
|
||||
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(""));
|
||||
module.addIncludePath(upstream.path(""));
|
||||
|
||||
@@ -12,7 +12,15 @@ pub fn build(b: *std.Build) !void {
|
||||
}),
|
||||
.linkage = .static,
|
||||
});
|
||||
lib.linkLibCpp();
|
||||
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();
|
||||
}
|
||||
lib.addIncludePath(b.path("vendor"));
|
||||
|
||||
if (target.result.os.tag.isDarwin()) {
|
||||
|
||||
@@ -12,7 +12,15 @@ pub fn build(b: *std.Build) !void {
|
||||
}),
|
||||
.linkage = .static,
|
||||
});
|
||||
lib.linkLibCpp();
|
||||
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");
|
||||
|
||||
@@ -83,6 +83,19 @@ pub fn init(b: *std.Build, appVersion: []const u8) !Config {
|
||||
result = genericMacOSTarget(b, result.query.cpu_arch);
|
||||
}
|
||||
|
||||
// On Windows, default to the MSVC ABI so that produced COFF
|
||||
// objects (including compiler_rt) are compatible with the MSVC
|
||||
// linker. Zig defaults to the GNU ABI which produces objects
|
||||
// with invalid COMDAT sections that MSVC rejects (LNK1143).
|
||||
// Only override when no explicit ABI was requested.
|
||||
if (result.result.os.tag == .windows and
|
||||
result.query.abi == null)
|
||||
{
|
||||
var query = result.query;
|
||||
query.abi = .msvc;
|
||||
result = b.resolveTargetQuery(query);
|
||||
}
|
||||
|
||||
// If we have no minimum OS version, we set the default based on
|
||||
// our tag. Not all tags have a minimum so this may be null.
|
||||
if (result.query.os_version_min == null) {
|
||||
|
||||
@@ -106,6 +106,12 @@ fn initLib(
|
||||
lib.root_module.pic = true;
|
||||
}
|
||||
|
||||
if (target.result.os.tag == .windows) {
|
||||
// Zig's ubsan emits /exclude-symbols linker directives that
|
||||
// are incompatible with the MSVC linker (LNK4229).
|
||||
lib.bundle_ubsan_rt = false;
|
||||
}
|
||||
|
||||
if (lib.rootModuleTarget().abi.isAndroid()) {
|
||||
// Support 16kb page sizes, required for Android 15+.
|
||||
lib.link_z_max_page_size = 16384; // 16kb
|
||||
|
||||
@@ -64,8 +64,12 @@ fn initVt(
|
||||
.optimize = cfg.optimize,
|
||||
|
||||
// SIMD require libc/libcpp (both) but otherwise we don't care.
|
||||
// On MSVC, we must not use linkLibCpp because Zig passes
|
||||
// -nostdinc++ and adds its bundled libc++/libc++abi headers
|
||||
// which conflict with MSVC's C++ runtime. The MSVC SDK dirs
|
||||
// added via link_libc contain both C and C++ headers.
|
||||
.link_libc = if (cfg.simd) true else null,
|
||||
.link_libcpp = if (cfg.simd) true else null,
|
||||
.link_libcpp = if (cfg.simd and cfg.target.result.abi != .msvc) true else null,
|
||||
});
|
||||
vt.addOptions("build_options", general_options);
|
||||
vt_options.add(b, vt);
|
||||
|
||||
@@ -399,8 +399,15 @@ pub fn add(
|
||||
step.addIncludePath(b.path("src/apprt/gtk"));
|
||||
}
|
||||
|
||||
// libcpp is required for various dependencies
|
||||
step.linkLibCpp();
|
||||
// libcpp is required for various dependencies. 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 (already added via linkLibC above) contain
|
||||
// both C and C++ headers, so linkLibCpp is not needed.
|
||||
if (step.rootModuleTarget().abi != .msvc) {
|
||||
step.linkLibCpp();
|
||||
}
|
||||
|
||||
// We always require the system SDK so that our system headers are available.
|
||||
// This makes things like `os/log.h` available for cross-compiling.
|
||||
@@ -783,12 +790,35 @@ pub fn addSimd(
|
||||
const HWY_AVX3_DL: c_int = 1 << 7;
|
||||
const HWY_AVX3: c_int = 1 << 8;
|
||||
|
||||
var flags: std.ArrayListUnmanaged([]const u8) = .empty;
|
||||
|
||||
// Zig 0.13 bug: https://github.com/ziglang/zig/issues/20414
|
||||
// To workaround this we just disable AVX512 support completely.
|
||||
// The performance difference between AVX2 and AVX512 is not
|
||||
// significant for our use case and AVX512 is very rare on consumer
|
||||
// hardware anyways.
|
||||
const HWY_DISABLED_TARGETS: c_int = HWY_AVX10_2 | HWY_AVX3_SPR | HWY_AVX3_ZEN4 | HWY_AVX3_DL | HWY_AVX3;
|
||||
if (target.result.cpu.arch == .x86_64) try flags.append(
|
||||
b.allocator,
|
||||
b.fmt("-DHWY_DISABLED_TARGETS={}", .{HWY_DISABLED_TARGETS}),
|
||||
);
|
||||
|
||||
// MSVC requires explicit std specification otherwise these
|
||||
// are guarded, at least on Windows 2025. Doing it unconditionally
|
||||
// doesn't cause any issues on other platforms and ensures we get
|
||||
// C++17 support on MSVC.
|
||||
try flags.append(
|
||||
b.allocator,
|
||||
"-std=c++17",
|
||||
);
|
||||
|
||||
// Disable ubsan for MSVC to avoid undefined references to
|
||||
// __ubsan_handle_* symbols that require a runtime we don't link
|
||||
// and bundle. Hopefully we can fix this one day since ubsan is nice!
|
||||
if (target.result.abi == .msvc) try flags.appendSlice(b.allocator, &.{
|
||||
"-fno-sanitize=undefined",
|
||||
"-fno-sanitize-trap=undefined",
|
||||
});
|
||||
|
||||
m.addCSourceFiles(.{
|
||||
.files = &.{
|
||||
@@ -797,9 +827,7 @@ pub fn addSimd(
|
||||
"src/simd/index_of.cpp",
|
||||
"src/simd/vt.cpp",
|
||||
},
|
||||
.flags = if (target.result.cpu.arch == .x86_64) &.{
|
||||
b.fmt("-DHWY_DISABLED_TARGETS={}", .{HWY_DISABLED_TARGETS}),
|
||||
} else &.{},
|
||||
.flags = flags.items,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <hwy/print-inl.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
|
||||
HWY_BEFORE_NAMESPACE();
|
||||
namespace ghostty {
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#endif
|
||||
|
||||
#include <hwy/highway.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
|
||||
HWY_BEFORE_NAMESPACE();
|
||||
|
||||
Reference in New Issue
Block a user