cmake_minimum_required(VERSION 3.15) project(kte) include(GNUInstallDirs) set(CMAKE_CXX_STANDARD 20) set(KTE_VERSION "1.5.3") # 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 OFF 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) # 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" "$<$:/O2>") else () add_compile_options( "-static" "-Wall" "-Wextra" "-Werror" "-pedantic" "-Wno-unused-function" "-Wno-unused-parameter" "$<$:-O2>" "$<$:-g>" ) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") add_compile_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) set(CURSES_NEED_WIDE) find_package(Curses REQUIRED) include_directories(${CURSES_INCLUDE_DIR}) 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 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/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/Syne.h fonts/Triplicate.h fonts/Unispace.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 ) 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}) 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/test_buffer_io.cc tests/test_piece_table.cc tests/test_search.cc # minimal engine sources required by Buffer PieceTable.cc Buffer.cc OptimizedSearch.cc UndoNode.cc UndoTree.cc UndoSystem.cc ${SYNTAX_SOURCES} ) # 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 () 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 () # 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 $ $/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) include(CMakeParseArguments) add_custom_target(kge_fixup_bundle ALL COMMAND ${CMAKE_COMMAND} -DAPP_BUNDLE=$ -P ${CMAKE_CURRENT_LIST_DIR}/cmake/fix_bundle.cmake BYPRODUCTS $/Contents/Frameworks COMMENT "Running fixup_bundle on kge.app to internalize non-Qt dylibs" VERBATIM) add_dependencies(kge_fixup_bundle kge) endif () endif ()