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.
This commit is contained in:
Mitchell Hashimoto
2026-04-09 21:01:54 -07:00
parent 48a01b8bd5
commit f2e299fb46
9 changed files with 472 additions and 31 deletions

View File

@@ -0,0 +1,59 @@
cmake_minimum_required(VERSION 3.19)
# --- Determine cross-compilation target before project() --------------------
#
# We need to know the target before project() so we can set up zig cc as the
# C/C++ compiler for the cross target.
# Pick a cross-compilation target: build for a different OS than the host.
# Can be overridden with -DZIG_TARGET=... on the command line.
if(NOT ZIG_TARGET)
# CMAKE_HOST_SYSTEM_PROCESSOR may not be set before project(), so
# fall back to `uname -m`.
if(CMAKE_HOST_SYSTEM_PROCESSOR)
set(_arch "${CMAKE_HOST_SYSTEM_PROCESSOR}")
else()
execute_process(COMMAND uname -m OUTPUT_VARIABLE _arch OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
if(_arch MATCHES "^(x86_64|AMD64)$")
set(_arch "x86_64")
elseif(_arch MATCHES "^(aarch64|arm64|ARM64)$")
set(_arch "aarch64")
endif()
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
set(ZIG_TARGET "${_arch}-windows-gnu")
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
set(ZIG_TARGET "${_arch}-linux-gnu")
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
set(ZIG_TARGET "${_arch}-linux-gnu")
else()
message(FATAL_ERROR
"Cannot derive ZIG_TARGET for ${CMAKE_HOST_SYSTEM_NAME}. "
"Pass -DZIG_TARGET=... manually.")
endif()
message(STATUS "Cross-compiling for ZIG_TARGET: ${ZIG_TARGET}")
endif()
# --- Set up zig cc as the cross compiler ------------------------------------
# GhosttyZigCompiler.cmake must be called before project().
# Downstream projects would copy this file into their tree; here we
# include it directly from the repo.
include(../../dist/cmake/GhosttyZigCompiler.cmake)
ghostty_zig_compiler(ZIG_TARGET "${ZIG_TARGET}")
project(c-vt-cmake-cross LANGUAGES C CXX)
include(FetchContent)
FetchContent_Declare(ghostty
GIT_REPOSITORY https://github.com/ghostty-org/ghostty.git
GIT_TAG main
)
FetchContent_MakeAvailable(ghostty)
ghostty_vt_add_target(NAME cross ZIG_TARGET "${ZIG_TARGET}")
add_executable(c_vt_cmake_cross src/main.c)
target_link_libraries(c_vt_cmake_cross PRIVATE ghostty-vt-static-cross)

View File

@@ -0,0 +1,21 @@
# c-vt-cmake-cross
Demonstrates using `ghostty_vt_add_target()` to cross-compile
libghostty-vt with static linking. The target OS is chosen automatically:
| Host | Target |
| ------- | --------------- |
| Linux | Windows (MinGW) |
| Windows | Linux (glibc) |
| macOS | Linux (glibc) |
Override with `-DZIG_TARGET=...` if needed.
## Building
```shell-session
cd example/c-vt-cmake-cross
cmake -B build -DFETCHCONTENT_SOURCE_DIR_GHOSTTY=../..
cmake --build build
file build/c_vt_cmake_cross
```

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!\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");
ghostty_free(NULL, buf, len);
ghostty_formatter_free(formatter);
ghostty_terminal_free(terminal);
return 0;
}