Skip to content

Commit 3e2ace5

Browse files
committed
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.
1 parent 42878c4 commit 3e2ace5

File tree

5 files changed

+139
-67
lines changed

5 files changed

+139
-67
lines changed

cmake/PythonBindings.cmake

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# CMake function for generating Python bindings
2+
#
3+
# This function:
4+
# 1. Creates a custom command to generate the core and enums Python modules
5+
# 2. Creates a custom command to copy all files to the output directory
6+
# 3. Creates a custom target that depends on all output files
7+
#
8+
# Usage:
9+
# generate_python_bindings(
10+
# TARGET_NAME <target_name>
11+
# DISPLAY_NAME <display_name>
12+
# GENERATOR_TARGET <generator_executable_target>
13+
# HEADER_FILE <path_to_header>
14+
# [TEMPLATE_FILE <path_to_template>]
15+
# OUTPUT_DIRECTORY <output_directory>
16+
# CORE_OUTPUT_FILE <core_output_file>
17+
# ENUMS_OUTPUT_FILE <enums_output_file>
18+
# [PYTHON_SOURCES <list_of_python_source_files>]
19+
# )
20+
#
21+
function(generate_python_bindings)
22+
set(options)
23+
set(oneValueArgs TARGET_NAME DISPLAY_NAME GENERATOR_TARGET HEADER_FILE TEMPLATE_FILE OUTPUT_DIRECTORY CORE_OUTPUT_FILE ENUMS_OUTPUT_FILE)
24+
set(multiValueArgs PYTHON_SOURCES)
25+
26+
cmake_parse_arguments(PARSE_ARGV 0 ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}")
27+
28+
foreach(required_arg TARGET_NAME DISPLAY_NAME GENERATOR_TARGET HEADER_FILE OUTPUT_DIRECTORY CORE_OUTPUT_FILE ENUMS_OUTPUT_FILE)
29+
if(NOT ARGS_${required_arg})
30+
message(FATAL_ERROR "${required_arg} is required")
31+
endif()
32+
endforeach()
33+
34+
set(CORE_SOURCE_PATH ${PROJECT_SOURCE_DIR}/${ARGS_CORE_OUTPUT_FILE})
35+
set(ENUMS_SOURCE_PATH ${PROJECT_SOURCE_DIR}/${ARGS_ENUMS_OUTPUT_FILE})
36+
37+
set(GENERATOR_DEPENDS ${ARGS_HEADER_FILE} $<TARGET_FILE:${ARGS_GENERATOR_TARGET}>)
38+
list(APPEND GENERATOR_DEPENDS ${ARGS_TEMPLATE_FILE})
39+
40+
add_custom_command(
41+
OUTPUT ${CORE_SOURCE_PATH} ${ENUMS_SOURCE_PATH}
42+
DEPENDS ${GENERATOR_DEPENDS}
43+
COMMENT "Generating ${ARGS_DISPLAY_NAME} Python Sources"
44+
COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_leaks=0 $<TARGET_FILE:${ARGS_GENERATOR_TARGET}>
45+
${ARGS_HEADER_FILE}
46+
${CORE_SOURCE_PATH}
47+
${ARGS_TEMPLATE_FILE}
48+
${ENUMS_SOURCE_PATH}
49+
VERBATIM
50+
)
51+
52+
set(PYTHON_OUTPUT_FILES)
53+
foreach(SOURCE_FILE ${ARGS_PYTHON_SOURCES})
54+
cmake_path(RELATIVE_PATH SOURCE_FILE BASE_DIRECTORY ${PROJECT_SOURCE_DIR} OUTPUT_VARIABLE REL_PATH)
55+
list(APPEND PYTHON_OUTPUT_FILES ${ARGS_OUTPUT_DIRECTORY}/${REL_PATH})
56+
endforeach()
57+
58+
list(APPEND PYTHON_OUTPUT_FILES ${ARGS_OUTPUT_DIRECTORY}/${ARGS_CORE_OUTPUT_FILE})
59+
list(APPEND PYTHON_OUTPUT_FILES ${ARGS_OUTPUT_DIRECTORY}/${ARGS_ENUMS_OUTPUT_FILE})
60+
61+
set(COPY_DEPENDENCIES ${CORE_SOURCE_PATH} ${ENUMS_SOURCE_PATH})
62+
list(APPEND COPY_DEPENDENCIES ${ARGS_PYTHON_SOURCES})
63+
64+
# Generate a script to copy the generated Python files, preserving their directory structure.
65+
file(GENERATE OUTPUT ${PROJECT_BINARY_DIR}/copy_python_sources.cmake
66+
CONTENT "
67+
foreach(PYTHON_SOURCE ${ARGS_PYTHON_SOURCES})
68+
cmake_path(RELATIVE_PATH PYTHON_SOURCE BASE_DIRECTORY ${PROJECT_SOURCE_DIR} OUTPUT_VARIABLE OUTPUT_SUBPATH)
69+
cmake_path(REMOVE_FILENAME OUTPUT_SUBPATH)
70+
file(COPY $\{PYTHON_SOURCE\} DESTINATION ${ARGS_OUTPUT_DIRECTORY}/$\{OUTPUT_SUBPATH\})
71+
endforeach()
72+
"
73+
)
74+
75+
add_custom_command(
76+
OUTPUT ${PYTHON_OUTPUT_FILES}
77+
DEPENDS ${COPY_DEPENDENCIES}
78+
COMMENT "Copying ${ARGS_DISPLAY_NAME} Python Sources"
79+
COMMAND ${CMAKE_COMMAND} -E make_directory ${ARGS_OUTPUT_DIRECTORY}
80+
COMMAND ${CMAKE_COMMAND} -P ${PROJECT_BINARY_DIR}/copy_python_sources.cmake
81+
COMMAND ${CMAKE_COMMAND} -E copy ${CORE_SOURCE_PATH} ${ARGS_OUTPUT_DIRECTORY}
82+
COMMAND ${CMAKE_COMMAND} -E copy ${ENUMS_SOURCE_PATH} ${ARGS_OUTPUT_DIRECTORY}
83+
VERBATIM
84+
)
85+
86+
# Create target that depends on all output files
87+
add_custom_target(${ARGS_TARGET_NAME} ALL DEPENDS ${PYTHON_OUTPUT_FILES})
88+
endfunction()

