Nimbin[12]?SDL & Graphics / sdl_runtime_compiler / CMakeLists.txt

sdl_runtime_compiler git · main

SDL3 game for running and compiling code at runtime

sdl3 c++ compiler dlopen cmake · first commit 2026-04-19 · last commit 2026-07-03 (3 months ago) · synced 3 days ago · upstream: git.ide3.de/hsnr/sdl-runtime-compiler

C++ 72.3% C 26.2%
git clone https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler.gitwget https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler/archive/sdl_runtime_compiler.tar.gz
CMakeLists.txt 12 KB · 245 lines raw
# Default build & run  (same as before, no profiler):
#   rm -rf build/; cmake -B build && cmake --build build && ./build/SDL3_RuntimeCompiler
#
# Debug + ASan, run under gdb:
#   rm -rf build/; cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_ASAN=ON -DENABLE_LSAN=OFF -B build && cmake --build build && LSAN_OPTIONS=detect_leaks=0 gdb ./build/SDL3_RuntimeCompiler
#
# Profiling build (RelWithDebInfo + in-engine FrameProfiler + perf-friendly symbols):
#   rm -rf build-prof/; cmake -B build-prof -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_PROFILER=ON && cmake --build build-prof -j && ./build-prof/SDL3_RuntimeCompiler
#
# Optimized shipping build (Release, no profiler, no asan, full -O3 + LTO):
#   rm -rf build-release/; cmake -B build-release -DCMAKE_BUILD_TYPE=Release && cmake --build build-release -j && ./build-release/SDL3_RuntimeCompiler
#
# Benchmarks (google-benchmark target):
#   cmake -S . -B build -DCMAKE_BUILD_TYPE=Release && cmake --build build --target bench
# ─────────────────────────────────────────────────────────────────────────────

cmake_minimum_required(VERSION 3.20)
project(SDL3_RuntimeCompiler LANGUAGES CXX C)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# ─────────────────────────────────────────────────────────────────────────────
# Options — all OFF by default so the plain `cmake -B build` build matches the
# pre-profiler behaviour.
# ─────────────────────────────────────────────────────────────────────────────
option(ENABLE_PROFILER "Enable in-engine FrameProfiler instrumentation"        OFF)
option(ENABLE_ASAN     "Enable AddressSanitizer (typically used with Debug)"   OFF)
option(ENABLE_LSAN     "Enable LeakSanitizer (typically combined with ASan)"   OFF)
option(ENABLE_LTO      "Enable link-time optimization (Release builds only)"   ON)

# ─────────────────────────────────────────────────────────────────────────────
# Dependencies
# ─────────────────────────────────────────────────────────────────────────────
find_package(SDL3 REQUIRED)
find_package(Freetype REQUIRED)
find_package(OpenGL REQUIRED)

# ─────────────────────────────────────────────────────────────────────────────
# Sources
# ─────────────────────────────────────────────────────────────────────────────
file(GLOB_RECURSE SOURCES "src/*.cpp")

# ─────────────────────────────────────────────────────────────────────────────
# Shared compile/link settings — applied to every target via a helper function.
# Keeping this in one place means bench and the main exe stay in sync.
# ─────────────────────────────────────────────────────────────────────────────
function(apply_common_settings target)
    target_include_directories(${target} PRIVATE
        src
        src/thirdparty/glad/include
        ${CMAKE_SOURCE_DIR}/external/cpputils/include
    )

    target_link_libraries(${target} PRIVATE
        SDL3::SDL3
        Freetype::Freetype
        OpenGL::GL
        m
        ${CMAKE_DL_LIBS}
        tree_sitter_runtime
        tree_sitter_cpp
    )

    # Profiler flag.
    if(ENABLE_PROFILER)
        target_compile_definitions(${target} PRIVATE FRAME_PROFILER_ENABLED)
    else()
        # The header checks FRAME_PROFILER_DISABLED — when set, every FP_ZONE
        # macro becomes ((void)0) and the compiler erases it entirely.
        target_compile_definitions(${target} PRIVATE FRAME_PROFILER_DISABLED)
    endif()

    # AddressSanitizer / LeakSanitizer.
    if(ENABLE_ASAN)
        target_compile_options(${target} PRIVATE -fsanitize=address -fno-omit-frame-pointer)
        target_link_options(${target}    PRIVATE -fsanitize=address)
    endif()
    if(ENABLE_LSAN)
        target_compile_options(${target} PRIVATE -fsanitize=leak)
        target_link_options(${target}    PRIVATE -fsanitize=leak)
    endif()

    # Extra optimization flags for Release builds.
    target_compile_options(${target} PRIVATE
        $<$<CONFIG:Release>:-O3>
        $<$<CONFIG:Release>:-DNDEBUG>
    )

    # Debug always wants frame pointers + symbols.
    target_compile_options(${target} PRIVATE
        $<$<CONFIG:Debug>:-g>
        $<$<CONFIG:Debug>:-fno-omit-frame-pointer>
    )

    # RelWithDebInfo: keep symbols, mild optimization, frame pointers for perf.
    target_compile_options(${target} PRIVATE
        $<$<CONFIG:RelWithDebInfo>:-fno-omit-frame-pointer>
    )
endfunction()

# ─────────────────────────────────────────────────────────────────────────────
# tree-sitter (C++ syntax highlighting) — vendored at configure time.
# Runtime (MIT) + C++ grammar (MIT). We compile the committed generated C
# sources directly; no tree-sitter CLI / generation step is involved.
# ─────────────────────────────────────────────────────────────────────────────
set(FETCHCONTENT_BASE_DIR "${CMAKE_SOURCE_DIR}/external")

