From 555bf7e92292b40b3d5b6b450bf8b92c99fd174c Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 21 Mar 2026 15:05:23 -0700 Subject: [PATCH] build: add cmake static library support Expose both shared and static libraries as separate CMake imported targets (ghostty-vt and ghostty-vt-static) rather than toggling between them with BUILD_SHARED_LIBS. The zig build already produces both in a single invocation, so both are always available. The find_package config template is updated to export both targets as ghostty-vt::ghostty-vt and ghostty-vt::ghostty-vt-static. Add a c-vt-cmake-static example that demonstrates linking the static library via FetchContent with -Dsimd=false to avoid C++ runtime dependencies. --- CMakeLists.txt | 53 ++++++++++++++++++------ dist/cmake/ghostty-vt-config.cmake.in | 24 +++++++++-- example/c-vt-cmake-static/CMakeLists.txt | 13 ++++++ example/c-vt-cmake-static/README.md | 21 ++++++++++ example/c-vt-cmake-static/src/main.c | 52 +++++++++++++++++++++++ 5 files changed, 147 insertions(+), 16 deletions(-) create mode 100644 example/c-vt-cmake-static/CMakeLists.txt create mode 100644 example/c-vt-cmake-static/README.md create mode 100644 example/c-vt-cmake-static/src/main.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 87925751e..cfa2677fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,8 @@ # ) # FetchContent_MakeAvailable(ghostty) # -# target_link_libraries(myapp PRIVATE ghostty-vt) +# 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: # @@ -40,7 +41,8 @@ # Option 2 — find_package (after installing to a prefix): # # find_package(ghostty-vt REQUIRED) -# target_link_libraries(myapp PRIVATE ghostty-vt::ghostty-vt) +# target_link_libraries(myapp PRIVATE ghostty-vt::ghostty-vt) # shared +# target_link_libraries(myapp PRIVATE ghostty-vt::ghostty-vt-static) # static # # See dist/cmake/README.md for more details and example/c-vt-cmake/ for a # complete working example. @@ -71,7 +73,7 @@ message(STATUS "Found zig: ${ZIG_EXECUTABLE}") # The zig build installs into zig-out/ relative to the source tree. set(ZIG_OUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/zig-out") -# The library file that zig build produces. +# 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}") @@ -85,28 +87,35 @@ else() set(GHOSTTY_VT_REALNAME "${CMAKE_SHARED_LIBRARY_PREFIX}ghostty-vt${CMAKE_SHARED_LIBRARY_SUFFIX}.0.1.0") endif() -set(GHOSTTY_VT_LIBRARY "${ZIG_OUT_DIR}/lib/${GHOSTTY_VT_REALNAME}") +set(GHOSTTY_VT_SHARED_LIBRARY "${ZIG_OUT_DIR}/lib/${GHOSTTY_VT_REALNAME}") + +# Static library name. +set(GHOSTTY_VT_STATIC_REALNAME "${CMAKE_STATIC_LIBRARY_PREFIX}ghostty-vt${CMAKE_STATIC_LIBRARY_SUFFIX}") +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 +# Custom command: run zig build -Demit-lib-vt (produces both shared and static) add_custom_command( - OUTPUT "${GHOSTTY_VT_LIBRARY}" + OUTPUT "${GHOSTTY_VT_SHARED_LIBRARY}" "${GHOSTTY_VT_STATIC_LIBRARY}" 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_LIBRARY}") +add_custom_target(zig_build_lib_vt ALL + DEPENDS "${GHOSTTY_VT_SHARED_LIBRARY}" "${GHOSTTY_VT_STATIC_LIBRARY}" +) -# --- IMPORTED library target -------------------------------------------------- +# --- IMPORTED library targets ------------------------------------------------ +# Shared add_library(ghostty-vt SHARED IMPORTED GLOBAL) set_target_properties(ghostty-vt PROPERTIES - IMPORTED_LOCATION "${GHOSTTY_VT_LIBRARY}" + IMPORTED_LOCATION "${GHOSTTY_VT_SHARED_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${ZIG_OUT_DIR}/include" ) if(APPLE) @@ -118,15 +127,32 @@ elseif(NOT WIN32) IMPORTED_SONAME "${GHOSTTY_VT_SONAME}" ) endif() - add_dependencies(ghostty-vt zig_build_lib_vt) +# Static +# +# When linking the static library, consumers must also link its transitive +# dependencies. By default (with SIMD enabled), these are: +# - libc +# - libc++ (or libstdc++ on Linux) +# - highway +# - simdutf +# +# Building with -Dsimd=false removes the C++ / highway / simdutf +# dependencies, leaving only libc. +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" +) +add_dependencies(ghostty-vt-static zig_build_lib_vt) + # --- Install ------------------------------------------------------------------ include(GNUInstallDirs) -# Install the library -install(FILES "${GHOSTTY_VT_LIBRARY}" TYPE LIB) +# Install shared library +install(FILES "${GHOSTTY_VT_SHARED_LIBRARY}" TYPE LIB) if(NOT WIN32) # Install symlinks install(CODE " @@ -139,6 +165,9 @@ if(NOT WIN32) ") 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}") diff --git a/dist/cmake/ghostty-vt-config.cmake.in b/dist/cmake/ghostty-vt-config.cmake.in index d67f99c0b..9e1d65f6c 100644 --- a/dist/cmake/ghostty-vt-config.cmake.in +++ b/dist/cmake/ghostty-vt-config.cmake.in @@ -2,11 +2,12 @@ include(CMakeFindDependencyMacro) +set(_ghostty_vt_libdir "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_LIBDIR@") + +# Shared library target if(NOT TARGET ghostty-vt::ghostty-vt) add_library(ghostty-vt::ghostty-vt SHARED IMPORTED) - set(_ghostty_vt_libdir "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_LIBDIR@") - set_target_properties(ghostty-vt::ghostty-vt PROPERTIES IMPORTED_LOCATION "${_ghostty_vt_libdir}/@GHOSTTY_VT_REALNAME@" INTERFACE_INCLUDE_DIRECTORIES "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_INCLUDEDIR@" @@ -26,8 +27,23 @@ if(NOT TARGET ghostty-vt::ghostty-vt) IMPORTED_SONAME "@GHOSTTY_VT_SONAME@" ) endif() - - unset(_ghostty_vt_libdir) endif() +# Static library target +# +# Consumers must link transitive dependencies themselves. By default (with +# SIMD enabled): libc, libc++ (or libstdc++ on Linux), highway, and +# simdutf. Building with -Dsimd=false removes the C++ / highway / simdutf +# dependencies. +if(NOT TARGET ghostty-vt::ghostty-vt-static) + add_library(ghostty-vt::ghostty-vt-static STATIC IMPORTED) + + set_target_properties(ghostty-vt::ghostty-vt-static PROPERTIES + IMPORTED_LOCATION "${_ghostty_vt_libdir}/@GHOSTTY_VT_STATIC_REALNAME@" + INTERFACE_INCLUDE_DIRECTORIES "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_INCLUDEDIR@" + ) +endif() + +unset(_ghostty_vt_libdir) + check_required_components(ghostty-vt) diff --git a/example/c-vt-cmake-static/CMakeLists.txt b/example/c-vt-cmake-static/CMakeLists.txt new file mode 100644 index 000000000..bb4b1ac35 --- /dev/null +++ b/example/c-vt-cmake-static/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.19) +project(c-vt-cmake-static LANGUAGES C) + +include(FetchContent) +FetchContent_Declare(ghostty + GIT_REPOSITORY https://github.com/ghostty-org/ghostty.git + GIT_TAG main +) +set(GHOSTTY_ZIG_BUILD_FLAGS "-Dsimd=false" CACHE STRING "" FORCE) +FetchContent_MakeAvailable(ghostty) + +add_executable(c_vt_cmake_static src/main.c) +target_link_libraries(c_vt_cmake_static PRIVATE ghostty-vt-static) diff --git a/example/c-vt-cmake-static/README.md b/example/c-vt-cmake-static/README.md new file mode 100644 index 000000000..6aa503e04 --- /dev/null +++ b/example/c-vt-cmake-static/README.md @@ -0,0 +1,21 @@ +# c-vt-cmake-static + +Demonstrates consuming libghostty-vt as a **static** library from a CMake +project using `FetchContent`. Creates a terminal, writes VT sequences into +it, and formats the screen contents as plain text. + +## Building + +```shell-session +cd example/c-vt-cmake-static +cmake -B build +cmake --build build +./build/c_vt_cmake_static +``` + +To build against a local checkout instead of fetching from GitHub: + +```shell-session +cmake -B build -DFETCHCONTENT_SOURCE_DIR_GHOSTTY=../.. +cmake --build build +``` diff --git a/example/c-vt-cmake-static/src/main.c b/example/c-vt-cmake-static/src/main.c new file mode 100644 index 000000000..4821e727e --- /dev/null +++ b/example/c-vt-cmake-static/src/main.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include + +int main() { + // Create a terminal with a small grid + GhosttyTerminal terminal; + GhosttyTerminalOptions opts = { + .cols = 80, + .rows = 24, + .max_scrollback = 0, + }; + GhosttyResult result = ghostty_terminal_new(NULL, &terminal, opts); + assert(result == GHOSTTY_SUCCESS); + + // Write some VT-encoded content into the terminal + const char *commands[] = { + "Hello from a \033[1mCMake\033[0m-built program (static)!\r\n", + "Line 2: \033[4munderlined\033[0m text\r\n", + "Line 3: \033[31mred\033[0m \033[32mgreen\033[0m \033[34mblue\033[0m\r\n", + }; + for (size_t i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) { + ghostty_terminal_vt_write(terminal, (const uint8_t *)commands[i], + strlen(commands[i])); + } + + // Format the terminal contents as plain text + GhosttyFormatterTerminalOptions fmt_opts = + GHOSTTY_INIT_SIZED(GhosttyFormatterTerminalOptions); + fmt_opts.emit = GHOSTTY_FORMATTER_FORMAT_PLAIN; + fmt_opts.trim = true; + + GhosttyFormatter formatter; + result = ghostty_formatter_terminal_new(NULL, &formatter, terminal, fmt_opts); + assert(result == GHOSTTY_SUCCESS); + + uint8_t *buf = NULL; + size_t len = 0; + result = ghostty_formatter_format_alloc(formatter, NULL, &buf, &len); + assert(result == GHOSTTY_SUCCESS); + + printf("Plain text (%zu bytes):\n", len); + fwrite(buf, 1, len, stdout); + printf("\n"); + + free(buf); + ghostty_formatter_free(formatter); + ghostty_terminal_free(terminal); + return 0; +}