@@ -12,11 +12,6 @@ set(AUX_TARGETS_FOLDER "${BASE_TARGETS_FOLDER}/Aux")
1212
1313get_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-
2015if (BUILD_TEST)
2116 enable_testing ()
2217 add_compile_definitions (BUILD_TEST=1 )
@@ -91,6 +86,95 @@ function(exp_gather_target_runtime_dependencies_recurse)
9186 set (${arg_OUT_DEP_TARGET} ${result_dep_target} PARENT_SCOPE )
9287endfunction ()
9388
89+ # Every runtime file lands in the single shared per-sub-project Binaries directory. To keep the copies generator-agnostic
90+ # and race-free, each destination is owned by exactly one copy step rather than being re-copied by every consumer:
91+ # - files referenced through a target ($<TARGET_FILE:...>) get a per-file owner created in the consumer's scope, so the
92+ # generator expression resolves where the target is visible (imported third-party targets such as Qt are
93+ # directory-scoped and would be invisible in a global aggregate target). A first-party owner additionally waits on its
94+ # producing target; merging these into one target is impossible because a build tool such as MirrorTool consumes a dll
95+ # while another dll's producer transitively depends on the tool, which would close a build cycle;
96+ # - prebuilt third-party files given as plain paths, plus resources, carry no target and are batched (deduplicated) into
97+ # one per-sub-project assets target whose copies run sequentially (see exp_finalize_dist_assets).
98+ function (exp_add_runtime_dep_copy )
99+ set (options "" )
100+ set (singleValueArgs KEY SRC PRODUCER OUTPUT_TARGET)
101+ set (multiValueArgs "" )
102+ cmake_parse_arguments (arg "${options} " "${singleValueArgs} " "${multiValueArgs} " ${ARGN} )
103+
104+ string (MAKE_C_IDENTIFIER "${arg_KEY} " key_id)
105+ set (registry_property EXP_RUNTIME_DEP_COPY_${SUB_PROJECT_NAME} _${key_id} )
106+
107+ get_property (copy_target GLOBAL PROPERTY ${registry_property} )
108+ if (NOT copy_target)
109+ exp_get_runtime_output_dir (OUTPUT out_dir )
110+ set (copy_target ${SUB_PROJECT_NAME} .CopyDll.${key_id} )
111+ add_custom_target (
112+ ${copy_target}
113+ COMMAND ${CMAKE_COMMAND } -E make_directory ${out_dir}
114+ COMMAND ${CMAKE_COMMAND } -E copy_if_different ${arg_SRC} ${out_dir}
115+ )
116+ set_target_properties (${copy_target} PROPERTIES FOLDER ${AUX_TARGETS_FOLDER} )
117+ if (arg_PRODUCER)
118+ add_dependencies (${copy_target} ${arg_PRODUCER} )
119+ endif ()
120+ set_property (GLOBAL PROPERTY ${registry_property} ${copy_target} )
121+ endif ()
122+
123+ set (${arg_OUTPUT_TARGET} ${copy_target} PARENT_SCOPE )
124+ endfunction ()
125+
126+ function (exp_schedule_dist_assets_finalize )
127+ get_property (scheduled GLOBAL PROPERTY EXP_DIST_ASSETS_SCHEDULED_${SUB_PROJECT_NAME} )
128+ if (NOT scheduled)
129+ set_property (GLOBAL PROPERTY EXP_DIST_ASSETS_SCHEDULED_${SUB_PROJECT_NAME} TRUE )
130+ cmake_language (DEFER DIRECTORY ${CMAKE_SOURCE_DIR } CALL exp_finalize_dist_assets "${SUB_PROJECT_NAME} " )
131+ endif ()
132+ endfunction ()
133+
134+ function (exp_finalize_dist_assets sub_project )
135+ get_property (asset_files GLOBAL PROPERTY EXP_DIST_ASSET_FILES_${sub_project} )
136+ get_property (asset_resources GLOBAL PROPERTY EXP_DIST_ASSET_RESOURCES_${sub_project} )
137+ get_property (consumers GLOBAL PROPERTY EXP_DIST_ASSET_CONSUMERS_${sub_project} )
138+
139+ if (NOT asset_files AND NOT asset_resources)
140+ return ()
141+ endif ()
142+
143+ if (with_multi_config_generator)
144+ set (out_dir ${CMAKE_BINARY_DIR } /Dist/$<CONFIG >/${sub_project} /Binaries)
145+ else ()
146+ set (out_dir ${CMAKE_BINARY_DIR } /Dist/${sub_project} /Binaries)
147+ endif ()
148+
149+ set (copy_commands COMMAND ${CMAKE_COMMAND } -E make_directory ${out_dir} )
150+ if (asset_files)
151+ list (REMOVE_DUPLICATES asset_files)
152+ foreach (f ${asset_files} )
153+ list (APPEND copy_commands COMMAND ${CMAKE_COMMAND } -E copy_if_different ${f} ${out_dir} )
154+ endforeach ()
155+ endif ()
156+ if (asset_resources)
157+ list (REMOVE_DUPLICATES asset_resources)
158+ foreach (entry ${asset_resources} )
159+ string (REPLACE "->" ";" pair "${entry} " )
160+ list (GET pair 0 src)
161+ list (GET pair 1 dst)
162+ list (APPEND copy_commands COMMAND ${CMAKE_COMMAND } -E copy_if_different ${src} ${out_dir} /${dst} )
163+ endforeach ()
164+ endif ()
165+
166+ set (copy_target ${sub_project} .CopyDistAssets)
167+ add_custom_target (${copy_target} ${copy_commands} )
168+ set_target_properties (${copy_target} PROPERTIES FOLDER ${sub_project} /Aux)
169+
170+ if (consumers)
171+ list (REMOVE_DUPLICATES consumers)
172+ foreach (consumer ${consumers} )
173+ add_dependencies (${consumer} ${copy_target} )
174+ endforeach ()
175+ endif ()
176+ endfunction ()
177+
94178function (exp_process_runtime_dependencies )
95179 set (options NOT_INSTALL)
96180 set (singleValueArgs NAME)
@@ -110,29 +194,41 @@ function(exp_process_runtime_dependencies)
110194 OUT_DEP_TARGET dep_dep_targets
111195 )
112196 list (APPEND runtime_deps ${dep_target_runtime_deps} )
113- list (APPEND dep_targets ${dep_dep_targets} )
114197 endforeach ()
115198
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
126- )
199+ if (runtime_deps)
200+ list (REMOVE_DUPLICATES runtime_deps)
201+ endif ()
127202
128- add_dependencies (${arg_NAME} ${custom_target_name} )
129- foreach (t ${dep_targets} )
130- add_dependencies (${custom_target_name} ${t} )
131- endforeach ()
203+ foreach (r ${runtime_deps} )
204+ set (referenced "" )
205+ if ("${r} " MATCHES "^\\ $<TARGET_FILE :(.+)>$" )
206+ set (referenced ${CMAKE_MATCH_1} )
207+ endif ()
132208
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 "" )
209+ if (referenced AND TARGET ${referenced} )
210+ set (producer "" )
211+ get_target_property (referenced_imported ${referenced} IMPORTED )
212+ if (NOT referenced_imported)
213+ set (producer ${referenced} )
214+ endif ()
215+
216+ exp_add_runtime_dep_copy (
217+ KEY ${r}
218+ SRC ${r}
219+ PRODUCER ${producer}
220+ OUTPUT_TARGET copy_target
221+ )
222+ add_dependencies (${arg_NAME} ${copy_target} )
223+ else ()
224+ set_property (GLOBAL APPEND PROPERTY EXP_DIST_ASSET_FILES_${SUB_PROJECT_NAME} ${r} )
225+ endif ()
226+ endforeach ()
227+
228+ set_property (GLOBAL APPEND PROPERTY EXP_DIST_ASSET_CONSUMERS_${SUB_PROJECT_NAME} ${arg_NAME} )
229+ exp_schedule_dist_assets_finalize ()
230+
231+ if (NOT arg_NOT_INSTALL AND runtime_deps)
136232 install (
137233 FILES ${runtime_deps} DESTINATION ${SUB_PROJECT_NAME} /Binaries
138234 )
@@ -166,7 +262,7 @@ function(exp_add_resources_copy_command)
166262 OUTPUT_DST dst
167263 )
168264
169- list ( APPEND copy_commands COMMAND ${ CMAKE_COMMAND } -E copy_if_different ${src} $< TARGET_FILE_DIR : ${arg_NAME} >/ ${dst} )
265+ set_property ( GLOBAL APPEND PROPERTY EXP_DIST_ASSET_RESOURCES_ ${SUB_PROJECT_NAME} " ${src} -> ${dst} " )
170266
171267 cmake_path (SET dst_path NORMALIZE "${SUB_PROJECT_NAME} /Binaries/${dst} " )
172268 cmake_path (GET dst_path PARENT_PATH dst_dir )
@@ -175,14 +271,8 @@ function(exp_add_resources_copy_command)
175271 endif ()
176272 endforeach ()
177273
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} )
274+ set_property (GLOBAL APPEND PROPERTY EXP_DIST_ASSET_CONSUMERS_${SUB_PROJECT_NAME} ${arg_NAME} )
275+ exp_schedule_dist_assets_finalize ()
186276endfunction ()
187277
188278function (exp_gather_target_libs )
0 commit comments