include(FetchContent)

FetchContent_Declare(treesitter
    GIT_REPOSITORY https://github.com/tree-sitter/tree-sitter.git
    GIT_TAG        v0.26.9
    GIT_SHALLOW    TRUE
)
FetchContent_Declare(treesitter_cpp
    GIT_REPOSITORY https://github.com/tree-sitter/tree-sitter-cpp.git
    GIT_TAG        v0.23.4
    GIT_SHALLOW    TRUE
)

message(STATUS "Fetching tree-sitter... <<-- Do i need that ?")
FetchContent_MakeAvailable(treesitter)
message(STATUS "tree-sitter ready")

message(STATUS "Fetching tree-sitter-cpp...")
FetchContent_MakeAvailable(treesitter_cpp)
message(STATUS "tree-sitter-cpp ready")

# The tree-sitter runtime: compile the amalgamated lib.c.
add_library(tree_sitter_runtime STATIC
    ${treesitter_SOURCE_DIR}/lib/src/lib.c
)
target_include_directories(tree_sitter_runtime PUBLIC
    ${treesitter_SOURCE_DIR}/lib/include
)
target_include_directories(tree_sitter_runtime PRIVATE
    ${treesitter_SOURCE_DIR}/lib/src
)

# The C++ grammar: generated parser + external scanner.
add_library(tree_sitter_cpp STATIC
    ${treesitter_cpp_SOURCE_DIR}/src/parser.c
    ${treesitter_cpp_SOURCE_DIR}/src/scanner.c
)
target_include_directories(tree_sitter_cpp PRIVATE
    ${treesitter_cpp_SOURCE_DIR}/src
)
# Expose the function declaration tree_sitter_cpp() to our C++ code.
target_include_directories(tree_sitter_cpp PUBLIC
    ${treesitter_cpp_SOURCE_DIR}/bindings/c
)

# ─────────────────────────────────────────────────────────────────────────────
# Main executable
# ─────────────────────────────────────────────────────────────────────────────
add_executable(SDL3_RuntimeCompiler
    ${SOURCES}
    src/thirdparty/glad/src/glad.c
)

apply_common_settings(SDL3_RuntimeCompiler)

# Strict warnings only on our code — keep them off bench so google-benchmark
# macros don't drown us in noise.
target_compile_options(SDL3_RuntimeCompiler PRIVATE
    -Wall -Wextra -Wshadow -Wconversion -Wsign-conversion
    -Wnull-dereference -Wdouble-promotion
    -Wunused -Wunused-function -Wunused-variable -Wunused-parameter
    -Wunused-but-set-variable -Wunused-label -Wunused-value
    -ffunction-sections -fdata-sections
)

target_link_options(SDL3_RuntimeCompiler PRIVATE
    -Wl,--gc-sections
)

# Link-time optimization for Release builds (controlled by ENABLE_LTO).
if(ENABLE_LTO)
    include(CheckIPOSupported)
    check_ipo_supported(RESULT lto_supported OUTPUT lto_err)
    if(lto_supported)
        set_property(TARGET SDL3_RuntimeCompiler PROPERTY
            INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
    else()
        message(STATUS "LTO requested but not supported: ${lto_err}")
    endif()
endif()

# compile_commands.json symlink for editors / clangd.
add_custom_target(compile_commands_link ALL
    COMMAND ${CMAKE_COMMAND} -E create_symlink
        ${CMAKE_BINARY_DIR}/compile_commands.json
        ${CMAKE_SOURCE_DIR}/compile_commands.json
    DEPENDS SDL3_RuntimeCompiler
)


# ─────────────────────────────────────────────────────────────────────────────
# Benchmarks
# ─────────────────────────────────────────────────────────────────────────────
find_package(benchmark QUIET)

if(benchmark_FOUND)
    file(GLOB_RECURSE BENCH_SOURCES "benchmark/*.cpp")

    # Engine sources minus main.cpp — google-benchmark provides its own main.
    set(BENCH_ENGINE_SOURCES ${SOURCES})
    list(FILTER BENCH_ENGINE_SOURCES EXCLUDE REGEX ".*/main\\.cpp$")

    add_executable(bench
        ${BENCH_SOURCES}
        ${BENCH_ENGINE_SOURCES}
        src/thirdparty/glad/src/glad.c
    )

    apply_common_settings(bench)

    target_compile_options(bench PRIVATE
        -Wall -Wextra
        # Skip stricter warnings on bench — google-benchmark macros need the breathing room.
    )

    target_link_libraries(bench PRIVATE
        benchmark::benchmark
        benchmark::benchmark_main
    )
else()
    message(STATUS "google-benchmark not found — skipping `bench` target")
endif()

# ─────────────────────────────────────────────────────────────────────────────
# Config summary — printed once at configure time so it's obvious which build
# you got when you re-cmake later.
# ─────────────────────────────────────────────────────────────────────────────
message(STATUS "")
message(STATUS "── SDL3_RuntimeCompiler configuration ─────────────────────────")
message(STATUS "  Build type      : ${CMAKE_BUILD_TYPE}")
message(STATUS "  Profiler        : ${ENABLE_PROFILER}")
message(STATUS "  ASan / LSan     : ${ENABLE_ASAN} / ${ENABLE_LSAN}")
message(STATUS "  LTO (Release)   : ${ENABLE_LTO}")
message(STATUS "  Benchmark found : ${benchmark_FOUND}")
message(STATUS "───────────────────────────────────────────────────────────────")
message(STATUS "")