Skip to content

Commit 210f06d

Browse files
committed
fix: serialize dist copies via a ninja job pool to avoid copy races
Replaces the per-destination copy-target deduplication with a depth-1 JOB_POOL, restoring the original per-target copy structure while still preventing concurrent writes to the shared Binaries directory.
1 parent 6834b75 commit 210f06d

1 file changed

Lines changed: 34 additions & 66 deletions

File tree

‎CMake/Target.cmake‎

Lines changed: 34 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ set(AUX_TARGETS_FOLDER "${BASE_TARGETS_FOLDER}/Aux")
1212

1313
get_cmake_property(with_multi_config_generator GENERATOR_IS_MULTI_CONFIG)
1414

15+
# Runtime-dependency and resource copies all land in the shared per-sub-project Binaries directory, and many targets copy
16+
# an overlapping set of files there. Running those copies concurrently races on the same destination files (intermittent
17+
# "Error copying file" failures under Ninja), so funnel them through a depth-1 job pool to serialize their execution.
18+
set_property(GLOBAL PROPERTY JOB_POOLS exp_copy=1)
19+
1520
if (BUILD_TEST)
1621
enable_testing()
1722
add_compile_definitions(BUILD_TEST=1)
@@ -86,37 +91,6 @@ function(exp_gather_target_runtime_dependencies_recurse)
8691
set(${arg_OUT_DEP_TARGET} ${result_dep_target} PARENT_SCOPE)
8792
endfunction()
8893

89-
function(exp_add_shared_copy_target)
90-
set(options "")
91-
set(singleValueArgs KEY SRC DST MAKE_DIR OUTPUT_TARGET)
92-
set(multiValueArgs DEPENDS)
93-
cmake_parse_arguments(arg "${options}" "${singleValueArgs}" "${multiValueArgs}" ${ARGN})
94-
95-
string(MAKE_C_IDENTIFIER "${arg_KEY}" key_id)
96-
set(registry_property EXP_SHARED_COPY_TARGET_${SUB_PROJECT_NAME}_${key_id})
97-
98-
get_property(copy_target GLOBAL PROPERTY ${registry_property})
99-
if (NOT copy_target)
100-
set(copy_target ${SUB_PROJECT_NAME}.Copy.${key_id})
101-
if (DEFINED arg_MAKE_DIR)
102-
set(copy_commands COMMAND ${CMAKE_COMMAND} -E make_directory ${arg_MAKE_DIR})
103-
else ()
104-
set(copy_commands "")
105-
endif ()
106-
list(APPEND copy_commands COMMAND ${CMAKE_COMMAND} -E copy_if_different ${arg_SRC} ${arg_DST})
107-
108-
add_custom_target(${copy_target} ${copy_commands})
109-
set_target_properties(${copy_target} PROPERTIES FOLDER ${AUX_TARGETS_FOLDER})
110-
set_property(GLOBAL PROPERTY ${registry_property} ${copy_target})
111-
endif ()
112-
113-
if (arg_DEPENDS)
114-
add_dependencies(${copy_target} ${arg_DEPENDS})
115-
endif ()
116-
117-
set(${arg_OUTPUT_TARGET} ${copy_target} PARENT_SCOPE)
118-
endfunction()
119-
12094
function(exp_process_runtime_dependencies)
12195
set(options NOT_INSTALL)
12296
set(singleValueArgs NAME)
@@ -136,37 +110,29 @@ function(exp_process_runtime_dependencies)
136110
OUT_DEP_TARGET dep_dep_targets
137111
)
138112
list(APPEND runtime_deps ${dep_target_runtime_deps})
113+
list(APPEND dep_targets ${dep_dep_targets})
139114
endforeach ()
140115

