On macOS GUI builds, chdir(HOME) runs before deferred file opens are processed, breaking relative paths passed on the command line. Resolve each argv path to absolute immediately during argument parsing. Bump version to 1.11.2. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
501 lines
15 KiB
CMake
501 lines
15 KiB
CMake
cmake_minimum_required(VERSION 3.15)
|
|
project(kte)
|
|
|
|
include(GNUInstallDirs)
|
|
|
|
set(CMAKE_CXX_STANDARD 20)
|
|
set(KTE_VERSION "1.11.2")
|
|
|
|
# Default to terminal-only build to avoid SDL/OpenGL dependency by default.
|
|
# Enable with -DBUILD_GUI=ON when SDL2/OpenGL/Freetype are available.
|
|
set(BUILD_GUI ON CACHE BOOL "Enable building the graphical version.")
|
|
set(KTE_USE_QT OFF CACHE BOOL "Build the QT frontend instead of ImGui.")
|
|
set(BUILD_TESTS ON CACHE BOOL "Enable building test programs.")
|
|
set(KTE_FONT_SIZE "18.0" CACHE STRING "Default font size for GUI")
|
|
option(KTE_UNDO_DEBUG "Enable undo instrumentation logs" OFF)
|
|
option(KTE_ENABLE_TREESITTER "Enable optional Tree-sitter highlighter adapter" OFF)
|
|
option(KTE_STATIC_LINK "Enable static linking on Linux" OFF)
|
|
|
|
# Optionally enable AddressSanitizer (ASan)
|
|
option(ENABLE_ASAN "Enable AddressSanitizer for builds" OFF)
|
|
|
|
if (ENABLE_ASAN)
|
|
message(STATUS "ASan enabled")
|
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
|
# Ensure the sanitizer is linked too (especially important on some platforms)
|
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
|
|
endif ()
|
|
|
|
if (CMAKE_HOST_UNIX)
|
|
message(STATUS "Build system is POSIX.")
|
|
else ()
|
|
message(STATUS "Build system is NOT POSIX.")
|
|
endif ()
|
|
|
|
add_compile_options(
|
|
|
|
)
|
|
|
|
if (MSVC)
|
|
add_compile_options("/W4" "$<$<CONFIG:RELEASE>:/O2>")
|
|
else ()
|
|
add_compile_options(
|
|
"-Wall"
|
|
"-Wextra"
|
|
"-Werror"
|
|
"-pedantic"
|
|
"-Wno-unused-function"
|
|
"-Wno-unused-parameter"
|
|
"$<$<CONFIG:RELEASE>:-O2>"
|
|
"$<$<CONFIG:DEBUG>:-g>"
|
|
)
|
|
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
|
add_compile_options("-stdlib=libc++")
|
|
add_link_options("-stdlib=libc++")
|
|
else ()
|
|
# nothing special for gcc at the moment
|
|
endif ()
|
|
endif ()
|
|
add_compile_definitions(KGE_PLATFORM=${CMAKE_HOST_SYSTEM_NAME})
|
|
add_compile_definitions(KTE_VERSION_STR="v${KTE_VERSION}")
|
|
if (KTE_ENABLE_TREESITTER)
|
|
add_compile_definitions(KTE_ENABLE_TREESITTER)
|
|
endif ()
|
|
|
|
message(STATUS "Build system: ${CMAKE_HOST_SYSTEM_NAME}")
|
|
|
|
if (BUILD_GUI)
|
|
include(cmake/imgui.cmake)
|
|
endif ()
|
|
|
|
# NCurses for terminal mode
|
|
set(CURSES_NEED_NCURSES TRUE)
|
|
set(CURSES_NEED_WIDE TRUE)
|
|
find_package(Curses REQUIRED)
|
|
include_directories(${CURSES_INCLUDE_DIR})
|
|
|
|
# On Alpine Linux, CMake's FindCurses looks in wrong paths
|
|
# Manually find the correct ncurses library
|
|
if (EXISTS "/etc/alpine-release")
|
|
find_library(NCURSESW_LIB NAMES ncursesw PATHS /usr/lib /lib REQUIRED)
|
|
set(CURSES_LIBRARIES ${NCURSESW_LIB})
|
|
message(STATUS "Alpine Linux detected, using ncurses at: ${NCURSESW_LIB}")
|
|
endif ()
|
|
|
|
set(SYNTAX_SOURCES
|
|
syntax/GoHighlighter.cc
|
|
syntax/CppHighlighter.cc
|
|
syntax/JsonHighlighter.cc
|
|
syntax/ErlangHighlighter.cc
|
|
syntax/MarkdownHighlighter.cc
|
|
syntax/TreeSitterHighlighter.cc
|
|
syntax/LispHighlighter.cc
|
|
syntax/HighlighterEngine.cc
|
|
syntax/RustHighlighter.cc
|
|
syntax/HighlighterRegistry.cc
|
|
syntax/SqlHighlighter.cc
|
|
syntax/NullHighlighter.cc
|
|
syntax/ForthHighlighter.cc
|
|
syntax/PythonHighlighter.cc
|
|
syntax/ShellHighlighter.cc
|
|
)
|
|
|
|
if (KTE_ENABLE_TREESITTER)
|
|
list(APPEND SYNTAX_SOURCES
|
|
TreeSitterHighlighter.cc)
|
|
endif ()
|
|
|
|
set(FONT_SOURCES
|
|
fonts/Font.cc
|
|
fonts/FontRegistry.cc
|
|
)
|
|
|
|
if (BUILD_GUI)
|
|
set(GUI_SOURCES
|
|
GUIConfig.cc
|
|
)
|
|
if (KTE_USE_QT)
|
|
find_package(Qt6 COMPONENTS Widgets REQUIRED)
|
|
set(GUI_SOURCES
|
|
${GUI_SOURCES}
|
|
QtFrontend.cc
|
|
QtInputHandler.cc
|
|
QtRenderer.cc
|
|
)
|
|
# Expose preprocessor switch so sources can exclude ImGui-specific code
|
|
add_compile_definitions(KTE_USE_QT)
|
|
else ()
|
|
set(GUI_SOURCES
|
|
${GUI_SOURCES}
|
|
${FONT_SOURCES}
|
|
ImGuiFrontend.cc
|
|
ImGuiInputHandler.cc
|
|
ImGuiRenderer.cc
|
|
)
|
|
endif ()
|
|
endif ()
|
|
|
|
set(COMMON_SOURCES
|
|
PieceTable.cc
|
|
Buffer.cc
|
|
Editor.cc
|
|
Command.cc
|
|
HelpText.cc
|
|
KKeymap.cc
|
|
Swap.cc
|
|
ErrorHandler.cc
|
|
SyscallWrappers.cc
|
|
ErrorRecovery.cc
|
|
TerminalInputHandler.cc
|
|
TerminalRenderer.cc
|
|
TerminalFrontend.cc
|
|
TestInputHandler.cc
|
|
TestRenderer.cc
|
|
TestFrontend.cc
|
|
UndoNode.cc
|
|
UndoTree.cc
|
|
UndoSystem.cc
|
|
|
|
${SYNTAX_SOURCES}
|
|
)
|
|
|
|
set(SYNTAX_HEADERS
|
|
syntax/GoHighlighter.h
|
|
syntax/HighlighterEngine.h
|
|
syntax/ShellHighlighter.h
|
|
syntax/MarkdownHighlighter.h
|
|
syntax/LispHighlighter.h
|
|
syntax/SqlHighlighter.h
|
|
syntax/ForthHighlighter.h
|
|
syntax/JsonHighlighter.h
|
|
syntax/TreeSitterHighlighter.h
|
|
syntax/NullHighlighter.h
|
|
syntax/CppHighlighter.h
|
|
syntax/ErlangHighlighter.h
|
|
syntax/LanguageHighlighter.h
|
|
syntax/RustHighlighter.h
|
|
syntax/PythonHighlighter.h
|
|
)
|
|
|
|
if (KTE_ENABLE_TREESITTER)
|
|
list(APPEND THEME_HEADERS
|
|
TreeSitterHighlighter.h)
|
|
endif ()
|
|
|
|
set(THEME_HEADERS
|
|
themes/ThemeHelpers.h
|
|
themes/EInk.h
|
|
themes/Gruvbox.h
|
|
themes/Solarized.h
|
|
themes/Plan9.h
|
|
themes/Nord.h
|
|
themes/Everforest.h
|
|
themes/KanagawaPaper.h
|
|
themes/LCARS.h
|
|
themes/OldBook.h
|
|
themes/Amber.h
|
|
themes/Orbital.h
|
|
themes/WeylandYutani.h
|
|
themes/Zenburn.h
|
|
)
|
|
|
|
set(FONT_HEADERS
|
|
fonts/Font.h
|
|
fonts/FontRegistry.h
|
|
fonts/FontRegistry.h
|
|
fonts/FontList.h
|
|
fonts/B612Mono.h
|
|
fonts/BrassMono.h
|
|
fonts/CrimsonPro.h
|
|
fonts/ETBook.h
|
|
fonts/BrassMonoCode.h
|
|
fonts/FiraCode.h
|
|
fonts/Go.h
|
|
fonts/IBMPlexMono.h
|
|
fonts/Idealist.h
|
|
fonts/Inconsolata.h
|
|
fonts/InconsolataExpanded.h
|
|
fonts/Iosevka.h
|
|
fonts/IosevkaExtended.h
|
|
fonts/ShareTech.h
|
|
fonts/SpaceMono.h
|
|
fonts/Spectral.h
|
|
fonts/Syne.h
|
|
fonts/Triplicate.h
|
|
fonts/Unispace.h
|
|
fonts/BerkeleyMono.h
|
|
)
|
|
|
|
set(COMMON_HEADERS
|
|
PieceTable.h
|
|
Buffer.h
|
|
Editor.h
|
|
Command.h
|
|
HelpText.h
|
|
KKeymap.h
|
|
Swap.h
|
|
InputHandler.h
|
|
TerminalInputHandler.h
|
|
Renderer.h
|
|
TerminalRenderer.h
|
|
Frontend.h
|
|
TerminalFrontend.h
|
|
TestInputHandler.h
|
|
TestRenderer.h
|
|
TestFrontend.h
|
|
UndoNode.h
|
|
UndoTree.h
|
|
UndoSystem.h
|
|
Highlight.h
|
|
|
|
${SYNTAX_HEADERS}
|
|
)
|
|
|
|
if (BUILD_GUI)
|
|
set(GUI_HEADERS
|
|
GUIConfig.h
|
|
)
|
|
|
|
if (KTE_USE_QT)
|
|
set(GUI_HEADERS
|
|
${GUI_HEADERS}
|
|
QtFrontend.h
|
|
QtInputHandler.h
|
|
QtRenderer.h
|
|
)
|
|
else ()
|
|
set(GUI_HEADERS
|
|
${GUI_HEADERS}
|
|
${THEME_HEADERS}
|
|
${FONT_HEADERS}
|
|
ImGuiFrontend.h
|
|
ImGuiInputHandler.h
|
|
ImGuiRenderer.h
|
|
fonts/BerkeleyMono.h
|
|
)
|
|
endif ()
|
|
endif ()
|
|
|
|
# kte (terminal-first) executable
|
|
add_executable(kte
|
|
main.cc
|
|
${COMMON_SOURCES}
|
|
${COMMON_HEADERS}
|
|
)
|
|
|
|
if (KTE_UNDO_DEBUG)
|
|
target_compile_definitions(kte PRIVATE KTE_UNDO_DEBUG=1)
|
|
endif ()
|
|
|
|
target_link_libraries(kte ${CURSES_LIBRARIES})
|
|
|
|
# Static linking on Linux only (macOS does not support static linking of system libraries)
|
|
if (NOT APPLE AND KTE_STATIC_LINK)
|
|
target_link_options(kte PRIVATE -static)
|
|
endif ()
|
|
|
|
if (KTE_ENABLE_TREESITTER)
|
|
# Users can provide their own tree-sitter include/lib via cache variables
|
|
set(TREESITTER_INCLUDE_DIR "" CACHE PATH "Path to tree-sitter include directory")
|
|
set(TREESITTER_LIBRARY "" CACHE FILEPATH "Path to tree-sitter library (.a/.dylib)")
|
|
if (TREESITTER_INCLUDE_DIR)
|
|
target_include_directories(kte PRIVATE ${TREESITTER_INCLUDE_DIR})
|
|
endif ()
|
|
if (TREESITTER_LIBRARY)
|
|
target_link_libraries(kte ${TREESITTER_LIBRARY})
|
|
endif ()
|
|
endif ()
|
|
|
|
install(TARGETS kte
|
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
)
|
|
|
|
# Man pages
|
|
install(FILES docs/kte.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
|
|
|
|
if (BUILD_TESTS)
|
|
# Unified unit test runner
|
|
add_executable(kte_tests
|
|
tests/TestRunner.cc
|
|
tests/Test.h
|
|
tests/TestHarness.h
|
|
tests/test_daily_driver_harness.cc
|
|
tests/test_daily_workflows.cc
|
|
tests/test_buffer_io.cc
|
|
tests/test_buffer_rows.cc
|
|
tests/test_command_semantics.cc
|
|
tests/test_kkeymap.cc
|
|
tests/test_swap_recorder.cc
|
|
tests/test_swap_writer.cc
|
|
tests/test_swap_replay.cc
|
|
tests/test_swap_edge_cases.cc
|
|
tests/test_swap_recovery_prompt.cc
|
|
tests/test_swap_cleanup.cc
|
|
tests/test_swap_cleanup2.cc
|
|
tests/test_swap_git_editor.cc
|
|
tests/test_piece_table.cc
|
|
tests/test_search.cc
|
|
tests/test_search_replace_flow.cc
|
|
tests/test_reflow_paragraph.cc
|
|
tests/test_reflow_indented_bullets.cc
|
|
tests/test_undo.cc
|
|
tests/test_visual_line_mode.cc
|
|
tests/test_benchmarks.cc
|
|
tests/test_migration_coverage.cc
|
|
tests/test_smart_newline.cc
|
|
tests/test_reflow_undo.cc
|
|
|
|
# minimal engine sources required by Buffer
|
|
PieceTable.cc
|
|
Buffer.cc
|
|
Editor.cc
|
|
Command.cc
|
|
HelpText.cc
|
|
Swap.cc
|
|
ErrorHandler.cc
|
|
SyscallWrappers.cc
|
|
ErrorRecovery.cc
|
|
KKeymap.cc
|
|
SwapRecorder.h
|
|
OptimizedSearch.cc
|
|
UndoNode.cc
|
|
UndoTree.cc
|
|
UndoSystem.cc
|
|
${SYNTAX_SOURCES}
|
|
)
|
|
|
|
# Allow test-only introspection hooks (guarded in headers) without affecting production builds.
|
|
target_compile_definitions(kte_tests PRIVATE KTE_TESTS=1)
|
|
|
|
# Allow tests to include project headers like "Buffer.h"
|
|
target_include_directories(kte_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
|
|
|
# Keep tests free of ncurses/GUI deps
|
|
if (KTE_ENABLE_TREESITTER)
|
|
if (TREESITTER_INCLUDE_DIR)
|
|
target_include_directories(kte_tests PRIVATE ${TREESITTER_INCLUDE_DIR})
|
|
endif ()
|
|
if (TREESITTER_LIBRARY)
|
|
target_link_libraries(kte_tests ${TREESITTER_LIBRARY})
|
|
endif ()
|
|
endif ()
|
|
|
|
# Static linking on Linux only (macOS does not support static linking of system libraries)
|
|
if (NOT APPLE AND KTE_STATIC_LINK)
|
|
target_link_options(kte_tests PRIVATE -static)
|
|
endif ()
|
|
endif ()
|
|
|
|
if (BUILD_GUI)
|
|
# ImGui::CreateContext();
|
|
# ImGuiIO& io = ImGui::GetIO();
|
|
|
|
# // Set custom ini filename path to ~/.config/kte/imgui.ini
|
|
# if (const char* home = std::getenv("HOME")) {
|
|
# static std::string ini_path = std::string(home) + "/.config/kte/imgui.ini";
|
|
# io.IniFilename = ini_path.c_str();
|
|
# }
|
|
|
|
# io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
|
# io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
|
# Do not enable GUI in the terminal-first 'kte' binary; GUI is built as separate 'kge'.
|
|
# This avoids referencing GUI classes from kte and keeps dependencies minimal.
|
|
|
|
# kge (GUI-first) executable
|
|
add_executable(kge
|
|
main.cc
|
|
${COMMON_SOURCES}
|
|
${GUI_SOURCES}
|
|
${COMMON_HEADERS}
|
|
${GUI_HEADERS}
|
|
|
|
)
|
|
target_compile_definitions(kge PRIVATE KTE_BUILD_GUI=1 KTE_DEFAULT_GUI=1 KTE_FONT_SIZE=${KTE_FONT_SIZE})
|
|
if (KTE_USE_QT)
|
|
target_compile_definitions(kge PRIVATE KTE_USE_QT=1)
|
|
endif ()
|
|
if (KTE_UNDO_DEBUG)
|
|
target_compile_definitions(kge PRIVATE KTE_UNDO_DEBUG=1)
|
|
endif ()
|
|
if (KTE_USE_QT)
|
|
target_link_libraries(kge ${CURSES_LIBRARIES} Qt6::Widgets)
|
|
else ()
|
|
target_link_libraries(kge ${CURSES_LIBRARIES} imgui)
|
|
endif ()
|
|
|
|
# Static linking on Linux only (macOS does not support static linking of system libraries)
|
|
if (NOT APPLE AND KTE_STATIC_LINK)
|
|
target_link_options(kge PRIVATE -static)
|
|
endif ()
|
|
|
|
# On macOS, build kge as a proper .app bundle
|
|
if (APPLE)
|
|
# Define the icon file
|
|
set(MACOSX_BUNDLE_ICON_FILE kge.icns)
|
|
set(kge_ICON "${CMAKE_CURRENT_SOURCE_DIR}/${MACOSX_BUNDLE_ICON_FILE}")
|
|
|
|
# Add icon to the target sources and mark it as a resource
|
|
target_sources(kge PRIVATE ${kge_ICON})
|
|
set_source_files_properties(${kge_ICON} PROPERTIES
|
|
MACOSX_PACKAGE_LOCATION Resources)
|
|
|
|
# Configure Info.plist with version and identifiers
|
|
set(KGE_BUNDLE_ID "dev.wntrmute.kge")
|
|
configure_file(
|
|
${CMAKE_CURRENT_LIST_DIR}/cmake/Info.plist.in
|
|
${CMAKE_CURRENT_BINARY_DIR}/kge-Info.plist
|
|
@ONLY)
|
|
|
|
# Ensure proper macOS bundle properties and RPATH so our bundled
|
|
# frameworks are preferred over system/Homebrew ones.
|
|
set_target_properties(kge PROPERTIES
|
|
MACOSX_BUNDLE TRUE
|
|
MACOSX_BUNDLE_GUI_IDENTIFIER ${KGE_BUNDLE_ID}
|
|
MACOSX_BUNDLE_BUNDLE_NAME "kge"
|
|
MACOSX_BUNDLE_ICON_FILE ${MACOSX_BUNDLE_ICON_FILE}
|
|
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/kge-Info.plist"
|
|
# Prefer the app's bundled frameworks at runtime
|
|
INSTALL_RPATH "@executable_path/../Frameworks"
|
|
BUILD_WITH_INSTALL_RPATH TRUE
|
|
)
|
|
|
|
add_dependencies(kge kte)
|
|
add_custom_command(TARGET kge POST_BUILD
|
|
COMMAND ${CMAKE_COMMAND} -E copy
|
|
$<TARGET_FILE:kte>
|
|
$<TARGET_FILE_DIR:kge>/kte
|
|
COMMENT "Copying kte binary into kge.app bundle")
|
|
|
|
install(TARGETS kge
|
|
BUNDLE DESTINATION .
|
|
)
|
|
|
|
install(TARGETS kte
|
|
RUNTIME DESTINATION kge.app/Contents/MacOS
|
|
)
|
|
else ()
|
|
install(TARGETS kge
|
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
)
|
|
endif ()
|
|
# Install kge man page only when GUI is built
|
|
install(FILES docs/kge.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
|
|
install(FILES kge.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons)
|
|
|
|
# Optional post-build bundle fixup (can also be run from scripts).
|
|
# This provides a CMake target to run BundleUtilities' fixup_bundle on the
|
|
# built app, useful after macdeployqt to ensure non-Qt dylibs are internalized.
|
|
if (APPLE AND TARGET kge)
|
|
get_target_property(IS_BUNDLE kge MACOSX_BUNDLE)
|
|
if (IS_BUNDLE)
|
|
add_custom_target(kge_fixup_bundle ALL
|
|
COMMAND ${CMAKE_COMMAND}
|
|
-DAPP_BUNDLE=${CMAKE_CURRENT_BINARY_DIR}/$<TARGET_PROPERTY:kge,MACOSX_BUNDLE_BUNDLE_NAME>.app
|
|
-P ${CMAKE_CURRENT_LIST_DIR}/cmake/fix_bundle.cmake
|
|
COMMENT "Running fixup_bundle on kge.app to internalize non-Qt dylibs"
|
|
VERBATIM)
|
|
add_dependencies(kge_fixup_bundle kge)
|
|
endif ()
|
|
endif ()
|
|
endif ()
|