Files
ghostty/CMakeLists.txt
Mitchell Hashimoto f2e299fb46 cmake: add ghostty_vt_add_target() for cross-compilation
Add a ghostty_vt_add_target() CMake function that lets downstream
projects build libghostty-vt for a specific Zig target triple. The
function encapsulates zig discovery, build-type-to-optimize mapping,
the zig build invocation, and output path conventions so consumers
do not need to duplicate any of that logic. It creates named IMPORTED
targets (e.g. ghostty-vt-static-linux-amd64) that work alongside the
existing native ghostty-vt and ghostty-vt-static targets.

The build-type mapping is factored into a shared _GHOSTTY_ZIG_OPT_FLAG
variable used by both the native build and the new function.

The static library targets now propagate c++ as a link dependency on
non-Windows platforms, fixing link failures when consumers use static
linking with the default SIMD-enabled build.

A new example/c-vt-cmake-cross/ demonstrates end-to-end cross-
compilation using zig cc as the C compiler, auto-detecting a cross
target based on the host OS.
2026-04-10 06:52:09 -07:00

383 lines
14 KiB
CMake

# CMake wrapper for libghostty-vt
#
# This file delegates to `zig build -Demit-lib-vt` to produce the shared library,
# headers, and pkg-config file. It exists so that CMake-based projects can
# consume libghostty-vt without interacting with the Zig build system
# directly. However, downstream users do still require `zig` on the PATH.
# Please consult the Ghostty docs for the required Zig version:
#
# https://ghostty.org/docs/install/build
#
# Building within the Ghostty repo
# ---------------------------------
#
# cmake -B build
# cmake --build build
# cmake --install build --prefix /usr/local
#
# Pass extra flags to the Zig build with GHOSTTY_ZIG_BUILD_FLAGS:
#
# cmake -B build -DGHOSTTY_ZIG_BUILD_FLAGS="-Demit-macos-app=false"
#
# Integrating into a downstream CMake project
# ---------------------------------------------
#
# Option 1 — FetchContent (recommended, no manual install step):
#
# include(FetchContent)
# FetchContent_Declare(ghostty
# GIT_REPOSITORY https://github.com/ghostty-org/ghostty.git
# GIT_TAG main
# )
# FetchContent_MakeAvailable(ghostty)
#
# target_link_libraries(myapp PRIVATE ghostty-vt) # shared
# target_link_libraries(myapp PRIVATE ghostty-vt-static) # static
#
# To use a local checkout instead of fetching:
#
# cmake -B build -DFETCHCONTENT_SOURCE_DIR_GHOSTTY=/path/to/ghostty
#
# Option 2 — find_package (after installing to a prefix):
#
# find_package(ghostty-vt REQUIRED)
# target_link_libraries(myapp PRIVATE ghostty-vt::ghostty-vt) # shared
# target_link_libraries(myapp PRIVATE ghostty-vt::ghostty-vt-static) # static
#
# Cross-compilation
# -------------------
#
# For building libghostty-vt for a non-native Zig target (e.g. cross-
# compiling), use the ghostty_vt_add_target() function after FetchContent:
#
# FetchContent_MakeAvailable(ghostty)
# ghostty_vt_add_target(NAME linux-amd64 ZIG_TARGET x86_64-linux-gnu)
#
# target_link_libraries(myapp PRIVATE ghostty-vt-static-linux-amd64) # static
# target_link_libraries(myapp PRIVATE ghostty-vt-linux-amd64) # shared
#
# This handles zig discovery, build-type-to-optimize mapping, and output
# path conventions internally. Extra flags can be forwarded with ZIG_FLAGS:
#
# ghostty_vt_add_target(NAME linux-amd64 ZIG_TARGET x86_64-linux-gnu
# ZIG_FLAGS -Dsimd=false)
#
# See dist/cmake/README.md for more details, example/c-vt-cmake/ for a
# complete working example, and example/c-vt-cmake-cross/ for a cross-
# compilation example.
cmake_minimum_required(VERSION 3.19)
project(ghostty-vt VERSION 0.1.0 LANGUAGES C)
# --- Options ----------------------------------------------------------------
set(GHOSTTY_ZIG_BUILD_FLAGS "" CACHE STRING "Additional flags to pass to zig build")
# Map CMake build types to Zig optimization levels. The result is stored in
# _GHOSTTY_ZIG_OPT_FLAG so both the native build and ghostty_vt_add_target()
# can reuse it without duplicating the mapping logic.
set(_GHOSTTY_ZIG_OPT_FLAG "")
if(CMAKE_BUILD_TYPE)
string(TOUPPER "${CMAKE_BUILD_TYPE}" _bt)
if(_bt STREQUAL "RELEASE" OR _bt STREQUAL "MINSIZEREL" OR _bt STREQUAL "RELWITHDEBINFO")
set(_GHOSTTY_ZIG_OPT_FLAG "-Doptimize=ReleaseFast")
endif()
unset(_bt)
endif()
if(_GHOSTTY_ZIG_OPT_FLAG)
list(APPEND GHOSTTY_ZIG_BUILD_FLAGS "${_GHOSTTY_ZIG_OPT_FLAG}")
endif()
# --- Find Zig ----------------------------------------------------------------
find_program(ZIG_EXECUTABLE zig REQUIRED)
message(STATUS "Found zig: ${ZIG_EXECUTABLE}")
# --- Build via zig build -----------------------------------------------------
# The zig build installs into zig-out/ relative to the source tree.
set(ZIG_OUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/zig-out")
# Shared library names (zig build produces both shared and static).
if(APPLE)
set(GHOSTTY_VT_LIBNAME "${CMAKE_SHARED_LIBRARY_PREFIX}ghostty-vt${CMAKE_SHARED_LIBRARY_SUFFIX}")
set(GHOSTTY_VT_SONAME "${CMAKE_SHARED_LIBRARY_PREFIX}ghostty-vt.0${CMAKE_SHARED_LIBRARY_SUFFIX}")
set(GHOSTTY_VT_REALNAME "${CMAKE_SHARED_LIBRARY_PREFIX}ghostty-vt.0.1.0${CMAKE_SHARED_LIBRARY_SUFFIX}")
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()
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.
# 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
# INTERFACE_INCLUDE_DIRECTORIES before the zig build has run.
file(MAKE_DIRECTORY "${ZIG_OUT_DIR}/include")
# Custom command: run zig build -Demit-lib-vt (produces both shared and static)
add_custom_command(
OUTPUT "${GHOSTTY_VT_SHARED_LIBRARY}" "${GHOSTTY_VT_STATIC_LIBRARY}" "${ZIG_OUT_DIR}/lib/${GHOSTTY_VT_IMPLIB}"
COMMAND "${ZIG_EXECUTABLE}" build -Demit-lib-vt ${GHOSTTY_ZIG_BUILD_FLAGS}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Building libghostty-vt via zig build..."
USES_TERMINAL
)
add_custom_target(zig_build_lib_vt ALL
DEPENDS "${GHOSTTY_VT_SHARED_LIBRARY}" "${GHOSTTY_VT_STATIC_LIBRARY}"
)
# Tell CMake's clean target to also remove Zig's output directory.
set_property(DIRECTORY APPEND PROPERTY
ADDITIONAL_CLEAN_FILES "${ZIG_OUT_DIR}"
)
# --- IMPORTED library targets ------------------------------------------------
# Shared
add_library(ghostty-vt SHARED IMPORTED GLOBAL)
set_target_properties(ghostty-vt PROPERTIES
IMPORTED_LOCATION "${GHOSTTY_VT_SHARED_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${ZIG_OUT_DIR}/include"
)
if(APPLE)
set_target_properties(ghostty-vt PROPERTIES
IMPORTED_SONAME "@rpath/${GHOSTTY_VT_SONAME}"
)
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}"
)
endif()
add_dependencies(ghostty-vt zig_build_lib_vt)
# Static
#
# On Linux and macOS, the static library is a fat archive that bundles
# the vendored SIMD dependencies (highway, simdutf, utfcpp). Consumers
# only need to link libc and libc++ (LLVM's C++ runtime, not GNU
# libstdc++). Use zig cc, clang, or any toolchain with libc++ support.
#
# On Windows, the SIMD dependencies are not bundled and must be linked
# separately.
#
# Building with -Dsimd=false removes all runtime dependencies.
add_library(ghostty-vt-static STATIC IMPORTED GLOBAL)
set_target_properties(ghostty-vt-static PROPERTIES
IMPORTED_LOCATION "${GHOSTTY_VT_STATIC_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${ZIG_OUT_DIR}/include"
INTERFACE_COMPILE_DEFINITIONS "GHOSTTY_STATIC"
)
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 ------------------------------------------------------------------
include(GNUInstallDirs)
# Install shared library
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
\"${GHOSTTY_VT_REALNAME}\"
\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/${GHOSTTY_VT_SONAME}\")
execute_process(COMMAND \${CMAKE_COMMAND} -E create_symlink
\"${GHOSTTY_VT_SONAME}\"
\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/${GHOSTTY_VT_LIBNAME}\")
")
endif()
# Install static library
install(FILES "${GHOSTTY_VT_STATIC_LIBRARY}" TYPE LIB)
# Install headers
install(DIRECTORY "${ZIG_OUT_DIR}/include/ghostty" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
# --- CMake package config for find_package() ----------------------------------
include(CMakePackageConfigHelpers)
# Generate the config file
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/dist/cmake/ghostty-vt-config.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/ghostty-vt-config.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ghostty-vt"
)
# Generate the version file
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/ghostty-vt-config-version.cmake"
VERSION "${PROJECT_VERSION}"
COMPATIBILITY SameMajorVersion
)
# Install the config files
install(
FILES
"${CMAKE_CURRENT_BINARY_DIR}/ghostty-vt-config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/ghostty-vt-config-version.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ghostty-vt"
)
# --- Cross-compilation helper ------------------------------------------------
#
# For downstream projects that need to build libghostty-vt for a specific
# Zig target triple. For native builds, use the IMPORTED targets above
# (ghostty-vt, ghostty-vt-static) directly.
#
# Usage (in a downstream CMakeLists.txt after FetchContent_MakeAvailable):
#
# ghostty_vt_add_target(NAME linux-amd64 ZIG_TARGET x86_64-linux-gnu)
#
# Creates:
# ghostty-vt-static-linux-amd64 (IMPORTED STATIC library)
# ghostty-vt-linux-amd64 (IMPORTED SHARED library)
#
# Optional ZIG_FLAGS to pass additional flags to zig build:
#
# ghostty_vt_add_target(NAME linux-amd64 ZIG_TARGET x86_64-linux-gnu
# ZIG_FLAGS -Dsimd=false)
function(ghostty_vt_add_target)
cmake_parse_arguments(PARSE_ARGV 0 _GVT "" "NAME;ZIG_TARGET" "ZIG_FLAGS")
if(NOT _GVT_NAME)
message(FATAL_ERROR "ghostty_vt_add_target: NAME is required")
endif()
if(NOT _GVT_ZIG_TARGET)
message(FATAL_ERROR "ghostty_vt_add_target: ZIG_TARGET is required")
endif()
set(_src_dir "${CMAKE_CURRENT_FUNCTION_LIST_DIR}")
set(_prefix "${CMAKE_CURRENT_BINARY_DIR}/ghostty-${_GVT_NAME}")
# Build flags
set(_flags
-Demit-lib-vt
-Dtarget=${_GVT_ZIG_TARGET}
--prefix "${_prefix}"
)
# Default to ReleaseFast when no build type is set. Debug builds enable
# UBSan in zig, and the sanitizer runtime is not available for all
# cross-compilation targets.
if(_GHOSTTY_ZIG_OPT_FLAG)
list(APPEND _flags "${_GHOSTTY_ZIG_OPT_FLAG}")
else()
list(APPEND _flags "-Doptimize=ReleaseFast")
endif()
if(_GVT_ZIG_FLAGS)
list(APPEND _flags ${_GVT_ZIG_FLAGS})
endif()
# Output paths
set(_include_dir "${_prefix}/include")
if(_GVT_ZIG_TARGET MATCHES "windows")
set(_static_lib "${_prefix}/lib/ghostty-vt-static.lib")
set(_shared_lib "${_prefix}/bin/ghostty-vt.dll")
set(_implib "${_prefix}/lib/ghostty-vt.lib")
elseif(_GVT_ZIG_TARGET MATCHES "darwin|macos")
set(_static_lib "${_prefix}/lib/libghostty-vt.a")
set(_shared_lib "${_prefix}/lib/libghostty-vt.0.1.0.dylib")
else()
set(_static_lib "${_prefix}/lib/libghostty-vt.a")
set(_shared_lib "${_prefix}/lib/libghostty-vt.so.0.1.0")
endif()
file(MAKE_DIRECTORY "${_include_dir}")
# Custom command: invoke zig build
add_custom_command(
OUTPUT "${_static_lib}" "${_shared_lib}"
COMMAND "${ZIG_EXECUTABLE}" build ${_flags}
WORKING_DIRECTORY "${_src_dir}"
COMMENT "Building libghostty-vt for ${_GVT_ZIG_TARGET}..."
USES_TERMINAL
)
set(_build_target "zig_build_lib_vt_${_GVT_NAME}")
add_custom_target(${_build_target} ALL
DEPENDS "${_static_lib}" "${_shared_lib}"
)
# Static target
set(_static_target "ghostty-vt-static-${_GVT_NAME}")
add_library(${_static_target} STATIC IMPORTED GLOBAL)
set_target_properties(${_static_target} PROPERTIES
IMPORTED_LOCATION "${_static_lib}"
INTERFACE_INCLUDE_DIRECTORIES "${_include_dir}"
INTERFACE_COMPILE_DEFINITIONS "GHOSTTY_STATIC"
)
if(_GVT_ZIG_TARGET MATCHES "windows")
set_target_properties(${_static_target} PROPERTIES
INTERFACE_LINK_LIBRARIES "c++;ntdll;kernel32"
)
else()
set_target_properties(${_static_target} PROPERTIES
INTERFACE_LINK_LIBRARIES "c++"
)
endif()
add_dependencies(${_static_target} ${_build_target})
# Shared target
set(_shared_target "ghostty-vt-${_GVT_NAME}")
add_library(${_shared_target} SHARED IMPORTED GLOBAL)
set_target_properties(${_shared_target} PROPERTIES
IMPORTED_LOCATION "${_shared_lib}"
INTERFACE_INCLUDE_DIRECTORIES "${_include_dir}"
)
if(_GVT_ZIG_TARGET MATCHES "windows")
set_target_properties(${_shared_target} PROPERTIES
IMPORTED_IMPLIB "${_implib}"
)
elseif(_GVT_ZIG_TARGET MATCHES "darwin|macos")
set_target_properties(${_shared_target} PROPERTIES
IMPORTED_SONAME "@rpath/libghostty-vt.0.dylib"
)
else()
set_target_properties(${_shared_target} PROPERTIES
IMPORTED_SONAME "libghostty-vt.so.0"
)
endif()
add_dependencies(${_shared_target} ${_build_target})
endfunction()