SourceFileRelocator.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #pragma once
  9. #include <QHash>
  10. #include <AzCore/base.h>
  11. #include <AzCore/std/containers/vector.h>
  12. #include <AzCore/std/string/string.h>
  13. #include <utility>
  14. #include "AzCore/EBus/EBus.h"
  15. #include "AzCore/Interface/Interface.h"
  16. #include "AzToolsFramework/AssetDatabase/AssetDatabaseConnection.h"
  17. #include <utilities/PlatformConfiguration.h>
  18. #include "AssetDatabase/AssetDatabase.h"
  19. // This needs to be up here so it is declared before the hash, which needs to be declared before the first usage
  20. namespace AssetProcessor
  21. {
  22. struct FileUpdateTask
  23. {
  24. FileUpdateTask(AZStd::vector<AZStd::string> oldString, AZStd::vector<AZStd::string> newString, AZStd::string absPathFileToUpdate, bool isAssetIdReference, bool skipTask)
  25. : m_oldStrings(AZStd::move(oldString)),
  26. m_newStrings(AZStd::move(newString)),
  27. m_absPathFileToUpdate(AZStd::move(absPathFileToUpdate)),
  28. m_isAssetIdReference(isAssetIdReference),
  29. m_skipTask(skipTask)
  30. {
  31. }
  32. bool operator==(const FileUpdateTask& rhs) const
  33. {
  34. return m_isAssetIdReference == rhs.m_isAssetIdReference
  35. && m_absPathFileToUpdate == rhs.m_absPathFileToUpdate
  36. && m_oldStrings == rhs.m_oldStrings
  37. && m_newStrings == rhs.m_newStrings;
  38. }
  39. AZStd::vector<AZStd::string> m_oldStrings; // The old path or uuid strings to search for
  40. AZStd::vector<AZStd::string> m_newStrings; // The new path or uuid strings to replace
  41. AZStd::string m_absPathFileToUpdate;
  42. bool m_isAssetIdReference = false;
  43. bool m_succeeded = false;
  44. bool m_skipTask = false;
  45. };
  46. }
  47. namespace AZStd
  48. {
  49. template<>
  50. struct hash<AssetProcessor::FileUpdateTask>
  51. {
  52. size_t operator()(const AssetProcessor::FileUpdateTask& obj) const
  53. {
  54. size_t h = 0;
  55. hash_combine(h, obj.m_isAssetIdReference);
  56. hash_combine(h, obj.m_absPathFileToUpdate);
  57. hash_range(h, obj.m_oldStrings.begin(), obj.m_oldStrings.end());
  58. hash_range(h, obj.m_newStrings.begin(), obj.m_newStrings.end());
  59. return h;
  60. }
  61. };
  62. }
  63. namespace AssetProcessor
  64. {
  65. enum class SourceFileRelocationStatus
  66. {
  67. None,
  68. Failed,
  69. Succeeded
  70. };
  71. enum RelocationParameters
  72. {
  73. RelocationParameters_PreviewOnlyFlag = 1 << 0,
  74. RelocationParameters_RemoveEmptyFoldersFlag = 1 << 1,
  75. RelocationParameters_AllowDependencyBreakingFlag = 1 << 2,
  76. RelocationParameters_UpdateReferencesFlag = 1 << 3,
  77. RelocationParameters_ExcludeMetaDataFilesFlag = 1 << 4,
  78. RelocationParameters_AllowNonDatabaseFilesFlag = 1 << 5
  79. };
  80. static constexpr int SourceFileRelocationInvalidIndex = -1;
  81. struct SourceFileRelocationInfo
  82. {
  83. SourceFileRelocationInfo(
  84. AzToolsFramework::AssetDatabase::SourceDatabaseEntry sourceEntry,
  85. AZStd::unordered_map<int, AzToolsFramework::AssetDatabase::ProductDatabaseEntry> products,
  86. const AZStd::string& oldRelativePath,
  87. const ScanFolderInfo* scanFolder,
  88. bool isMetadataEnabledType)
  89. : m_sourceEntry(AZStd::move(sourceEntry))
  90. , m_products(AZStd::move(products))
  91. , m_oldRelativePath(oldRelativePath)
  92. , m_isMetadataEnabledType(isMetadataEnabledType)
  93. {
  94. AzFramework::StringFunc::Path::ConstructFull(
  95. scanFolder->ScanPath().toUtf8().constData(), m_oldRelativePath.c_str(), m_oldAbsolutePath, false);
  96. m_oldAbsolutePath = AssetUtilities::NormalizeFilePath(m_oldAbsolutePath.c_str()).toUtf8().constData();
  97. }
  98. SourceFileRelocationInfo(const AZStd::string& filePath, const ScanFolderInfo* scanFolder)
  99. {
  100. QString relFilePath;
  101. PlatformConfiguration::ConvertToRelativePath(filePath.c_str(), scanFolder, relFilePath);
  102. m_oldRelativePath = relFilePath.toUtf8().data();
  103. AzFramework::StringFunc::Path::ConstructFull(scanFolder->ScanPath().toUtf8().constData(), m_oldRelativePath.c_str(), m_oldAbsolutePath, false);
  104. m_oldAbsolutePath = AssetUtilities::NormalizeFilePath(m_oldAbsolutePath.c_str()).toUtf8().constData();
  105. }
  106. AzToolsFramework::AssetDatabase::SourceDatabaseEntry m_sourceEntry;
  107. AZStd::unordered_map<int, AzToolsFramework::AssetDatabase::ProductDatabaseEntry> m_products; // Key = product SubId
  108. AzToolsFramework::AssetDatabase::SourceFileDependencyEntryContainer m_sourceDependencyEntries;
  109. AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntryContainer m_productDependencyEntries;
  110. AZ::Uuid m_newUuid;
  111. AZStd::string m_oldRelativePath;
  112. AZStd::string m_newRelativePath;
  113. AZStd::string m_oldAbsolutePath;
  114. AZStd::string m_newAbsolutePath;
  115. bool m_hasPathDependencies = false;
  116. SourceFileRelocationStatus m_operationStatus = SourceFileRelocationStatus::None;
  117. // If >= 0, this is a metadata file. This is the index into the
  118. // PlatformConfiguration metadata list (use with GetMetaDataFileTypeAt)
  119. int m_metadataIndex = SourceFileRelocationInvalidIndex;
  120. bool m_isMetadataEnabledType = false; // Indicates if this file uses metadata relocation
  121. // This is a cached index of the SourceFile in the SourceFileRelocationContainer.
  122. // This is only used by the metadata file to determine the destination path if needed.
  123. int m_sourceFileIndex = AssetProcessor::SourceFileRelocationInvalidIndex;
  124. };
  125. using SourceFileRelocationContainer = AZStd::vector<SourceFileRelocationInfo>;
  126. using FileUpdateTasks = AZStd::unordered_set<FileUpdateTask>;
  127. struct MoveFailure
  128. {
  129. MoveFailure(AZStd::string reason, bool dependencyFailure)
  130. : m_reason(AZStd::move(reason)),
  131. m_dependencyFailure(dependencyFailure)
  132. {
  133. }
  134. AZStd::string m_reason;
  135. bool m_dependencyFailure{};
  136. };
  137. struct RelocationSuccess
  138. {
  139. RelocationSuccess() = default;
  140. RelocationSuccess(int moveSuccessCount, int moveFailureCount, int moveTotalCount, int updateSuccessCount, int updateFailureCount, int updateTotalCount, SourceFileRelocationContainer sourceFileRelocationInfos, FileUpdateTasks fileUpdateTasks)
  141. : m_moveSuccessCount(moveSuccessCount),
  142. m_moveFailureCount(moveFailureCount),
  143. m_moveTotalCount(moveTotalCount),
  144. m_updateSuccessCount(updateSuccessCount),
  145. m_updateFailureCount(updateFailureCount),
  146. m_updateTotalCount(updateTotalCount),
  147. m_relocationContainer(AZStd::move(sourceFileRelocationInfos)),
  148. m_updateTasks(AZStd::move(fileUpdateTasks))
  149. {
  150. }
  151. int m_moveSuccessCount{};
  152. int m_moveFailureCount{};
  153. int m_moveTotalCount{};
  154. int m_updateSuccessCount{};
  155. int m_updateFailureCount{};
  156. int m_updateTotalCount{};
  157. SourceFileRelocationContainer m_relocationContainer;
  158. FileUpdateTasks m_updateTasks;
  159. };
  160. class ISourceFileRelocation
  161. {
  162. public:
  163. AZ_RTTI(ISourceFileRelocation, "{FEDD188E-D5FF-4852-B945-F82F7CC1CA5F}");
  164. ISourceFileRelocation() = default;
  165. virtual ~ISourceFileRelocation() = default;
  166. //! Moves source files or renames a file. Source and destination can be absolute paths or scanfolder relative paths. Wildcards are supported for source.
  167. //! By default no changes are made to the disk. Set previewOnly to false to actually move files.
  168. //! If allowDependencyBreaking is false, the move will fail if moving any files will break existing dependencies. Set to true to ignore and move anyway.
  169. virtual AZ::Outcome<RelocationSuccess, MoveFailure> Move(const AZStd::string& source, const AZStd::string& destination, int flags = RelocationParameters_PreviewOnlyFlag | RelocationParameters_RemoveEmptyFoldersFlag) = 0;
  170. //! Deletes source files. Source can be an absolute path or a scanfolder relative path. Wildcards are supported.
  171. //! By default no changes are made to the disk. Set previewOnly to false to actually delete files.
  172. //! If allowDependencyBreaking is false, the delete will fail if deleting any file breaks existing dependencies. Set to true to ignore and delete anyway.
  173. virtual AZ::Outcome<RelocationSuccess, AZStd::string> Delete(const AZStd::string& source, int flags = RelocationParameters_PreviewOnlyFlag | RelocationParameters_RemoveEmptyFoldersFlag) = 0;
  174. //! Takes a relocation set and builds a string report to output the result of what files will change and what dependencies will break
  175. virtual AZStd::string BuildReport(const SourceFileRelocationContainer& relocationEntries, const FileUpdateTasks& updateTasks, bool isMove, bool updateReference) const = 0;
  176. //! Takes a relocation set and builds a string report to output the result of what files will change and what dependencies will break formatted for use in a dialog box
  177. virtual AZStd::string BuildChangeReport(const SourceFileRelocationContainer& relocationEntries, const FileUpdateTasks& updateTasks) const = 0;
  178. AZ_DISABLE_COPY_MOVE(ISourceFileRelocation);
  179. };
  180. class SourceFileRelocator
  181. : public ISourceFileRelocation
  182. {
  183. public:
  184. SourceFileRelocator(AZStd::shared_ptr<AzToolsFramework::AssetDatabase::AssetDatabaseConnection> stateData, PlatformConfiguration* platformConfiguration);
  185. ~SourceFileRelocator();
  186. static void MakePathRelative(const AZStd::string& parentPath, const AZStd::string& childPath, AZStd::string& parentRelative, AZStd::string& childRelative);
  187. static AZ::Outcome<AZStd::string, AZStd::string> HandleWildcard(AZStd::string_view absFile, AZStd::string_view absSearch, AZStd::string destination);
  188. static void FixDestinationMissingFilename(AZStd::string& destination, const AZStd::string& source);
  189. // Takes a relocation set, scanfolder, source, and destination and calculates the new file path of every file
  190. AZ::Outcome<void, AZStd::string> ComputeDestination(SourceFileRelocationContainer& relocationContainer, const ScanFolderInfo* sourceScanFolder, const AZStd::string& source, AZStd::string destination, const ScanFolderInfo*& destinationScanFolderOut) const;
  191. // Takes a QStringList of paths and populates sources with all the corresponding source database entries
  192. QHash<QString, int> GetSources(
  193. QStringList pathMatches,
  194. const ScanFolderInfo* scanFolderInfo,
  195. SourceFileRelocationContainer& sources,
  196. bool allowNonDatabaseFiles = false) const;
  197. // Takes a QStringList of paths and populates metadata files.
  198. void HandleMetaDataFiles(QStringList pathMatches, QHash<QString, int>& pathIndexMap, const ScanFolderInfo* scanFolderInfo, SourceFileRelocationContainer& metadataFiles, bool excludeMetaDataFiles) const;
  199. // Returns a map of SubId -> ProductEntry for all the products of a source
  200. AZStd::unordered_map<int, AzToolsFramework::AssetDatabase::ProductDatabaseEntry> GetProductMapForSource(AZ::s64 sourceId) const;
  201. bool GetFilesFromSourceControl(
  202. SourceFileRelocationContainer& sources,
  203. const ScanFolderInfo* scanFolderInfo,
  204. QString absolutePath,
  205. bool excludeMetaDataFiles = false,
  206. bool allowNonDatabaseFiles = false) const;
  207. // Populates a relocation set with all direct source and product dependency database entries for every file
  208. void PopulateDependencies(SourceFileRelocationContainer& relocationContainer) const;
  209. // Gets the scanfolder and relative path given an input of an absolute or relative path (wildcard paths not supported). Fails if the source path is not within a scanfolder or can't be made relative
  210. AZ::Outcome<void, AZStd::string> GetScanFolderAndRelativePath(const AZStd::string& normalizedSource, bool allowNonexistentPath, const ScanFolderInfo*& scanFolderInfo, AZStd::string& relativePath) const;
  211. // Given a path, populates a relocation set with all source files that match. Will fail if a scanfolder itself is selected or the source string matches files from multiple scanfolders
  212. AZ::Outcome<void, AZStd::string> GetSourcesByPath(const AZStd::string& normalizedSource, SourceFileRelocationContainer& sources, const ScanFolderInfo*& scanFolderInfoOut, bool excludeMetaDataFiles = false, bool allowNonDatabaseFiles = false) const;
  213. int DoSourceControlMoveFiles(AZStd::string normalizedSource, AZStd::string normalizedDestination, SourceFileRelocationContainer& relocationContainer, const ScanFolderInfo* sourceScanFolderInfo, const ScanFolderInfo* destinationScanFolderInfo, bool removeEmptyFolders) const;
  214. int DoSourceControlDeleteFiles(AZStd::string normalizedSource, SourceFileRelocationContainer& relocationContainer, const ScanFolderInfo* sourceScanFolderInfo, bool removeEmptyFolders) const;
  215. static bool UpdateFileReferences(const FileUpdateTask& updateTask);
  216. bool ComputeProductDependencyUpdatePaths(const SourceFileRelocationInfo& relocationInfo, const AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntry& productDependency, AZStd::vector<AZStd::string>& oldPaths, AZStd::vector<AZStd::string>& newPaths, AZStd::string& absPathFileToUpdate) const;
  217. FileUpdateTasks UpdateReferences(const SourceFileRelocationContainer& relocationContainer, bool useSourceControl) const;
  218. // ISourceFileRelocation implementation
  219. AZ::Outcome<RelocationSuccess, MoveFailure> Move(const AZStd::string& source, const AZStd::string& destination, int flags = RelocationParameters_PreviewOnlyFlag | RelocationParameters_RemoveEmptyFoldersFlag) override;
  220. AZ::Outcome<RelocationSuccess, AZStd::string> Delete(const AZStd::string& source, int flags = RelocationParameters_PreviewOnlyFlag | RelocationParameters_RemoveEmptyFoldersFlag) override;
  221. AZStd::string BuildReport(const SourceFileRelocationContainer& relocationEntries, const FileUpdateTasks& updateTasks, bool isMove, bool updateReference) const override;
  222. AZStd::string BuildChangeReport(const SourceFileRelocationContainer& relocationEntries, const FileUpdateTasks& updateTasks) const override;
  223. private:
  224. AZStd::shared_ptr<AzToolsFramework::AssetDatabase::AssetDatabaseConnection> m_stateData;
  225. PlatformConfiguration* m_platformConfig;
  226. AZStd::unordered_map<AZStd::string, AZStd::string> m_additionalHelpTextMap;
  227. };
  228. } // namespace AssetProcessor