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 | |
| 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.
| -rw-r--r-- | cmake/PythonBindings.cmake | 88 | ||||
| -rw-r--r-- | plugins/warp/api/python/CMakeLists.txt | 29 | ||||
| -rw-r--r-- | python/CMakeLists.txt | 29 | ||||
| -rw-r--r-- | view/kernelcache/api/python/CMakeLists.txt | 30 | ||||
| -rw-r--r-- | view/sharedcache/api/python/CMakeLists.txt | 30 |
5 files changed, 139 insertions, 67 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() diff --git a/plugins/warp/api/python/CMakeLists.txt b/plugins/warp/api/python/CMakeLists.txt index 98337285..7c616bc3 100644 --- a/plugins/warp/api/python/CMakeLists.txt +++ b/plugins/warp/api/python/CMakeLists.txt @@ -2,9 +2,11 @@ cmake_minimum_required(VERSION 3.15 FATAL_ERROR) project(warp-python-api) +include(${PROJECT_SOURCE_DIR}/../../../../cmake/PythonBindings.cmake) + file(GLOB PYTHON_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/*.py) list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/_warpcore.py) -list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/enums.py) +list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/warp_enums.py) add_executable(warp_generator ${PROJECT_SOURCE_DIR}/generator.cpp) @@ -33,18 +35,15 @@ if(WIN32) endif() endif() -add_custom_target(warp_generator_copy ALL - BYPRODUCTS ${PROJECT_SOURCE_DIR}/_warpcore.py ${PROJECT_SOURCE_DIR}/enums.py - DEPENDS ${PYTHON_SOURCES} ${PROJECT_SOURCE_DIR}/../warpcore.h $<TARGET_FILE:warp_generator> - COMMAND ${CMAKE_COMMAND} -E echo "Copying WARP Python Sources" - COMMAND ${CMAKE_COMMAND} -E make_directory ${PYTHON_OUTPUT_DIRECTORY} - COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_leaks=0 $<TARGET_FILE:warp_generator> - ${PROJECT_SOURCE_DIR}/../warpcore.h - ${PROJECT_SOURCE_DIR}/_warpcore.py - ${PROJECT_SOURCE_DIR}/_warpcore_template.py - ${PROJECT_SOURCE_DIR}/warp_enums.py - - COMMAND ${CMAKE_COMMAND} -E copy ${PYTHON_SOURCES} ${PYTHON_OUTPUT_DIRECTORY} - COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/_warpcore.py ${PYTHON_OUTPUT_DIRECTORY} - COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/warp_enums.py ${PYTHON_OUTPUT_DIRECTORY}) +generate_python_bindings( + TARGET_NAME warp_generator_copy + DISPLAY_NAME "WARP" + GENERATOR_TARGET warp_generator + HEADER_FILE ${PROJECT_SOURCE_DIR}/../warpcore.h + TEMPLATE_FILE ${PROJECT_SOURCE_DIR}/_warpcore_template.py + OUTPUT_DIRECTORY ${PYTHON_OUTPUT_DIRECTORY} + CORE_OUTPUT_FILE _warpcore.py + ENUMS_OUTPUT_FILE warp_enums.py + PYTHON_SOURCES ${PYTHON_SOURCES} +) diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 4e147d00..d8f733d0 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -31,25 +31,14 @@ if(BN_INTERNAL_BUILD) COMMAND ${CMAKE_COMMAND} -E copy ${BN_CORE_OUTPUT_DIR}/binaryninjacore.dll ${PROJECT_BINARY_DIR}/) endif() - # Generate script to copy python sources with the correct paths - file(GENERATE OUTPUT ${PROJECT_BINARY_DIR}/copy_python_sources.cmake - CONTENT " - foreach(PYTHON_SOURCE ${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 ${BN_RESOURCE_DIR}/python/binaryninja/$\{OUTPUT_SUBPATH\}) - endforeach() - " - ) - - add_custom_target(generator_copy ALL - BYPRODUCTS ${PROJECT_SOURCE_DIR}/_binaryninjacore.py ${PROJECT_SOURCE_DIR}/enums.py - DEPENDS ${PYTHON_SOURCES} ${PROJECT_SOURCE_DIR}/../binaryninjacore.h $<TARGET_FILE:generator> - COMMENT "Copying API Python sources" - COMMAND ${CMAKE_COMMAND} -E make_directory ${BN_RESOURCE_DIR}/python/binaryninja/ - COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_leaks=0 $<TARGET_FILE:generator> ${PROJECT_SOURCE_DIR}/../binaryninjacore.h ${PROJECT_SOURCE_DIR}/_binaryninjacore.py ${PROJECT_SOURCE_DIR}/enums.py - COMMAND ${CMAKE_COMMAND} -P ${PROJECT_BINARY_DIR}/copy_python_sources.cmake - COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/_binaryninjacore.py ${BN_RESOURCE_DIR}/python/binaryninja/ - COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/enums.py ${BN_RESOURCE_DIR}/python/binaryninja/ + generate_python_bindings( + TARGET_NAME generator_copy + DISPLAY_NAME "Binary Ninja" + GENERATOR_TARGET generator + HEADER_FILE ${PROJECT_SOURCE_DIR}/../binaryninjacore.h + OUTPUT_DIRECTORY ${BN_RESOURCE_DIR}/python/binaryninja + CORE_OUTPUT_FILE _binaryninjacore.py + ENUMS_OUTPUT_FILE enums.py + PYTHON_SOURCES ${PYTHON_SOURCES} ) endif() diff --git a/view/kernelcache/api/python/CMakeLists.txt b/view/kernelcache/api/python/CMakeLists.txt index 55dfa308..938bbf38 100644 --- a/view/kernelcache/api/python/CMakeLists.txt +++ b/view/kernelcache/api/python/CMakeLists.txt @@ -2,9 +2,11 @@ cmake_minimum_required(VERSION 3.15 FATAL_ERROR) project(kernelcache-python-api) +include(${PROJECT_SOURCE_DIR}/../../../../cmake/PythonBindings.cmake) + file(GLOB PYTHON_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/*.py) list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/_kernelcachecore.py) -list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/enums.py) +list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/kernelcache_enums.py) add_executable(kernelcache_generator ${PROJECT_SOURCE_DIR}/generator.cpp) @@ -33,18 +35,14 @@ if(WIN32) endif() endif() -add_custom_target(kernelcache_generator_copy ALL - BYPRODUCTS ${PROJECT_SOURCE_DIR}/_kernelcachecore.py ${PROJECT_SOURCE_DIR}/enums.py - DEPENDS ${PYTHON_SOURCES} ${PROJECT_SOURCE_DIR}/../kernelcachecore.h $<TARGET_FILE:kernelcache_generator> - COMMAND ${CMAKE_COMMAND} -E echo "Copying Kernel Cache Python Sources" - COMMAND ${CMAKE_COMMAND} -E make_directory ${PYTHON_OUTPUT_DIRECTORY} - COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_leaks=0 $<TARGET_FILE:kernelcache_generator> - ${PROJECT_SOURCE_DIR}/../kernelcachecore.h - ${PROJECT_SOURCE_DIR}/_kernelcachecore.py - ${PROJECT_SOURCE_DIR}/_kernelcachecore_template.py - ${PROJECT_SOURCE_DIR}/kernelcache_enums.py - - COMMAND ${CMAKE_COMMAND} -E copy ${PYTHON_SOURCES} ${PYTHON_OUTPUT_DIRECTORY} - COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/_kernelcachecore.py ${PYTHON_OUTPUT_DIRECTORY} - COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/kernelcache_enums.py ${PYTHON_OUTPUT_DIRECTORY}) - +generate_python_bindings( + TARGET_NAME kernelcache_generator_copy + DISPLAY_NAME "Kernel Cache" + GENERATOR_TARGET kernelcache_generator + HEADER_FILE ${PROJECT_SOURCE_DIR}/../kernelcachecore.h + TEMPLATE_FILE ${PROJECT_SOURCE_DIR}/_kernelcachecore_template.py + OUTPUT_DIRECTORY ${PYTHON_OUTPUT_DIRECTORY} + CORE_OUTPUT_FILE _kernelcachecore.py + ENUMS_OUTPUT_FILE kernelcache_enums.py + PYTHON_SOURCES ${PYTHON_SOURCES} +) diff --git a/view/sharedcache/api/python/CMakeLists.txt b/view/sharedcache/api/python/CMakeLists.txt index 8e091eea..b6f6f875 100644 --- a/view/sharedcache/api/python/CMakeLists.txt +++ b/view/sharedcache/api/python/CMakeLists.txt @@ -2,9 +2,11 @@ cmake_minimum_required(VERSION 3.15 FATAL_ERROR) project(sharedcache-python-api) +include(${PROJECT_SOURCE_DIR}/../../../../cmake/PythonBindings.cmake) + file(GLOB PYTHON_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/*.py) list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/_sharedcachecore.py) -list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/enums.py) +list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/sharedcache_enums.py) add_executable(sharedcache_generator ${PROJECT_SOURCE_DIR}/generator.cpp) @@ -33,18 +35,14 @@ if(WIN32) endif() endif() -add_custom_target(sharedcache_generator_copy ALL - BYPRODUCTS ${PROJECT_SOURCE_DIR}/_sharedcachecore.py ${PROJECT_SOURCE_DIR}/enums.py - DEPENDS ${PYTHON_SOURCES} ${PROJECT_SOURCE_DIR}/../sharedcachecore.h $<TARGET_FILE:sharedcache_generator> - COMMAND ${CMAKE_COMMAND} -E echo "Copying Shared Cache Python Sources" - COMMAND ${CMAKE_COMMAND} -E make_directory ${PYTHON_OUTPUT_DIRECTORY} - COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_leaks=0 $<TARGET_FILE:sharedcache_generator> - ${PROJECT_SOURCE_DIR}/../sharedcachecore.h - ${PROJECT_SOURCE_DIR}/_sharedcachecore.py - ${PROJECT_SOURCE_DIR}/_sharedcachecore_template.py - ${PROJECT_SOURCE_DIR}/sharedcache_enums.py - - COMMAND ${CMAKE_COMMAND} -E copy ${PYTHON_SOURCES} ${PYTHON_OUTPUT_DIRECTORY} - COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/_sharedcachecore.py ${PYTHON_OUTPUT_DIRECTORY} - COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/sharedcache_enums.py ${PYTHON_OUTPUT_DIRECTORY}) - +generate_python_bindings( + TARGET_NAME sharedcache_generator_copy + DISPLAY_NAME "Shared Cache" + GENERATOR_TARGET sharedcache_generator + HEADER_FILE ${PROJECT_SOURCE_DIR}/../sharedcachecore.h + TEMPLATE_FILE ${PROJECT_SOURCE_DIR}/_sharedcachecore_template.py + OUTPUT_DIRECTORY ${PYTHON_OUTPUT_DIRECTORY} + CORE_OUTPUT_FILE _sharedcachecore.py + ENUMS_OUTPUT_FILE sharedcache_enums.py + PYTHON_SOURCES ${PYTHON_SOURCES} +) |
