cmake_minimum_required(VERSION 3.16) project(cst-agent C) set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) option(CST_BUILD_TUI "Build ncurses/PDCurses TUI" ON) option(CST_NATIVE_ARCH "Pass -march=native to sashidown" OFF) option(CST_BUNDLE_BUSYBOX "Install placeholder/scripts for busybox beside binary" ON) set(SD_DIR "${CMAKE_CURRENT_SOURCE_DIR}/vendor/sashidown") if(CST_NATIVE_ARCH) set(_SD_MAKE_EXTRA CST_NATIVE_ARCH=1) else() set(_SD_MAKE_EXTRA "") endif() # Separate archive for MinGW so Linux host builds keep libsashidown.a if(CMAKE_SYSTEM_NAME STREQUAL "Windows") set(SASHIDOWN_A "${SD_DIR}/libsashidown-mingw.a") set(_MINGW_AR x86_64-w64-mingw32-ar) add_custom_command( OUTPUT "${SASHIDOWN_A}" COMMAND ${CMAKE_MAKE_PROGRAM} -C "${SD_DIR}" clean COMMAND ${CMAKE_MAKE_PROGRAM} -C "${SD_DIR}" libsashidown.a CC=${CMAKE_C_COMPILER} AR=${_MINGW_AR} RANLIB=x86_64-w64-mingw32-ranlib COMMAND ${CMAKE_COMMAND} -E copy_if_different "${SD_DIR}/libsashidown.a" "${SASHIDOWN_A}" COMMAND ${CMAKE_MAKE_PROGRAM} -C "${SD_DIR}" clean COMMENT "Building libsashidown for MinGW" VERBATIM) else() set(SASHIDOWN_A "${SD_DIR}/libsashidown.a") add_custom_command( OUTPUT "${SASHIDOWN_A}" COMMAND ${CMAKE_MAKE_PROGRAM} -C "${SD_DIR}" libsashidown.a CC=${CMAKE_C_COMPILER} ${_SD_MAKE_EXTRA} COMMENT "Building libsashidown.a" VERBATIM) endif() add_custom_target(sashidown DEPENDS "${SASHIDOWN_A}") find_package(CURL REQUIRED) find_package(Threads REQUIRED) set(CST_SOURCES src/ctl_parse.c src/os_compat.c src/sandbox.c src/session.c src/tools/tools.c src/llm_openai.c src/agent_loop.c src/agent_store.c src/json_baseline.c src/creds.c src/mm_tool.c src/models.c src/main.c ) if(CST_BUILD_TUI) list(APPEND CST_SOURCES src/tui.c) else() list(APPEND CST_SOURCES src/tui_stub.c) endif() add_executable(cst-agent ${CST_SOURCES}) add_dependencies(cst-agent sashidown) target_include_directories(cst-agent PRIVATE include "${SD_DIR}/include" ) target_compile_definitions(cst-agent PRIVATE _GNU_SOURCE) if(NOT WIN32) target_compile_definitions(cst-agent PRIVATE _XOPEN_SOURCE_EXTENDED) endif() target_link_libraries(cst-agent PRIVATE "${SASHIDOWN_A}" CURL::libcurl Threads::Threads ) if(NOT WIN32) target_link_libraries(cst-agent PRIVATE m) endif() if(CST_BUILD_TUI) if(WIN32) # Prefer PDCurses (pdcurses / curses). Users may set PDCurses_ROOT. find_path(PDCURSES_INCLUDE_DIR curses.h HINTS ENV PDCurses_ROOT "${PDCurses_ROOT}" PATH_SUFFIXES include) find_library(PDCURSES_LIBRARY NAMES pdcurses pdcurses.dll curses HINTS ENV PDCurses_ROOT "${PDCurses_ROOT}" PATH_SUFFIXES lib) if(PDCURSES_INCLUDE_DIR AND PDCURSES_LIBRARY) target_include_directories(cst-agent PRIVATE ${PDCURSES_INCLUDE_DIR}) target_link_libraries(cst-agent PRIVATE ${PDCURSES_LIBRARY}) target_compile_definitions(cst-agent PRIVATE CST_USE_PDCURSES=1) message(STATUS "TUI: PDCurses (${PDCURSES_LIBRARY})") else() message(FATAL_ERROR "CST_BUILD_TUI=ON on Windows requires PDCurses (set PDCurses_ROOT)") endif() else() set(CURSES_NEED_WIDE TRUE) find_package(Curses) if(CURSES_FOUND) target_include_directories(cst-agent PRIVATE ${CURSES_INCLUDE_DIRS}) target_link_libraries(cst-agent PRIVATE ${CURSES_LIBRARIES}) else() # Fallback common on Linux distros target_link_libraries(cst-agent PRIVATE ncursesw) endif() target_compile_definitions(cst-agent PRIVATE CST_USE_NCURSES=1) endif() else() target_compile_definitions(cst-agent PRIVATE CST_HEADLESS_ONLY=1) endif() if(CMAKE_SYSTEM_NAME STREQUAL "Windows" OR WIN32) # curl Schannel + networking target_link_libraries(cst-agent PRIVATE ws2_32 crypt32 secur32 bcrypt advapi32) endif() # --- tests --- enable_testing() foreach(t test_ctl_frontier test_json_baseline test_replay_starvation test_mm_tool) if(t STREQUAL test_ctl_frontier) add_executable(${t} tests/test_ctl_frontier.c src/ctl_parse.c) elseif(t STREQUAL test_json_baseline) add_executable(${t} tests/test_json_baseline.c src/json_baseline.c) elseif(t STREQUAL test_replay_starvation) add_executable(${t} tests/test_replay_starvation.c src/ctl_parse.c src/json_baseline.c) else() add_executable(${t} tests/test_mm_tool.c src/mm_tool.c) endif() target_include_directories(${t} PRIVATE include) target_compile_definitions(${t} PRIVATE _GNU_SOURCE) add_test(NAME ${t} COMMAND ${t}) endforeach() # --- busybox bundle helpers --- if(CST_BUNDLE_BUSYBOX) install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/third_party/busybox/README.md" DESTINATION . OPTIONAL) if(WIN32 AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/third_party/busybox/busybox.exe") install(PROGRAMS "${CMAKE_CURRENT_SOURCE_DIR}/third_party/busybox/busybox.exe" DESTINATION .) endif() endif() install(TARGETS cst-agent RUNTIME DESTINATION .) message(STATUS "Compiler: ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER}") message(STATUS "CST_BUILD_TUI=${CST_BUILD_TUI}")