141-
if (runtime_deps)
142-
list(REMOVE_DUPLICATES runtime_deps)
143-
endif ()
144-
145-
exp_get_runtime_output_dir(OUTPUT out_dir)
146-
foreach (r ${runtime_deps})
147-
set(copy_depends "")
148-
if ("${r}" MATCHES "^\\$<TARGET_FILE:(.+)>$")
149-
set(producer ${CMAKE_MATCH_1})
150-
if (TARGET ${producer})
151-
get_target_property(producer_imported ${producer} IMPORTED)
152-
if (NOT producer_imported)
153-
set(copy_depends ${producer})
154-
endif ()
155-
endif ()
156-
endif ()
157-
158-
exp_add_shared_copy_target(
159-
KEY ${r}
160-
SRC ${r}
161-
DST ${out_dir}
162-
MAKE_DIR ${out_dir}
163-
DEPENDS ${copy_depends}
164-
OUTPUT_TARGET copy_target
116+
set(copy_commands COMMAND ${CMAKE_COMMAND} -E make_directory $<TARGET_FILE_DIR:${arg_NAME}>)
117+
foreach(r ${runtime_deps})
118+
list(APPEND copy_commands COMMAND ${CMAKE_COMMAND} -E copy_if_different ${r} $<TARGET_FILE_DIR:${arg_NAME}>)
119+
endforeach()
120+
if (NOT "${copy_commands}" STREQUAL "")
121+
set(custom_target_name ${arg_NAME}.CopyRuntimeDeps)
122+
add_custom_target(
123+
${custom_target_name}
124+
${copy_commands}
125+
JOB_POOL exp_copy
165126
)
166-
add_dependencies(${arg_NAME} ${copy_target})
167-
endforeach ()
168127

169-
if (NOT arg_NOT_INSTALL AND runtime_deps)
128+
add_dependencies(${arg_NAME} ${custom_target_name})
129+
foreach (t ${dep_targets})
130+
add_dependencies(${custom_target_name} ${t})
131+
endforeach ()
132+
133+
set_target_properties(${custom_target_name} PROPERTIES FOLDER ${AUX_TARGETS_FOLDER})
134+
endif ()
135+
if (NOT arg_NOT_INSTALL AND NOT "${runtime_deps}" STREQUAL "")
170136
install(
171137
FILES ${runtime_deps} DESTINATION ${SUB_PROJECT_NAME}/Binaries
172138
)
@@ -193,28 +159,30 @@ function(exp_add_resources_copy_command)
193159
set(multiValueArgs RES)
194160
cmake_parse_arguments(arg "${options}" "${singleValueArgs}" "${multiValueArgs}" ${ARGN})
195161

196-
exp_get_runtime_output_dir(OUTPUT out_dir)
197162
foreach(r ${arg_RES})
198163
exp_expand_resource_path_expression(
199164
INPUT ${r}
200165
OUTPUT_SRC src
201166
OUTPUT_DST dst
202167
)
203168

204-
exp_add_shared_copy_target(
205-
KEY ${dst}
206-
SRC ${src}
207-
DST ${out_dir}/${dst}
208-
OUTPUT_TARGET copy_target
209-
)
210-
add_dependencies(${arg_NAME} ${copy_target})
169+
list(APPEND copy_commands COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} $<TARGET_FILE_DIR:${arg_NAME}>/${dst})
211170

212171
cmake_path(SET dst_path NORMALIZE "${SUB_PROJECT_NAME}/Binaries/${dst}")
213172
cmake_path(GET dst_path PARENT_PATH dst_dir)
214173
if (NOT arg_NOT_INSTALL)
215174
install(FILES ${src} DESTINATION ${dst_dir})
216175
endif ()
217176
endforeach()
177+
178+
set(copy_res_target_name ${arg_NAME}.CopyRes)
179+
add_custom_target(
180+
${copy_res_target_name}
181+
${copy_commands}
182+
JOB_POOL exp_copy
183+
)
184+
set_target_properties(${copy_res_target_name} PROPERTIES FOLDER ${AUX_TARGETS_FOLDER})
185+
add_dependencies(${arg_NAME} ${copy_res_target_name})
218186
endfunction()
219187

220188
function(exp_gather_target_libs)

0 commit comments

Comments
 (0)