assetUtils.h 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  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 <AzCore/PlatformIncl.h>
  10. #include <cstdlib> // for size_t
  11. #include <QString>
  12. #include <AssetBuilderSDK/AssetBuilderSDK.h>
  13. #include <AssetBuilderSDK/AssetBuilderBusses.h>
  14. #include <AzCore/std/parallel/atomic.h>
  15. #include <AzFramework/Logging/LogFile.h>
  16. #include <AzCore/Debug/TraceMessageBus.h>
  17. #include "native/assetprocessor.h"
  18. #include "native/utilities/AssetUtilEBusHelper.h"
  19. #include "native/utilities/ApplicationManagerAPI.h"
  20. #include <AzToolsFramework/Asset/AssetProcessorMessages.h>
  21. #include <AzCore/IO/Path/Path.h>
  22. #include <AzToolsFramework/AssetDatabase/AssetDatabaseConnection.h>
  23. #include <AssetManager/SourceAssetReference.h>
  24. #include <AzCore/EBus/Event.h>
  25. #include <AzCore/std/parallel/mutex.h>
  26. namespace AzToolsFramework
  27. {
  28. namespace AssetSystem
  29. {
  30. struct JobInfo;
  31. }
  32. namespace Logging
  33. {
  34. class LogLine;
  35. }
  36. }
  37. class QStringList;
  38. class QDir;
  39. namespace AssetProcessor
  40. {
  41. class PlatformConfiguration;
  42. struct AssetRecognizer;
  43. class JobEntry;
  44. class AssetDatabaseConnection;
  45. struct BuilderParams;
  46. }
  47. namespace AssetUtilities
  48. {
  49. inline constexpr char ProjectPathOverrideParameter[] = "project-path";
  50. //! You can read and write any sub-keys to this key and it will be persisted to the project user registry folder:
  51. inline constexpr AZStd::string_view AssetProcessorUserSettingsRootKey = "/O3DE/AssetProcessor/UserSettings";
  52. class AssetProcessorUserSettingsNotifications : public AZ::EBusTraits
  53. {
  54. public:
  55. static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; // any number of connected listeners
  56. static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; // no addressing used.
  57. typedef AZStd::recursive_mutex MutexType; // protect bus addition and removal since listeners can disconnect in threads.
  58. //! Invoked on SetUserSetting(settingName, ...) and the settingName will be just the name of the setting, not the full registry path.
  59. //! If you hanle your own reading and writing to the above sub-keys, consider invoking this yourself.
  60. virtual void OnSettingChanged(const AZStd::string_view& settingName) = 0;
  61. };
  62. using AssetProcessorUserSettingsNotificationBus = AZ::EBus<AssetProcessorUserSettingsNotifications>;
  63. //! Set a named user setting. The name must be json-path compatible name (avoid dots, slashes, spaces, etc)
  64. //! The value must be something that can be written into the settings registry.
  65. //! Really only meant to be used for very simple types (ints, strings, bools, etc). Don't use it for structs.
  66. //! You can write your own structs as a sub key of AssetProcessorUserSettingsRootKey and invoke SaveSettingsFile.
  67. template<typename T>
  68. bool SetUserSetting(const char* settingName, T value);
  69. //! Gets a named user setting, it will be persisted only for this project, for this user.
  70. template<typename T>
  71. T GetUserSetting(const char* settingName, T defaultValue);
  72. //! Save user settings into the default settings file.
  73. bool SaveSettingsFile();
  74. //! Set precision fingerprint timestamps will be truncated to avoid mismatches across systems/packaging with different file timestamp precisions
  75. //! Timestamps default to milliseconds. A value of 1 will keep the default millisecond precision. A value of 1000 will reduce the precision to seconds
  76. void SetTruncateFingerprintTimestamp(int precision);
  77. //! Sets an override for using file hashing. If override is true, the value of enable will be used instead of the settings file
  78. void SetUseFileHashOverride(bool override, bool enable);
  79. //! Compute the root asset folder by scanning for marker files such as root.ini
  80. //! By Default, queries the EngineRootFolder value from within the SettingsRegistry
  81. bool ComputeAssetRoot(QDir& root, const QDir* assetRootOverride = nullptr);
  82. //! Get the engine root folder by looking up the EngineRootFolder key from the Settings Registry
  83. bool ComputeEngineRoot(QDir& root, const QDir* engineRootOverride = nullptr);
  84. //! Reset the asset root to not be cached anymore. Generally only useful for tests
  85. void ResetAssetRoot();
  86. //! Reset the game name to not be cached anymore. Generally only useful for tests
  87. void ResetGameName();
  88. //! Copy all files from the source directory to the destination directory, returns true if successfull, else return false
  89. bool CopyDirectory(QDir source, QDir destination);
  90. //! makes the file writable
  91. //! return true if operation is successful, otherwise return false
  92. bool MakeFileWritable(const QString& filename);
  93. //! Check to see if we can Lock the file
  94. bool CheckCanLock(const QString& filename);
  95. //! Updates the branch token in the bootstrap file
  96. bool UpdateBranchToken();
  97. bool ShouldUseFileHashing();
  98. //! Determine the name of the current project - for example, AutomatedTesting
  99. //! Can be overridden by passing in a non-empty projectNameOverride
  100. //! The override will persist if the project name wasn't set previously or
  101. //! force=true is supplied
  102. QString ComputeProjectName(QString projectNameOverride = QString(), bool force = false);
  103. //! Determine the absolute path of the current project
  104. //! The path computed path will be cached on subsequent calls unless resetCachedProjectPath=true
  105. QString ComputeProjectPath(bool resetCachedProjectPath = false);
  106. //! Reads the allowed list directly from the bootstrap file
  107. QString ReadAllowedlistFromSettingsRegistry(QString initialFolder = QString());
  108. //! Reads the allowed list directly from the bootstrap file
  109. QString ReadRemoteIpFromSettingsRegistry(QString initialFolder = QString());
  110. //! Writes the allowed list directly to the bootstrap file
  111. bool WriteAllowedlistToSettingsRegistry(const QStringList& allowedList);
  112. //! Reads the listening port from the bootstrap file
  113. //! By default the listening port is 45643
  114. quint16 ReadListeningPortFromSettingsRegistry(QString initialFolder = QString());
  115. //! Reads platforms from command line
  116. QStringList ReadPlatformsFromCommandLine();
  117. //! Copies the sourceFile to the outputFile,returns true if the copy operation succeeds otherwise return false
  118. //! This function will try deleting the outputFile first,if it exists, before doing the copy operation
  119. bool CopyFileWithTimeout(QString sourceFile, QString outputFile, unsigned int waitTimeinSeconds = 0);
  120. //! Moves the sourceFile to the outputFile,returns true if the move operation succeeds otherwise return false
  121. //! This function will try deleting the outputFile first,if it exists, before doing the move operation
  122. bool MoveFileWithTimeout(QString sourceFile, QString outputFile, unsigned int waitTimeinSeconds = 0);
  123. //! Create directory with retries, returns true if the create operation succeeds otherwise return false
  124. bool CreateDirectoryWithTimeout(QDir dir, unsigned int waitTimeinSeconds = 0);
  125. //! Normalize and removes any alias from the path
  126. QString NormalizeAndRemoveAlias(QString path);
  127. //! Determine the Job Description for a job, for now it is the name of the recognizer
  128. QString ComputeJobDescription(const AssetProcessor::AssetRecognizer* recognizer);
  129. //! Compute the root of the cache for the current project.
  130. //! This is generally the "<Project>/Cache" folder
  131. bool ComputeProjectCacheRoot(QDir& projectCacheRoot);
  132. //! Compute the folder that will be used for fence files.
  133. bool ComputeFenceDirectory(QDir& fenceDir);
  134. //! Strips the first "asset platform" from the first path segment of a relative product path
  135. //! This is meant for removing the asset platform for paths such as "pc/MyAssetFolder/MyAsset.asset"
  136. //! Therefore the result here becomes "MyAssetFolder/MyAsset"
  137. //!
  138. //! Similarly invoking this function on relative path that begins with the "server" platform
  139. //! "server/AssetFolder/Server.asset2" -> "AssetFolder/Server.asset2"
  140. //! This function does not strip an asset platform from anywhere, but the first path segment
  141. //! Therefore invoking strip Asset on "MyProject/Cache/pc/MyAsset/MyAsset.asset"
  142. //! would return a copy of the relative path
  143. QString StripAssetPlatform(AZStd::string_view relativeProductPath);
  144. //! Same as StripAssetPlatform, but does not perform any string copies
  145. //! The return result is only valid for as long as the original input is valid
  146. AZStd::string_view StripAssetPlatformNoCopy(AZStd::string_view relativeProductPath, AZStd::string_view* outputPlatform = nullptr);
  147. //! Converts all slashes to forward slashes, removes double slashes,
  148. //! replaces all indirections such as '.' or '..' as appropriate.
  149. //! On windows, the drive letter (if present) is converted to uppercase.
  150. //! Besides that, all case is preserved.
  151. QString NormalizeFilePath(const QString& filePath);
  152. void NormalizeFilePaths(QStringList& filePaths);
  153. //! given a directory name, normalize it the same way as the above file path normalizer
  154. //! does not convert into absolute path - do that yourself before calling this if you want that
  155. QString NormalizeDirectoryPath(const QString& directoryPath);
  156. // UUID generation defaults to lowercase SHA1 of the source name, this does normalization and such
  157. AZ::Uuid CreateSafeSourceUUIDFromName(const char* sourceName, bool caseInsensitive = true);
  158. AZ::Outcome<AZ::Uuid, AZStd::string> GetSourceUuid(const AssetProcessor::SourceAssetReference& sourceAsset);
  159. AZ::Outcome<AZStd::unordered_set<AZ::Uuid>, AZStd::string> GetLegacySourceUuids(const AssetProcessor::SourceAssetReference& sourceAsset);
  160. //! Compute a CRC given a null-terminated string
  161. //! @param[in] priorCRC If supplied, continues an existing CRC by feeding it more data
  162. unsigned int ComputeCRC32(const char* inString, unsigned int priorCRC = 0xFFFFFFFF);
  163. //! Compute a CRC given data and a size
  164. //! @param[in] priorCRC If supplied, continues an existing CRC by feeding it more data
  165. unsigned int ComputeCRC32(const char* data, size_t dataSize, unsigned int priorCRC = 0xFFFFFFFF);
  166. //! Compute a CRC given data and a size
  167. //! @param[in] priorCRC If supplied, continues an existing CRC by feeding it more data
  168. template <typename T>
  169. unsigned int ComputeCRC32(const T* data, size_t dataSize, unsigned int priorCRC = 0xFFFFFFFF)
  170. {
  171. return ComputeCRC32(reinterpret_cast<const char*>(data), dataSize, priorCRC);
  172. }
  173. //! Compute a CRC given a null-terminated string
  174. //! @param[in] priorCRC If supplied, continues an existing CRC by feeding it more data
  175. unsigned int ComputeCRC32Lowercase(const char* inString, unsigned int priorCRC = 0xFFFFFFFF);
  176. //! Compute a CRC given data and a size
  177. //! @param[in] priorCRC If supplied, continues an existing CRC by feeding it more data
  178. unsigned int ComputeCRC32Lowercase(const char* data, size_t dataSize, unsigned int priorCRC = 0xFFFFFFFF);
  179. //! Compute a CRC given data and a size
  180. //! @param[in] priorCRC If supplied, continues an existing CRC by feeding it more data
  181. template <typename T>
  182. unsigned int ComputeCRC32Lowercase(const T* data, size_t dataSize, unsigned int priorCRC = 0xFFFFFFFF)
  183. {
  184. return ComputeCRC32Lowercase(reinterpret_cast<const char*>(data), dataSize, priorCRC);
  185. }
  186. //! attempt to create a workspace for yourself to use as scratch-space, at that starting root folder.
  187. //! If it succeeds, it will return true and set the result to the final absolute folder name.
  188. //! this includes creation of temp folder with numbered/lettered temp characters in it.
  189. //! Note that its up to you to clean this temp workspace up. It will not automatically be deleted!
  190. //! If you fail to delete the temp workspace, it will eventually fill the folder up and cause problems.
  191. bool CreateTempWorkspace(QString startFolder, QString& result);
  192. //! Create a temp workspace in a default location
  193. //! If it succeeds, it will return true and set the result to the final absolute folder name.
  194. //! If it fails, it will return false and result will be an empty string
  195. //! Note that its up to you to clean this temp workspace up. It will not automatically be deleted!
  196. //! If you fail to delete the temp workspace, it will eventually fill the folder up and cause problems.
  197. bool CreateTempWorkspace(QString& result);
  198. bool CreateTempRootFolder(QString startFolder, QDir& tempRoot);
  199. AZStd::string ComputeJobLogFolder();
  200. AZStd::string ComputeJobLogFileName(const AzToolsFramework::AssetSystem::JobInfo& jobInfo);
  201. AZStd::string ComputeJobLogFileName(const AssetProcessor::JobEntry& jobEntry);
  202. AZStd::string ComputeJobLogFileName(const AssetBuilderSDK::CreateJobsRequest& createJobsRequest);
  203. enum class ReadJobLogResult
  204. {
  205. Success,
  206. MissingFileIO,
  207. MissingLogFile,
  208. EmptyLogFile,
  209. };
  210. ReadJobLogResult ReadJobLog(AzToolsFramework::AssetSystem::JobInfo& jobInfo, AzToolsFramework::AssetSystem::AssetJobLogResponse& response);
  211. ReadJobLogResult ReadJobLog(const char* absolutePath, AzToolsFramework::AssetSystem::AssetJobLogResponse& response);
  212. //! interrogate a given file, which is specified as a full path name, and generate a fingerprint for it.
  213. unsigned int GenerateFingerprint(const AssetProcessor::JobDetails& jobDetail);
  214. //! Returns a hash of the contents of the specified file
  215. // hashMsDelay is only for automated tests to test that writing to a file while it's hashing does not cause a crash.
  216. // hashMsDelay is not used in non-unit test builds.
  217. AZ::u64 GetFileHash(const char* filePath, bool force = false, AZ::IO::SizeType* bytesReadOut = nullptr, int hashMsDelay = 0);
  218. //! Adjusts a timestamp to fix timezone settings and account for any precision adjustment needed
  219. AZ::u64 AdjustTimestamp(QDateTime timestamp);
  220. // Generates a fingerprint string based on details of the file, will return the string "0" if the file does not exist.
  221. // note that the 'name to use' can be blank, but it used to disambiguate between files that have the same
  222. // modtime and size.
  223. AZStd::string GetFileFingerprint(const AZStd::string& absolutePath, const AZStd::string& nameToUse);
  224. QString GuessProductNameInDatabase(QString path, QString platform, AssetProcessor::AssetDatabaseConnection* databaseConnection);
  225. //! A utility function which checks the given path starting at the root and updates the relative path to be the actual case correct path.
  226. //! Set checkEntirePath to false if the caller is absolutely sure the path is correct and only the last element (file name or extension)
  227. //! is potentially wrong. This can happen when for example taking a real file found from a real file directory that is already correct
  228. //! and modifying just the file path or extension. It is significantly faster to avoid checking the entire path.
  229. bool UpdateToCorrectCase(const QString& rootPath, QString& relativePathFromRoot, bool checkEntirePath = true);
  230. //! Returns true if the path is in the cachePath and *not* in the intermediate assets folder.
  231. //! If cachePath is empty, it will be computed using ComputeProjectCacheRoot.
  232. bool IsInCacheFolder(AZ::IO::PathView path, AZ::IO::Path cachePath = "");
  233. //! Returns true if the path is in the intermediate assets folder.
  234. //! If cachePath is empty, it will be computed using ComputeProjectCacheRoot.
  235. bool IsInIntermediateAssetsFolder(AZ::IO::PathView path, AZ::IO::PathView cachePath = "");
  236. //! Returns the absolute path of the intermediate assets folder
  237. AZ::IO::FixedMaxPath GetIntermediateAssetsFolder(AZ::IO::PathView cachePath);
  238. //! Appends the platform prefix for an intermediate asset to get the database name used for products
  239. AZStd::string GetIntermediateAssetDatabaseName(AZ::IO::PathView relativePath);
  240. //! Finds the top level source that produced an intermediate product. If the source is not yet recorded in the database or has no top level source, this will return nothing
  241. AZStd::optional<AzToolsFramework::AssetDatabase::SourceDatabaseEntry> GetTopLevelSourceForIntermediateAsset(const AssetProcessor::SourceAssetReference& sourceAsset, AZStd::shared_ptr<AssetProcessor::AssetDatabaseConnection> db);
  242. //! Gets the absolute path to the top level source that produced an intermediate product. Returns nothing if the source is not yet recorded, there is no top level source, or other issues are encountered.
  243. //! Does not check if the file exists.
  244. AZStd::optional<AZ::IO::Path> GetTopLevelSourcePathForIntermediateAsset(
  245. const AssetProcessor::SourceAssetReference& sourceAsset, AZStd::shared_ptr<AssetProcessor::AssetDatabaseConnection> db);
  246. //! Finds all the sources (up and down) in an intermediate output chain
  247. AZStd::vector<AssetProcessor::SourceAssetReference> GetAllIntermediateSources(
  248. const AssetProcessor::SourceAssetReference& sourceAsset, AZStd::shared_ptr<AssetProcessor::AssetDatabaseConnection> db);
  249. //! Given a source path for an intermediate asset, constructs the product path.
  250. //! This does not verify either exist, it just manipulates the string.
  251. AZStd::string GetRelativeProductPathForIntermediateSourcePath(AZStd::string_view relativeSourcePath);
  252. //! Helper class that provides various paths related to a single output asset.
  253. //! Files are not guaranteed to exist at the given path.
  254. struct ProductPath
  255. {
  256. ProductPath(AZStd::string scanfolderRelativeProductPath, AZStd::string platformIdentifier);
  257. static ProductPath FromDatabasePath(AZStd::string_view databasePath, AZStd::string_view* platformOut = nullptr);
  258. static ProductPath FromAbsoluteProductPath(AZ::IO::PathView absolutePath, AZStd::string& outPlatform);
  259. //! Absolute path for the product in the intermediate asset folder. Not guaranteed to exist, this is just the path the file would be at
  260. AZStd::string GetIntermediatePath() const { return m_intermediatePath.StringAsPosix(); }
  261. //! Absolute path for the product in the cache folder. Not guaranteed to exist, this is just the path the file would be at
  262. AZStd::string GetCachePath() const { return m_cachePath.StringAsPosix(); }
  263. //! Relative path of the product for the database, this includes the platform prefix and is lowercased
  264. AZStd::string GetDatabasePath() const { return m_databasePath.StringAsPosix(); }
  265. //! Scanfolder relative path of the product. This is lowercased and does not include the platform prefix
  266. AZStd::string GetRelativePath() const { return m_relativePath; }
  267. protected:
  268. AZStd::string m_relativePath;
  269. AZ::IO::Path m_intermediatePath, m_cachePath, m_databasePath;
  270. };
  271. class BuilderFilePatternMatcher
  272. : public AssetBuilderSDK::FilePatternMatcher
  273. {
  274. public:
  275. AZ_CLASS_ALLOCATOR(BuilderFilePatternMatcher, AZ::SystemAllocator)
  276. BuilderFilePatternMatcher() = default;
  277. BuilderFilePatternMatcher(const BuilderFilePatternMatcher& copy);
  278. BuilderFilePatternMatcher(const AssetBuilderSDK::AssetBuilderPattern& pattern, const AZ::Uuid& builderDescID);
  279. const AZ::Uuid& GetBuilderDescID() const;
  280. protected:
  281. AZ::Uuid m_builderDescID;
  282. };
  283. //! QuitListener is an utility class that can be used to listen for application quit notification
  284. class QuitListener
  285. : public AssetProcessor::ApplicationManagerNotifications::Bus::Handler
  286. {
  287. public:
  288. QuitListener();
  289. ~QuitListener();
  290. /// ApplicationManagerNotifications::Bus::Handler
  291. void ApplicationShutdownRequested() override;
  292. bool WasQuitRequested() const;
  293. private:
  294. AZStd::atomic<bool> m_requestedQuit;
  295. };
  296. //! JobLogTraceListener listens for job messages
  297. class JobLogTraceListener
  298. : public AZ::Debug::TraceMessageBus::Handler
  299. {
  300. public:
  301. JobLogTraceListener(const AZStd::string& logFileName, AZ::s64 jobKey, bool overwriteLogFile = false);
  302. JobLogTraceListener(const AzToolsFramework::AssetSystem::JobInfo& jobInfo, bool overwriteLogFile = false);
  303. JobLogTraceListener(const AssetProcessor::JobEntry& jobEntry, bool overwriteLogFile = false);
  304. ~JobLogTraceListener();
  305. //////////////////////////////////////////////////////////////////////////
  306. // AZ::Debug::TraceMessagesBus - we actually ignore all outputs except those for our ID.
  307. bool OnAssert(const char* message) override;
  308. bool OnException(const char* message) override;
  309. bool OnPreError(const char* window, const char* file, int line, const char* func, const char* message) override;
  310. bool OnWarning(const char* window, const char* message) override;
  311. //////////////////////////////////////////////////////////////////////////
  312. bool OnPrintf(const char* window, const char* message) override;
  313. //////////////////////////////////////////////////////////////////////////
  314. void AppendLog(AzToolsFramework::Logging::LogLine& logLine);
  315. AZ::s64 GetErrorCount() const;
  316. AZ::s64 GetWarningCount() const;
  317. void AddError();
  318. void AddWarning();
  319. private:
  320. AZStd::unique_ptr<AzFramework::LogFile> m_logFile;
  321. AZStd::string m_logFileName;
  322. AZ::s64 m_runKey = 0;
  323. // using m_isLogging bool to prevent an infinite loop which can happen if an error/warning happens when trying to create an invalid logFile,
  324. // because it will cause the appendLog function to be called again, which will again try to create that log file.
  325. bool m_isLogging = false;
  326. bool m_inException = false;
  327. //! If true, log file will be overwritten instead of appended
  328. bool m_forceOverwriteLog = false;
  329. AZ::s64 m_errorCount = 0;
  330. AZ::s64 m_warningCount = 0;
  331. void AppendLog(AzFramework::LogFile::SeverityLevel severity, const char* window, const char* message);
  332. };
  333. extern template bool SetUserSetting<bool>(const char* settingName, bool value);
  334. extern template bool GetUserSetting<bool>(const char* settingName, bool defaultValue);
  335. extern template bool SetUserSetting<AZ::s64>(const char* settingName, AZ::s64 value);
  336. extern template AZ::s64 GetUserSetting<AZ::s64>(const char* settingName, AZ::s64 defaultValue);
  337. extern template bool SetUserSetting<AZ::u64>(const char* settingName, AZ::u64 value);
  338. extern template AZ::u64 GetUserSetting<AZ::u64>(const char* settingName, AZ::u64 defaultValue);
  339. extern template bool SetUserSetting<double>(const char* settingName, double value);
  340. extern template double GetUserSetting<double>(const char* settingName, double defaultValue);
  341. extern template bool SetUserSetting<AZStd::string>(const char* settingName, AZStd::string value);
  342. extern template AZStd::string GetUserSetting<AZStd::string>(const char* settingName, AZStd::string defaultValue);
  343. //! Gets a named user setting, it will be persisted only for this project, for this user.
  344. } // namespace AssetUtilities