128 lines
5.4 KiB
CMake
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)
|