Files
2026-07-13 13:05:14 +08:00

128 lines
5.4 KiB
CMake

cmake_minimum_required(VERSION 3.16...3.27)
# Instead of building separate executables (one per snippet), we build a single
# "snippets" binary with a dispatcher. Each snippet's `main` is renamed to `<name>_main`
# at configure time, and a generated dispatcher routes `argv[1]` to the right function.
file(GLOB_RECURSE sources_list CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/all/*.cpp)
# Not complete examples (code fragments without main):
list(FILTER sources_list EXCLUDE REGEX .*/concepts/static/*)
list(FILTER sources_list EXCLUDE REGEX .*/migration/log_tick_enabled.*)
list(FILTER sources_list EXCLUDE REGEX .*/migration/transactional_transforms/*)
list(FILTER sources_list EXCLUDE REGEX .*/tutorials/custom-application-id.*)
list(FILTER sources_list EXCLUDE REGEX .*/tutorials/custom-recording-id.*)
list(FILTER sources_list EXCLUDE REGEX .*/tutorials/log-file.*)
list(FILTER sources_list EXCLUDE REGEX .*/tutorials/log_line.*)
list(FILTER sources_list EXCLUDE REGEX .*/tutorials/quick_start.*)
list(FILTER sources_list EXCLUDE REGEX .*/tutorials/timelines_example.*)
set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated_snippets")
file(MAKE_DIRECTORY "${GENERATED_DIR}")
set(ALL_GENERATED_SOURCES "")
set(FORWARD_DECLARATIONS "")
set(DISPATCH_ENTRIES "")
set(SNIPPET_NAMES "")
foreach(SOURCE_PATH ${sources_list})
get_filename_component(SOURCE_NAME ${SOURCE_PATH} NAME_WLE)
if(${SOURCE_NAME} STREQUAL "CMakeFiles")
continue()
endif()
# C++ identifier: replace dashes with underscores.
string(REPLACE "-" "_" FUNC_NAME "${SOURCE_NAME}")
# Read the original source and validate the entry point signature.
file(READ "${SOURCE_PATH}" SOURCE_CONTENT)
string(FIND "${SOURCE_CONTENT}" "int main(int argc, char* argv[])" _pos)
if(_pos EQUAL -1)
message(FATAL_ERROR
"Snippet '${SOURCE_PATH}' does not use the expected signature: "
"int main(int argc, char* argv[]). "
"All snippets must use this exact signature."
)
endif()
# Rename main -> <func_name>_main.
string(REPLACE
"int main(int argc, char* argv[])"
"int ${FUNC_NAME}_main(int argc, char* argv[])"
SOURCE_CONTENT "${SOURCE_CONTENT}"
)
# Make non-main helper functions `static` to avoid duplicate symbols when
# multiple snippets define functions with the same name (e.g. `run_main`, `truncated_radians`).
# If a new snippet adds a helper function that conflicts, the linker will error with
# "duplicate symbol" - add a new REPLACE line here to fix it.
string(REPLACE "arrow::Status run_main()" "static arrow::Status run_main()" SOURCE_CONTENT "${SOURCE_CONTENT}")
string(REPLACE "float truncated_radians(int deg)" "static float truncated_radians(int deg)" SOURCE_CONTENT "${SOURCE_CONTENT}")
# Prepend per-file fixups before the original source:
# - #define renames for user types that would otherwise cause ODR violations across snippets
# (macros don't expand in string literals, so component name strings are preserved).
# Add new defines here if a new snippet introduces a conflicting type name.
# - #line restores __FILE__ to the original path so that snippets using
# `fs::path(__FILE__).parent_path()` (e.g. encoded_image.cpp) find adjacent assets.
string(PREPEND SOURCE_CONTENT
"#define CustomPosition3D ${FUNC_NAME}_CustomPosition3D\n"
"#define CustomPoints3D ${FUNC_NAME}_CustomPoints3D\n"
"#define Confidence ${FUNC_NAME}_Confidence\n"
"#line 1 \"${SOURCE_PATH}\"\n"
)
# If the last statement before the closing `}` isn't a `return`, add `return 0;`.
# This handles cases where `return` only appears in nested blocks (e.g. `if (argc < 2) return 1;`)
# but the main function body falls off the end without returning.
string(REGEX MATCH "return [^;]*;[ \t\r\n]*}[ \t\r\n]*$" _ends_with_return "${SOURCE_CONTENT}")
if(NOT _ends_with_return)
string(REGEX REPLACE "}[ \t\r\n]*$" " return 0;\n}\n" SOURCE_CONTENT "${SOURCE_CONTENT}")
endif()
# Write the transformed source.
file(WRITE "${GENERATED_DIR}/${FUNC_NAME}.cpp" "${SOURCE_CONTENT}")
list(APPEND ALL_GENERATED_SOURCES "${GENERATED_DIR}/${FUNC_NAME}.cpp")
# Build dispatcher data.
# CLI name keeps original dashes.
string(APPEND FORWARD_DECLARATIONS "int ${FUNC_NAME}_main(int argc, char* argv[]);\n")
string(APPEND DISPATCH_ENTRIES " {\"${SOURCE_NAME}\", ${FUNC_NAME}_main},\n")
list(APPEND SNIPPET_NAMES "${SOURCE_NAME}")
endforeach()
# Sort snippet names for the usage message.
list(SORT SNIPPET_NAMES)
set(SNIPPET_LIST_LITERAL "")
foreach(_name ${SNIPPET_NAMES})
string(APPEND SNIPPET_LIST_LITERAL " \"${_name}\",\n")
endforeach()
# Generate the dispatcher from template, substituting @VARIABLES@.
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/snippet_dispatcher.cpp.in"
"${GENERATED_DIR}/snippet_dispatcher.cpp"
@ONLY
)
# Build the single executable.
add_executable(snippets
"${GENERATED_DIR}/snippet_dispatcher.cpp"
${ALL_GENERATED_SOURCES}
)
rerun_strict_warning_settings(snippets)
# Renamed mains lose the compiler's special `main` treatment that suppresses
# unused-parameter warnings for argc/argv.
if(MSVC)
target_compile_options(snippets PRIVATE /wd4100)
else()
target_compile_options(snippets PRIVATE -Wno-unused-parameter)
endif()
target_link_libraries(snippets PRIVATE rerun_sdk loguru::loguru rerun_arrow_target)