sdl_runtime_compiler git · main
SDL3 game for running and compiling code at runtime
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.gzCMakeLists.txt 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 "")