plugins/warp/api/python/CMakeLists.txt

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ cmake_minimum_required(VERSION 3.15 FATAL_ERROR)
22

33
project(warp-python-api)
44

5+
include(${PROJECT_SOURCE_DIR}/../../../../cmake/PythonBindings.cmake)
6+
57
file(GLOB PYTHON_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/*.py)
68
list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/_warpcore.py)
7-
list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/enums.py)
9+
list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/warp_enums.py)
810

911
add_executable(warp_generator
1012
${PROJECT_SOURCE_DIR}/generator.cpp)
@@ -33,18 +35,15 @@ if(WIN32)
3335
endif()
3436
endif()
3537

36-
add_custom_target(warp_generator_copy ALL
37-
BYPRODUCTS ${PROJECT_SOURCE_DIR}/_warpcore.py ${PROJECT_SOURCE_DIR}/enums.py
38-
DEPENDS ${PYTHON_SOURCES} ${PROJECT_SOURCE_DIR}/../warpcore.h $<TARGET_FILE:warp_generator>
39-
COMMAND ${CMAKE_COMMAND} -E echo "Copying WARP Python Sources"
40-
COMMAND ${CMAKE_COMMAND} -E make_directory ${PYTHON_OUTPUT_DIRECTORY}
41-
COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_leaks=0 $<TARGET_FILE:warp_generator>
42-
${PROJECT_SOURCE_DIR}/../warpcore.h
43-
${PROJECT_SOURCE_DIR}/_warpcore.py
44-
${PROJECT_SOURCE_DIR}/_warpcore_template.py
45-
${PROJECT_SOURCE_DIR}/warp_enums.py
46-
47-
COMMAND ${CMAKE_COMMAND} -E copy ${PYTHON_SOURCES} ${PYTHON_OUTPUT_DIRECTORY}
48-
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/_warpcore.py ${PYTHON_OUTPUT_DIRECTORY}
49-
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/warp_enums.py ${PYTHON_OUTPUT_DIRECTORY})
38+
generate_python_bindings(
39+
TARGET_NAME warp_generator_copy
40+
DISPLAY_NAME "WARP"
41+
GENERATOR_TARGET warp_generator
42+
HEADER_FILE ${PROJECT_SOURCE_DIR}/../warpcore.h
43+
TEMPLATE_FILE ${PROJECT_SOURCE_DIR}/_warpcore_template.py
44+
OUTPUT_DIRECTORY ${PYTHON_OUTPUT_DIRECTORY}
45+
CORE_OUTPUT_FILE _warpcore.py
46+
ENUMS_OUTPUT_FILE warp_enums.py
47+
PYTHON_SOURCES ${PYTHON_SOURCES}
48+
)
5049

python/CMakeLists.txt

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,14 @@ if(BN_INTERNAL_BUILD)
3131
COMMAND ${CMAKE_COMMAND} -E copy ${BN_CORE_OUTPUT_DIR}/binaryninjacore.dll ${PROJECT_BINARY_DIR}/)
3232
endif()
3333

34-
# Generate script to copy python sources with the correct paths
35-
file(GENERATE OUTPUT ${PROJECT_BINARY_DIR}/copy_python_sources.cmake
36-
CONTENT "
37-
foreach(PYTHON_SOURCE ${PYTHON_SOURCES})
38-
cmake_path(RELATIVE_PATH PYTHON_SOURCE BASE_DIRECTORY ${PROJECT_SOURCE_DIR} OUTPUT_VARIABLE OUTPUT_SUBPATH)
39-
cmake_path(REMOVE_FILENAME OUTPUT_SUBPATH)
40-
file(COPY $\{PYTHON_SOURCE\} DESTINATION ${BN_RESOURCE_DIR}/python/binaryninja/$\{OUTPUT_SUBPATH\})
41-
endforeach()
42-
"
43-
)
44-
45-
add_custom_target(generator_copy ALL
46-
BYPRODUCTS ${PROJECT_SOURCE_DIR}/_binaryninjacore.py ${PROJECT_SOURCE_DIR}/enums.py
47-
DEPENDS ${PYTHON_SOURCES} ${PROJECT_SOURCE_DIR}/../binaryninjacore.h $<TARGET_FILE:generator>
48-
COMMENT "Copying API Python sources"
49-
COMMAND ${CMAKE_COMMAND} -E make_directory ${BN_RESOURCE_DIR}/python/binaryninja/
50-
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
51-
COMMAND ${CMAKE_COMMAND} -P ${PROJECT_BINARY_DIR}/copy_python_sources.cmake
52-
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/_binaryninjacore.py ${BN_RESOURCE_DIR}/python/binaryninja/
53-
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/enums.py ${BN_RESOURCE_DIR}/python/binaryninja/
34+
generate_python_bindings(
35+
TARGET_NAME generator_copy
36+
DISPLAY_NAME "Binary Ninja"
37+
GENERATOR_TARGET generator
38+
HEADER_FILE ${PROJECT_SOURCE_DIR}/../binaryninjacore.h
39+
OUTPUT_DIRECTORY ${BN_RESOURCE_DIR}/python/binaryninja
40+
CORE_OUTPUT_FILE _binaryninjacore.py
41+
ENUMS_OUTPUT_FILE enums.py
42+
PYTHON_SOURCES ${PYTHON_SOURCES}
5443
)
5544
endif()

view/kernelcache/api/python/CMakeLists.txt

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ cmake_minimum_required(VERSION 3.15 FATAL_ERROR)
22

33
project(kernelcache-python-api)
44

5+
include(${PROJECT_SOURCE_DIR}/../../../../cmake/PythonBindings.cmake)
6+
57
file(GLOB PYTHON_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/*.py)
68
list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/_kernelcachecore.py)
7-
list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/enums.py)
9+
list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/kernelcache_enums.py)
810

911
add_executable(kernelcache_generator
1012
${PROJECT_SOURCE_DIR}/generator.cpp)
@@ -33,18 +35,14 @@ if(WIN32)
3335
endif()
3436
endif()
3537

36-
add_custom_target(kernelcache_generator_copy ALL
37-
BYPRODUCTS ${PROJECT_SOURCE_DIR}/_kernelcachecore.py ${PROJECT_SOURCE_DIR}/enums.py
38-
DEPENDS ${PYTHON_SOURCES} ${PROJECT_SOURCE_DIR}/../kernelcachecore.h $<TARGET_FILE:kernelcache_generator>
39-
COMMAND ${CMAKE_COMMAND} -E echo "Copying Kernel Cache Python Sources"
40-
COMMAND ${CMAKE_COMMAND} -E make_directory ${PYTHON_OUTPUT_DIRECTORY}
41-
COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_leaks=0 $<TARGET_FILE:kernelcache_generator>
42-
${PROJECT_SOURCE_DIR}/../kernelcachecore.h
43-
${PROJECT_SOURCE_DIR}/_kernelcachecore.py
44-
${PROJECT_SOURCE_DIR}/_kernelcachecore_template.py
45-
${PROJECT_SOURCE_DIR}/kernelcache_enums.py
46-
47-
COMMAND ${CMAKE_COMMAND} -E copy ${PYTHON_SOURCES} ${PYTHON_OUTPUT_DIRECTORY}
48-
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/_kernelcachecore.py ${PYTHON_OUTPUT_DIRECTORY}
49-
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/kernelcache_enums.py ${PYTHON_OUTPUT_DIRECTORY})
50-
38+
generate_python_bindings(
39+
TARGET_NAME kernelcache_generator_copy
40+
DISPLAY_NAME "Kernel Cache"
41+
GENERATOR_TARGET kernelcache_generator
42+
HEADER_FILE ${PROJECT_SOURCE_DIR}/../kernelcachecore.h
43+
TEMPLATE_FILE ${PROJECT_SOURCE_DIR}/_kernelcachecore_template.py
44+
OUTPUT_DIRECTORY ${PYTHON_OUTPUT_DIRECTORY}
45+
CORE_OUTPUT_FILE _kernelcachecore.py
46+
ENUMS_OUTPUT_FILE kernelcache_enums.py
47+
PYTHON_SOURCES ${PYTHON_SOURCES}
48+
)

view/sharedcache/api/python/CMakeLists.txt

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ cmake_minimum_required(VERSION 3.15 FATAL_ERROR)
22

33
project(sharedcache-python-api)
44

5+
include(${PROJECT_SOURCE_DIR}/../../../../cmake/PythonBindings.cmake)
6+
57
file(GLOB PYTHON_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/*.py)
68
list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/_sharedcachecore.py)
7-
list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/enums.py)
9+
list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/sharedcache_enums.py)
810

911
add_executable(sharedcache_generator
1012
${PROJECT_SOURCE_DIR}/generator.cpp)
@@ -33,18 +35,14 @@ if(WIN32)
3335
endif()
3436
endif()
3537

36-
add_custom_target(sharedcache_generator_copy ALL
37-
BYPRODUCTS ${PROJECT_SOURCE_DIR}/_sharedcachecore.py ${PROJECT_SOURCE_DIR}/enums.py
38-
DEPENDS ${PYTHON_SOURCES} ${PROJECT_SOURCE_DIR}/../sharedcachecore.h $<TARGET_FILE:sharedcache_generator>
39-
COMMAND ${CMAKE_COMMAND} -E echo "Copying Shared Cache Python Sources"
40-
COMMAND ${CMAKE_COMMAND} -E make_directory ${PYTHON_OUTPUT_DIRECTORY}
41-
COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_leaks=0 $<TARGET_FILE:sharedcache_generator>
42-
${PROJECT_SOURCE_DIR}/../sharedcachecore.h
43-
${PROJECT_SOURCE_DIR}/_sharedcachecore.py
44-
${PROJECT_SOURCE_DIR}/_sharedcachecore_template.py
45-
${PROJECT_SOURCE_DIR}/sharedcache_enums.py
46-
47-
COMMAND ${CMAKE_COMMAND} -E copy ${PYTHON_SOURCES} ${PYTHON_OUTPUT_DIRECTORY}
48-
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/_sharedcachecore.py ${PYTHON_OUTPUT_DIRECTORY}
49-
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/sharedcache_enums.py ${PYTHON_OUTPUT_DIRECTORY})
50-
38+
generate_python_bindings(
39+
TARGET_NAME sharedcache_generator_copy
40+
DISPLAY_NAME "Shared Cache"
41+
GENERATOR_TARGET sharedcache_generator
42+
HEADER_FILE ${PROJECT_SOURCE_DIR}/../sharedcachecore.h
43+
TEMPLATE_FILE ${PROJECT_SOURCE_DIR}/_sharedcachecore_template.py
44+
OUTPUT_DIRECTORY ${PYTHON_OUTPUT_DIRECTORY}
45+
CORE_OUTPUT_FILE _sharedcachecore.py
46+
ENUMS_OUTPUT_FILE sharedcache_enums.py
47+
PYTHON_SOURCES ${PYTHON_SOURCES}
48+
)

0 commit comments

Comments
 (0)