assetUtils.h 20 KB

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