summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Rowe <mark@vector35.com>2025-07-01 22:56:05 -0700
committerMark Rowe <mark@vector35.com>2025-07-30 17:05:10 -0700
commit3e2ace5e8d127d7995a94f1b296cad9152cb7274 (patch)
tree3fda150bff7944cc90323337818959467938be6d
parent42878c46ace7ee6d267d26ee87b4d4015b4e4b2b (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.cmake88
-rw-r--r--plugins/warp/api/python/CMakeLists.txt29
-rw-r--r--python/CMakeLists.txt29
-rw-r--r--view/kernelcache/api/python/CMakeLists.txt30
-rw-r--r--view/sharedcache/api/python/CMakeLists.txt30
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}
+)