diff --git a/CMakeLists.txt b/CMakeLists.txt index cfc441b66a..c4d53399f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -364,6 +364,29 @@ install(CODE "file(REMOVE \${CMAKE_INSTALL_PREFIX}/Core)" COMPONENT Shaders) if(AUTO_PLUGIN_DEPLOYMENT OR AIO_ZIP_TO_DIST) message("Preparing AIO package in ${AIO_DIR}") + function(append_copy_if_different _cmds _srcs _src_base _dst_base) + foreach(_src IN LISTS ${_srcs}) + file(RELATIVE_PATH _rel "${_src_base}" "${_src}") + set(_dst "${_dst_base}/${_rel}") + get_filename_component(_dst_dir "${_dst}" DIRECTORY) + list( + APPEND ${_cmds} + COMMAND + ${CMAKE_COMMAND} + -E + make_directory + "${_dst_dir}" + COMMAND + ${CMAKE_COMMAND} + -E + copy_if_different + "${_src}" + "${_dst}" + ) + endforeach() + set(${_cmds} ${${_cmds}} PARENT_SCOPE) + endfunction() + # Prepare AIO only when sources change. Gather package + feature files as # inputs so the prepare step runs only when something actually changed. file( @@ -381,6 +404,7 @@ if(AUTO_PLUGIN_DEPLOYMENT OR AIO_ZIP_TO_DIST) # timestamps for unchanged files and prevents downstream incremental # deploys from copying everything every build. set(_prepare_aio_cmds) + string(REPLACE ";" "\\;" _feature_paths_arg "${FEATURE_PATHS}") # Ensure SKSE/Plugins dir exists # Note: DLL and PDB are copied via POST_BUILD command to avoid race conditions @@ -392,6 +416,17 @@ if(AUTO_PLUGIN_DEPLOYMENT OR AIO_ZIP_TO_DIST) make_directory "${AIO_DIR}/SKSE/Plugins" ) + list( + APPEND _prepare_aio_cmds + COMMAND + ${CMAKE_COMMAND} + -DMODE=AIO + -DAIO_DIR="${AIO_DIR}" + -DSOURCE_DIR="${CMAKE_SOURCE_DIR}" + -DFEATURE_PATHS="${_feature_paths_arg}" + -P + "${CMAKE_SOURCE_DIR}/cmake/CleanupStaleEntries.cmake" + ) # Copy package files (exclude test files from production packages) file( @@ -400,25 +435,7 @@ if(AUTO_PLUGIN_DEPLOYMENT OR AIO_ZIP_TO_DIST) "${CMAKE_SOURCE_DIR}/package/*" ) list(FILTER _AIO_PACKAGE_SOURCE_FILES EXCLUDE REGEX "/Tests/") - foreach(_src IN LISTS _AIO_PACKAGE_SOURCE_FILES) - file(RELATIVE_PATH _rel "${CMAKE_SOURCE_DIR}/package" "${_src}") - set(_dst "${AIO_DIR}/${_rel}") - get_filename_component(_dst_dir "${_dst}" DIRECTORY) - list( - APPEND _prepare_aio_cmds - COMMAND - ${CMAKE_COMMAND} - -E - make_directory - "${_dst_dir}" - COMMAND - ${CMAKE_COMMAND} - -E - copy_if_different - "${_src}" - "${_dst}" - ) - endforeach() + append_copy_if_different(_prepare_aio_cmds _AIO_PACKAGE_SOURCE_FILES "${CMAKE_SOURCE_DIR}/package" "${AIO_DIR}") # Copy feature folders (only files, preserve existing files in AIO) foreach(_fpath IN LISTS FEATURE_PATHS) @@ -428,25 +445,7 @@ if(AUTO_PLUGIN_DEPLOYMENT OR AIO_ZIP_TO_DIST) LIST_DIRECTORIES FALSE "${_fpath}/*" ) - foreach(_src IN LISTS _feature_files) - file(RELATIVE_PATH _rel "${_fpath}" "${_src}") - set(_dst "${AIO_DIR}/${_rel}") - get_filename_component(_dst_dir "${_dst}" DIRECTORY) - list( - APPEND _prepare_aio_cmds - COMMAND - ${CMAKE_COMMAND} - -E - make_directory - "${_dst_dir}" - COMMAND - ${CMAKE_COMMAND} - -E - copy_if_different - "${_src}" - "${_dst}" - ) - endforeach() + append_copy_if_different(_prepare_aio_cmds _feature_files "${_fpath}" "${AIO_DIR}") endif() endforeach() @@ -472,7 +471,10 @@ if(AUTO_PLUGIN_DEPLOYMENT OR AIO_ZIP_TO_DIST) OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/prepare_aio.stamp ${_prepare_aio_cmds} - DEPENDS ${_AIO_PACKAGE_FILES} ${PROJECT_NAME} + DEPENDS + ${_AIO_PACKAGE_FILES} + ${PROJECT_NAME} + ${CMAKE_SOURCE_DIR}/cmake/CleanupStaleEntries.cmake ) add_custom_target( @@ -509,25 +511,34 @@ if(AUTO_PLUGIN_DEPLOYMENT OR AIO_ZIP_TO_DIST) # Exclude test files from production packages list(FILTER _package_shaders EXCLUDE REGEX "/Tests/") set(_shader_copy_cmds) - foreach(_src IN LISTS _package_shaders) - file(RELATIVE_PATH _rel "${CMAKE_SOURCE_DIR}/package/Shaders" "${_src}") - set(_dst "${AIO_DIR}/Shaders/${_rel}") - get_filename_component(_dst_dir "${_dst}" DIRECTORY) - list( - APPEND _shader_copy_cmds - COMMAND - ${CMAKE_COMMAND} - -E - make_directory - "${_dst_dir}" - COMMAND - ${CMAKE_COMMAND} - -E - copy_if_different - "${_src}" - "${_dst}" - ) + + set(_feature_shader_paths) + foreach(_fpath IN LISTS FEATURE_PATHS) + if(EXISTS "${_fpath}/Shaders") + list(APPEND _feature_shader_paths "${_fpath}/Shaders") + endif() endforeach() + string( + REPLACE ";" + "\\;" + _feature_shader_paths_arg + "${_feature_shader_paths}" + ) + + list( + APPEND _shader_copy_cmds + COMMAND + ${CMAKE_COMMAND} + -DMODE=SHADERS + -DAIO_DIR="${AIO_DIR}" + -DSOURCE_DIR="${CMAKE_SOURCE_DIR}" + -DFEATURE_SHADER_PATHS="${_feature_shader_paths_arg}" + -P + "${CMAKE_SOURCE_DIR}/cmake/CleanupStaleEntries.cmake" + ) + + append_copy_if_different(_shader_copy_cmds _package_shaders "${CMAKE_SOURCE_DIR}/package/Shaders" "${AIO_DIR}/Shaders") + # feature shader folders foreach(_fpath IN LISTS FEATURE_PATHS) if(EXISTS "${_fpath}/Shaders") @@ -537,28 +548,7 @@ if(AUTO_PLUGIN_DEPLOYMENT OR AIO_ZIP_TO_DIST) "${_fpath}/Shaders/*" ) list(FILTER _feat_shaders EXCLUDE REGEX "/Tests/") - get_filename_component(_feat_name "${_fpath}" NAME) - foreach(_src IN LISTS _feat_shaders) - file(RELATIVE_PATH _rel "${_fpath}/Shaders" "${_src}") - # Place feature shader files directly under AIO_DIR/Shaders to preserve expected include paths - # This matches the package shader layout and ensures includes like "TerrainShadows/..." resolve correctly - set(_dst "${AIO_DIR}/Shaders/${_rel}") - get_filename_component(_dst_dir "${_dst}" DIRECTORY) - list( - APPEND _shader_copy_cmds - COMMAND - ${CMAKE_COMMAND} - -E - make_directory - "${_dst_dir}" - COMMAND - ${CMAKE_COMMAND} - -E - copy_if_different - "${_src}" - "${_dst}" - ) - endforeach() + append_copy_if_different(_shader_copy_cmds _feat_shaders "${_fpath}/Shaders" "${AIO_DIR}/Shaders") endif() endforeach() @@ -568,7 +558,9 @@ if(AUTO_PLUGIN_DEPLOYMENT OR AIO_ZIP_TO_DIST) ${CMAKE_COMMAND} -E make_directory "${AIO_DIR}/Shaders" ${_shader_copy_cmds} COMMAND ${CMAKE_COMMAND} -E touch copy_shaders.stamp - DEPENDS ${HLSL_FILES} + DEPENDS + ${HLSL_FILES} + ${CMAKE_SOURCE_DIR}/cmake/CleanupStaleEntries.cmake COMMENT "Copying changed shaders into AIO/Shaders" ) diff --git a/cmake/CleanupStaleEntries.cmake b/cmake/CleanupStaleEntries.cmake new file mode 100644 index 0000000000..a6ba852eb7 --- /dev/null +++ b/cmake/CleanupStaleEntries.cmake @@ -0,0 +1,104 @@ +if(NOT DEFINED MODE) + message(FATAL_ERROR "CleanupStaleEntries.cmake requires MODE") +endif() + +if(NOT DEFINED AIO_DIR) + message(FATAL_ERROR "CleanupStaleEntries.cmake requires AIO_DIR") +endif() + +if(NOT DEFINED SOURCE_DIR) + message(FATAL_ERROR "CleanupStaleEntries.cmake requires SOURCE_DIR") +endif() + +if(MODE STREQUAL "AIO") + if(NOT DEFINED FEATURE_PATHS) + set(FEATURE_PATHS "") + endif() + + file(GLOB_RECURSE _current_aio_sources LIST_DIRECTORIES FALSE "${SOURCE_DIR}/package/*") + list(FILTER _current_aio_sources EXCLUDE REGEX "/Tests/") + + foreach(_fpath IN LISTS FEATURE_PATHS) + if(EXISTS "${_fpath}") + file(GLOB_RECURSE _tmp LIST_DIRECTORIES FALSE "${_fpath}/*") + list(FILTER _tmp EXCLUDE REGEX "/Tests/") + list(APPEND _current_aio_sources ${_tmp}) + endif() + endforeach() + + set(_current_aio_rels) + foreach(_src IN LISTS _current_aio_sources) + if(_src MATCHES "^${SOURCE_DIR}/package/") + file(RELATIVE_PATH _rel "${SOURCE_DIR}/package" "${_src}") + list(APPEND _current_aio_rels "${_rel}") + else() + foreach(_fpath IN LISTS FEATURE_PATHS) + string(FIND "${_src}" "${_fpath}" _is_feature_path) + if(NOT _is_feature_path EQUAL -1) + file(RELATIVE_PATH _rel "${_fpath}" "${_src}") + list(APPEND _current_aio_rels "${_rel}") + break() + endif() + endforeach() + endif() + endforeach() + list(REMOVE_DUPLICATES _current_aio_rels) + + file(GLOB_RECURSE _existing_aio_files LIST_DIRECTORIES FALSE "${AIO_DIR}/*") + set(_stale_aio_files) + foreach(_existing IN LISTS _existing_aio_files) + file(RELATIVE_PATH _rel "${AIO_DIR}" "${_existing}") + if(NOT _rel MATCHES "^SKSE/Plugins/") + list(FIND _current_aio_rels "${_rel}" _idx) + if(_idx EQUAL -1) + list(APPEND _stale_aio_files "${_existing}") + endif() + endif() + endforeach() + + foreach(_stale IN LISTS _stale_aio_files) + file(REMOVE "${_stale}") + endforeach() + +elseif(MODE STREQUAL "SHADERS") + if(NOT DEFINED FEATURE_SHADER_PATHS) + set(FEATURE_SHADER_PATHS "") + endif() + + file(GLOB_RECURSE _package_shaders LIST_DIRECTORIES FALSE "${SOURCE_DIR}/package/Shaders/*") + list(FILTER _package_shaders EXCLUDE REGEX "/Tests/") + + set(_current_shader_rels) + foreach(_src IN LISTS _package_shaders) + file(RELATIVE_PATH _rel "${SOURCE_DIR}/package/Shaders" "${_src}") + list(APPEND _current_shader_rels "${_rel}") + endforeach() + + foreach(_shader_path IN LISTS FEATURE_SHADER_PATHS) + if(EXISTS "${_shader_path}") + file(GLOB_RECURSE _feat_shaders LIST_DIRECTORIES FALSE "${_shader_path}/*") + list(FILTER _feat_shaders EXCLUDE REGEX "/Tests/") + foreach(_src IN LISTS _feat_shaders) + file(RELATIVE_PATH _rel "${_shader_path}" "${_src}") + list(APPEND _current_shader_rels "${_rel}") + endforeach() + endif() + endforeach() + list(REMOVE_DUPLICATES _current_shader_rels) + + file(GLOB_RECURSE _aio_shaders LIST_DIRECTORIES FALSE "${AIO_DIR}/Shaders/*") + set(_stale_shader_files) + foreach(_existing IN LISTS _aio_shaders) + file(RELATIVE_PATH _rel "${AIO_DIR}/Shaders" "${_existing}") + list(FIND _current_shader_rels "${_rel}" _idx) + if(_idx EQUAL -1) + list(APPEND _stale_shader_files "${_existing}") + endif() + endforeach() + + foreach(_stale IN LISTS _stale_shader_files) + file(REMOVE "${_stale}") + endforeach() +else() + message(FATAL_ERROR "CleanupStaleEntries.cmake MODE must be AIO or SHADERS") +endif()