cmake: fix Windows libghostty build support#11756
Conversation
89c1cb5 to
6895393
Compare
|
Sorry, added some fat here and I'm fixing our windows build at the same time... |
There's too much to do, so not doing that. libghostty is fine. I'll keep it tightly scoped to that. |
|
Is there a list of tasks somewhere to help divide the work? |
Not for Windows. I'd suggest diving into it but keep the scope of PRs small to make review easier. |
|
Hm, besides the build issues there MAY be more going here to look into. Step 1 might be getting a passing |
|
Hi there! I was searching for lib-vt and I bumped onto this. May I interest you with some comedy and with:
? It's not free, there's a gate you have to open and there's no turning back. Your choice: #11742 🙈 |
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.
Use writerStreaming() instead of writer() for stdout in helpgen and main_build_data. The positional writer calls setEndPos/ftruncate in end(), which fails on Windows when stdout is redirected via captureStdOut() because ftruncate maps INVALID_PARAMETER to FileTooBig. Streaming mode skips truncation entirely since stdout is inherently a sequential stream. Replace scandir with opendir/readdir plus qsort in framegen since scandir is a POSIX extension not available on Windows.
Add a "Run Example" step to the build-examples-cmake-windows job so that each CMake example is executed after it is built, verifying the resulting binaries actually work. The executable name is derived from the matrix directory name by replacing hyphens with underscores, matching the project convention.
This reverts commit 7045114.
The cmake examples were failing at runtime on Windows CI for two reasons. The static library was installed as "libghostty-vt.a" on all platforms, but on Windows the DLL import library is also placed in zig-out/lib/ as "ghostty-vt.lib". The CMakeLists.txt expected the platform-native name "ghostty-vt.lib" for the static lib, so it picked up the tiny DLL import lib instead, silently producing a dynamically-linked executable. That executable then failed at runtime because the DLL was not on PATH. Fix this by installing the static library as "ghostty-vt-static.lib" on Windows to avoid the name collision, and updating CMakeLists.txt to match. For the shared (DLL) example, add zig-out/bin to PATH in the CI run step so the DLL can be found at runtime.
7b71826 to
2afadfc
Compare
|
Rebased onto the various windows fixes on main, hoping that makes this a lot more tractable PR. 😄 |
Zig's bundled compiler_rt and ubsan_rt produce object files with ELF-style linker directives (/exclude-symbols) and COMDAT sections that are incompatible with the MSVC linker, causing LNK1143 and LNK4229 errors when linking the static library. MSVC provides its own compiler runtime so bundling Zig's versions is unnecessary. Skip bundling both runtimes when the target ABI is MSVC.
Zig's ubsan instrumentation emits ELF-style /exclude-symbols linker directives into the compiled object files, causing LNK4229 warnings with the MSVC linker. The bundled compiler_rt also produces COMDAT sections that are incompatible with MSVC, causing fatal LNK1143. Disable sanitize_c entirely on the root module for MSVC targets and skip bundling both compiler_rt and ubsan_rt since MSVC provides its own runtime.
bc72db4 to
1ce057f
Compare
Zig's ubsan runtime emits /exclude-symbols linker directives that are incompatible with the MSVC linker, causing LNK4229 warnings and LNK1143 errors. Disable bundling ubsan_rt on Windows while keeping compiler_rt which provides essential symbols like memcpy, memset, memmove, and ___chkstk_ms. The previous check used target.result.abi == .msvc which never matched because Zig defaults to the gnu ABI on Windows.
Zig's compiler_rt produces COFF objects with invalid COMDAT sections that the MSVC linker rejects (LNK1143), and its ubsan_rt emits /exclude-symbols directives that MSVC does not understand (LNK4229). Skip bundling both in the static library on Windows since the MSVC CRT provides the needed builtins (memcpy, memset, etc.). The shared library continues to bundle compiler_rt as it needs to be self-contained.
Three issues when linking the static library with the MSVC linker: Use the LLVM backend on Windows to produce valid COFF objects. The self-hosted backend generates compiler_rt objects with invalid COMDAT sections that the MSVC linker rejects (LNK1143). Disable bundling ubsan_rt on Windows. Zig's ubsan runtime emits /exclude-symbols linker directives that MSVC does not understand (LNK4229). Add ntdll and kernel32 as transitive link dependencies for the static library on Windows. The Zig standard library uses NT API functions (NtClose, NtCreateSection, etc.) that consumers must link.
Zig defaults to the GNU ABI on Windows, which produces COFF objects with invalid COMDAT sections in compiler_rt that the MSVC linker rejects (LNK1143), and uses GNU conventions like ___chkstk_ms that are unavailable in the MSVC CRT. Default to the MSVC ABI when no explicit ABI is requested, following the same pattern as the existing macOS target override. This ensures compiler_rt produces valid COFF and the generated code uses MSVC-compatible symbols. Users can still explicitly request the GNU ABI via -Dtarget. Also disable bundling ubsan_rt on Windows (its /exclude-symbols directives are MSVC-incompatible) and add ntdll and kernel32 as transitive link dependencies for the static library.
Zig's bundled libc++/libc++abi conflicts with the MSVC C++ runtime headers (vcruntime_typeinfo.h, vcruntime_exception.h, etc.) when targeting native-native-msvc. This caused compilation failures in the SIMD C++ code due to -nostdinc++ suppressing MSVC headers and libc++ types clashing with MSVC runtime types. Skip linkLibCpp() for MSVC targets across all packages (highway, simdutf, utfcpp) and the main build (SharedDeps, GhosttyZig) since MSVC provides its own C++ standard library natively. Also add missing <iterator> and <cstddef> includes that were previously pulled in transitively through libc++ headers but are not guaranteed by MSVC's headers.
|
Actually a lot of issues related to the msvc ABI, so chasing those down now... |
When compiling C++ files, Zig unconditionally passes -nostdinc++ and, if link_libcpp is set, adds its bundled libc++/libc++abi include paths as replacements (see Compilation.zig). On MSVC targets this conflicts with the MSVC C++ runtime headers (vcruntime_typeinfo.h, vcruntime_exception.h, etc.), causing compilation failures in SIMD C++ code. The fix is to use linkLibC instead of linkLibCpp on MSVC. Zig always passes -nostdinc to strip default search paths, but LibCDirs.detect re-adds the MSVC SDK include directories, which contain both C and C++ standard library headers. This gives us proper access to MSVC's own <optional>, <iterator>, <cstddef>, etc. without the libc++ conflicts. For the package builds (highway, simdutf, utfcpp) this means switching from linkLibCpp to linkLibC on MSVC. For SharedDeps and GhosttyZig, linkLibC is already called separately, so we just skip linkLibCpp.
The SIMD C++ files use C++17 features (std::optional, std::size). With Zig's bundled libc++ these are available implicitly, but MSVC headers guard C++17 features behind the standard version (_HAS_CXX17). Without an explicit -std=c++17 flag, clang defaults to a lower standard and the MSVC <optional> header does not define std::optional.
44374ea to
b4c529a
Compare
The SIMD C++ files reference __ubsan_handle_* symbols when compiled in debug mode, but we do not link or bundle the ubsan runtime on MSVC. This matches what the highway and simdutf packages already do in their own build files.
The "Run Example" step in the build-examples-cmake-windows job hangs, so remove it entirely. The build step is still run so compilation is verified, but the examples are no longer executed on Windows.
|
Okay!! I think we finally made it to a finish line (not the finish line, since I'm sure other issues remain). This PR fixed a whole trove of build issues we had with the msvc ABI. That was the major blocker. Our lib-vt shared and static builds on Windows now work great with the msvc ABI. Our examples build but they hang when running. I don't know why they hang but even getting them to build is such a huge milestone I'm willing to look past that and open an issue to track that. If CI goes green I'll merge this. |
|
Yes, we've done it. |
# What PR #11756 added IMPORTED_IMPLIB pointing to the .lib import library, but the import library is not listed in the OUTPUT directive of the `add_custom_command` that runs zig build. The file is produced as a side-effect of the build. This works with the Visual Studio generator (which is lenient about undeclared outputs) but fails with Ninja: ninja: error: 'zig-out/lib/ghostty-vt.lib', needed by 'ghostling', missing and no known rule to make it The fix adds "${ZIG_OUT_DIR}/lib/${GHOSTTY_VT_IMPLIB}" to the OUTPUT list. No behavioral change. The file was already being built, Ninja just needs to know about it. ## but_why.gif I am cleaning up ghostty-org/ghostling#6 and I realise that in order to get rid of the CMake workarounds we had before #11756, this change is necessary. # POC I set up a branch pointing at my fork with a POC and it builds, this is the cleaned up CMakeList https://github.com/deblasis/winghostling/blob/test/cmake-implib-no-workaround/CMakeLists.txt
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.