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.
This commit is contained in:
Mitchell Hashimoto
2026-03-21 15:05:23 -07:00
parent 8d6be5a3dd
commit 555bf7e922
5 changed files with 147 additions and 16 deletions

View File

@@ -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}")

View File

@@ -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)

View File

@@ -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)

View File

@@ -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
```

View File

@@ -0,0 +1,52 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ghostty/vt.h>
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;
}