diff options
| author | Mark Rowe <mark@vector35.com> | 2025-07-01 22:56:05 -0700 |
|---|---|---|
| committer | Mark Rowe <mark@vector35.com> | 2025-07-30 17:05:10 -0700 |
| commit | 3e2ace5e8d127d7995a94f1b296cad9152cb7274 (patch) | |
| tree | 3fda150bff7944cc90323337818959467938be6d /cmake/PythonBindings.cmake | |
| parent | 42878c46ace7ee6d267d26ee87b4d4015b4e4b2b (diff) | |
Add dependency tracking to the Python code generation
This ensures that the Python source files are only generated and copied
into the output directory if inputs have changed, rather than being done
unconditionally.
Diffstat (limited to 'cmake/PythonBindings.cmake')
| -rw-r--r-- | cmake/PythonBindings.cmake | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/cmake/PythonBindings.cmake b/cmake/PythonBindings.cmake new file mode 100644 index 00000000..206d2a7e --- /dev/null +++ b/cmake/PythonBindings.cmake @@ -0,0 +1,88 @@ +# CMake function for generating Python bindings +# +# This function: +# 1. Creates a custom command to generate the core and enums Python modules +# 2. Creates a custom command to copy all files to the output directory +# 3. Creates a custom target that depends on all output files +# +# Usage: +# generate_python_bindings( +# TARGET_NAME <target_name> +# DISPLAY_NAME <display_name> +# GENERATOR_TARGET <generator_executable_target> +# HEADER_FILE <path_to_header> +# [TEMPLATE_FILE <path_to_template>] +# OUTPUT_DIRECTORY <output_directory> +# CORE_OUTPUT_FILE <core_output_file> +# ENUMS_OUTPUT_FILE <enums_output_file> +# [PYTHON_SOURCES <list_of_python_source_files>] +# ) +# +function(generate_python_bindings) + set(options) + set(oneValueArgs TARGET_NAME DISPLAY_NAME GENERATOR_TARGET HEADER_FILE TEMPLATE_FILE OUTPUT_DIRECTORY CORE_OUTPUT_FILE ENUMS_OUTPUT_FILE) + set(multiValueArgs PYTHON_SOURCES) + + cmake_parse_arguments(PARSE_ARGV 0 ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}") + + foreach(required_arg TARGET_NAME DISPLAY_NAME GENERATOR_TARGET HEADER_FILE OUTPUT_DIRECTORY CORE_OUTPUT_FILE ENUMS_OUTPUT_FILE) + if(NOT ARGS_${required_arg}) + message(FATAL_ERROR "${required_arg} is required") + endif() + endforeach() + + set(CORE_SOURCE_PATH ${PROJECT_SOURCE_DIR}/${ARGS_CORE_OUTPUT_FILE}) + set(ENUMS_SOURCE_PATH ${PROJECT_SOURCE_DIR}/${ARGS_ENUMS_OUTPUT_FILE}) + + set(GENERATOR_DEPENDS ${ARGS_HEADER_FILE} $<TARGET_FILE:${ARGS_GENERATOR_TARGET}>) + list(APPEND GENERATOR_DEPENDS ${ARGS_TEMPLATE_FILE}) + + add_custom_command( + OUTPUT ${CORE_SOURCE_PATH} ${ENUMS_SOURCE_PATH} + DEPENDS ${GENERATOR_DEPENDS} + COMMENT "Generating ${ARGS_DISPLAY_NAME} Python Sources" + COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_leaks=0 $<TARGET_FILE:${ARGS_GENERATOR_TARGET}> + ${ARGS_HEADER_FILE} + ${CORE_SOURCE_PATH} + ${ARGS_TEMPLATE_FILE} + ${ENUMS_SOURCE_PATH} + VERBATIM + ) + + set(PYTHON_OUTPUT_FILES) + foreach(SOURCE_FILE ${ARGS_PYTHON_SOURCES}) + cmake_path(RELATIVE_PATH SOURCE_FILE BASE_DIRECTORY ${PROJECT_SOURCE_DIR} OUTPUT_VARIABLE REL_PATH) + list(APPEND PYTHON_OUTPUT_FILES ${ARGS_OUTPUT_DIRECTORY}/${REL_PATH}) + endforeach() + + list(APPEND PYTHON_OUTPUT_FILES ${ARGS_OUTPUT_DIRECTORY}/${ARGS_CORE_OUTPUT_FILE}) + list(APPEND PYTHON_OUTPUT_FILES ${ARGS_OUTPUT_DIRECTORY}/${ARGS_ENUMS_OUTPUT_FILE}) + + set(COPY_DEPENDENCIES ${CORE_SOURCE_PATH} ${ENUMS_SOURCE_PATH}) + list(APPEND COPY_DEPENDENCIES ${ARGS_PYTHON_SOURCES}) + + # Generate a script to copy the generated Python files, preserving their directory structure. + file(GENERATE OUTPUT ${PROJECT_BINARY_DIR}/copy_python_sources.cmake + CONTENT " + foreach(PYTHON_SOURCE ${ARGS_PYTHON_SOURCES}) + cmake_path(RELATIVE_PATH PYTHON_SOURCE BASE_DIRECTORY ${PROJECT_SOURCE_DIR} OUTPUT_VARIABLE OUTPUT_SUBPATH) + cmake_path(REMOVE_FILENAME OUTPUT_SUBPATH) + file(COPY $\{PYTHON_SOURCE\} DESTINATION ${ARGS_OUTPUT_DIRECTORY}/$\{OUTPUT_SUBPATH\}) + endforeach() + " + ) + + add_custom_command( + OUTPUT ${PYTHON_OUTPUT_FILES} + DEPENDS ${COPY_DEPENDENCIES} + COMMENT "Copying ${ARGS_DISPLAY_NAME} Python Sources" + COMMAND ${CMAKE_COMMAND} -E make_directory ${ARGS_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -P ${PROJECT_BINARY_DIR}/copy_python_sources.cmake + COMMAND ${CMAKE_COMMAND} -E copy ${CORE_SOURCE_PATH} ${ARGS_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy ${ENUMS_SOURCE_PATH} ${ARGS_OUTPUT_DIRECTORY} + VERBATIM + ) + + # Create target that depends on all output files + add_custom_target(${ARGS_TARGET_NAME} ALL DEPENDS ${PYTHON_OUTPUT_FILES}) +endfunction() |
