utils.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813
  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. #include <AzFramework/Asset/AssetSystemComponent.h>
  9. #include <AzFramework/API/ApplicationAPI.h>
  10. #include <AzFramework/FileFunc/FileFunc.h>
  11. #include <AzFramework/Platform/PlatformDefaults.h>
  12. #include <AzToolsFramework/Asset/AssetSeedManager.h>
  13. #include <AzToolsFramework/Asset/AssetBundler.h>
  14. #include <AzFramework/IO/LocalFileIO.h>
  15. #include <source/utils/utils.h>
  16. #include <AzFramework/StringFunc/StringFunc.h>
  17. #include <AzCore/IO/FileIO.h>
  18. #include <AzCore/IO/Path/Path.h>
  19. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  20. #include <AzCore/std/algorithm.h>
  21. #include <AzCore/std/string/regex.h>
  22. #include <AzCore/Utils/Utils.h>
  23. #include <cctype>
  24. AZ_PUSH_DISABLE_WARNING(4244 4251, "-Wunknown-warning-option")
  25. #include <QDir>
  26. #include <QString>
  27. #include <QStringList>
  28. #include <QJsonDocument>
  29. AZ_POP_DISABLE_WARNING
  30. namespace AssetBundler
  31. {
  32. // General
  33. const char* AppWindowName = "AssetBundler";
  34. const char* AppWindowNameVerbose = "AssetBundlerVerbose";
  35. const char* HelpFlag = "help";
  36. const char* HelpFlagAlias = "h";
  37. const char* VerboseFlag = "verbose";
  38. const char* SaveFlag = "save";
  39. const char* PlatformArg = "platform";
  40. const char* PrintFlag = "print";
  41. const char* AssetCatalogFileArg = "overrideAssetCatalogFile";
  42. const char* AllowOverwritesFlag = "allowOverwrites";
  43. const char* IgnoreFileCaseFlag = "ignoreFileCase";
  44. const char* ProjectArg = "project-path";
  45. // Seeds
  46. const char* SeedsCommand = "seeds";
  47. const char* SeedListFileArg = "seedListFile";
  48. const char* AddSeedArg = "addSeed";
  49. const char* RemoveSeedArg = "removeSeed";
  50. const char* AddPlatformToAllSeedsFlag = "addPlatformToSeeds";
  51. const char* RemovePlatformFromAllSeedsFlag = "removePlatformFromSeeds";
  52. const char* UpdateSeedPathArg = "updateSeedPath";
  53. const char* RemoveSeedPathArg = "removeSeedPath";
  54. const char* DefaultProjectTemplatePath = "Templates/DefaultProject/Template";
  55. const char* ProjectName = "${Name}";
  56. const char* DependenciesFileSuffix = "_Dependencies";
  57. const char* DependenciesFileExtension = "xml";
  58. // Asset Lists
  59. const char* AssetListsCommand = "assetLists";
  60. const char* AssetListFileArg = "assetListFile";
  61. const char* AddDefaultSeedListFilesFlag = "addDefaultSeedListFiles";
  62. const char* DryRunFlag = "dryRun";
  63. const char* GenerateDebugFileFlag = "generateDebugFile";
  64. const char* SkipArg = "skip";
  65. // Comparison Rules
  66. const char* ComparisonRulesCommand = "comparisonRules";
  67. const char* ComparisonRulesFileArg = "comparisonRulesFile";
  68. const char* ComparisonTypeArg = "comparisonType";
  69. const char* ComparisonFilePatternArg = "filePattern";
  70. const char* ComparisonFilePatternTypeArg = "filePatternType";
  71. const char* ComparisonTokenNameArg = "tokenName";
  72. const char* ComparisonFirstInputArg = "firstInput";
  73. const char* ComparisonSecondInputArg = "secondInput";
  74. const char* AddComparisonStepArg = "addComparison";
  75. const char* RemoveComparisonStepArg = "removeComparison";
  76. const char* MoveComparisonStepArg = "moveComparison";
  77. const char* EditComparisonStepArg = "editComparison";
  78. // Compare
  79. const char* CompareCommand = "compare";
  80. const char* CompareFirstFileArg = "firstAssetFile";
  81. const char* CompareSecondFileArg = "secondAssetFile";
  82. const char* CompareOutputFileArg = "output";
  83. const char* ComparePrintArg = "print";
  84. const char* IntersectionCountArg = "intersectionCount";
  85. // Bundle Settings
  86. const char* BundleSettingsCommand = "bundleSettings";
  87. const char* BundleSettingsFileArg = "bundleSettingsFile";
  88. const char* OutputBundlePathArg = "outputBundlePath";
  89. const char* BundleVersionArg = "bundleVersion";
  90. const char* MaxBundleSizeArg = "maxSize";
  91. // Bundles
  92. const char* BundlesCommand = "bundles";
  93. // Bundle Seed
  94. const char* BundleSeedCommand = "bundleSeed";
  95. const char* AssetCatalogFilename = "assetcatalog.xml";
  96. constexpr auto EngineDirectoryName = AZ::IO::FixedMaxPath("Assets") / "Engine";
  97. const char RestrictedDirectoryName[] = "restricted";
  98. const char PlatformsDirectoryName[] = "Platforms";
  99. const char GemsDirectoryName[] = "Gems";
  100. const char GemsSeedFileName[] = "seedList";
  101. const char EngineSeedFileName[] = "SeedAssetList";
  102. namespace Internal
  103. {
  104. const AZ::u32 PlatformFlags_RESTRICTED = aznumeric_cast<AZ::u32>(AzFramework::PlatformFlags::Platform_JASPER | AzFramework::PlatformFlags::Platform_PROVO | AzFramework::PlatformFlags::Platform_SALEM);
  105. void AddPlatformSeeds(
  106. const AZ::IO::Path& engineDirectory,
  107. const AZStd::string& rootFolderDisplayName,
  108. AZStd::unordered_map<AZStd::string, AZStd::string>& defaultSeedLists,
  109. AzFramework::PlatformFlags platformFlags)
  110. {
  111. AZ::IO::FixedMaxPath engineRoot(AZ::Utils::GetEnginePath());
  112. AZ::IO::FixedMaxPath engineRestrictedRoot = engineRoot / RestrictedDirectoryName;
  113. AZ::IO::FixedMaxPath engineLocalPath = AZ::IO::PathView(engineDirectory.LexicallyRelative(engineRoot));
  114. AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
  115. auto platformsIdxList = AzFramework::PlatformHelper::GetPlatformIndicesInterpreted(platformFlags);
  116. for (const AzFramework::PlatformId& platformId : platformsIdxList)
  117. {
  118. const AzFramework::PlatformFlags platformFlag = AzFramework::PlatformHelper::GetPlatformFlagFromPlatformIndex(platformId);
  119. const char* platformDirName = AzFramework::PlatformHelper::GetPlatformName(platformId);
  120. AZ::IO::FixedMaxPath platformDirectory;
  121. if (aznumeric_cast<AZ::u32>(platformFlag) & PlatformFlags_RESTRICTED)
  122. {
  123. platformDirectory = engineRestrictedRoot / platformDirName / engineLocalPath;
  124. }
  125. else
  126. {
  127. platformDirectory = engineDirectory / PlatformsDirectoryName / platformDirName;
  128. }
  129. if (fileIO->Exists(platformDirectory.c_str()))
  130. {
  131. bool recurse = true;
  132. AZ::Outcome<AZStd::list<AZStd::string>, AZStd::string> result = AzFramework::FileFunc::FindFileList(
  133. platformDirectory.String(),
  134. AZStd::string::format("*.%s", AzToolsFramework::AssetSeedManager::GetSeedFileExtension()).c_str(),
  135. recurse);
  136. if (result.IsSuccess())
  137. {
  138. AZStd::list<AZStd::string> seedFiles = result.TakeValue();
  139. for (AZStd::string& seedFile : seedFiles)
  140. {
  141. AZStd::string normalizedFilePath = seedFile;
  142. AzFramework::StringFunc::Path::Normalize(seedFile);
  143. defaultSeedLists[seedFile] = AZStd::string::format("%s (%s)", rootFolderDisplayName.c_str(), platformDirName);
  144. }
  145. }
  146. }
  147. }
  148. }
  149. void AddPlatformsDirectorySeeds(
  150. const AZ::IO::Path& engineDirectory,
  151. const AZStd::string& rootFolderDisplayName,
  152. AZStd::unordered_map<AZStd::string, AZStd::string>& defaultSeedLists,
  153. AzFramework::PlatformFlags platformFlags)
  154. {
  155. AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
  156. AZ_Assert(fileIO, "AZ::IO::FileIOBase must be ready for use.\n");
  157. // Check whether platforms directory exists inside the root, if yes than add
  158. // * All seed files from the platforms directory
  159. // * All platform specific seed files based on the platform flags specified.
  160. auto platformsDirectory = engineDirectory / PlatformsDirectoryName;
  161. if (fileIO->Exists(platformsDirectory.c_str()))
  162. {
  163. fileIO->FindFiles(platformsDirectory.c_str(),
  164. AZStd::string::format("*.%s", AzToolsFramework::AssetSeedManager::GetSeedFileExtension()).c_str(),
  165. [&](const char* fileName)
  166. {
  167. AZStd::string normalizedFilePath = fileName;
  168. AzFramework::StringFunc::Path::Normalize(normalizedFilePath);
  169. defaultSeedLists[normalizedFilePath] = rootFolderDisplayName;
  170. return true;
  171. });
  172. }
  173. AddPlatformSeeds(engineDirectory, rootFolderDisplayName, defaultSeedLists, platformFlags);
  174. }
  175. }
  176. void AddPlatformIdentifier(AZStd::string& filePath, const AZStd::string& platformIdentifier)
  177. {
  178. AZStd::string fileName;
  179. AzFramework::StringFunc::Path::GetFileName(filePath.c_str(), fileName);
  180. AZStd::string extension;
  181. AzFramework::StringFunc::Path::GetExtension(filePath.c_str(), extension);
  182. AZStd::string platformSuffix = AZStd::string::format("_%s", platformIdentifier.c_str());
  183. fileName = AZStd::string::format("%s%s", fileName.c_str(), platformSuffix.c_str());
  184. AzFramework::StringFunc::Path::ReplaceFullName(filePath, fileName.c_str(), extension.c_str());
  185. }
  186. AzFramework::PlatformFlags GetPlatformsOnDiskForPlatformSpecificFile(const AZStd::string& platformIndependentAbsolutePath)
  187. {
  188. AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_NONE;
  189. auto allPlatformNames = AzFramework::PlatformHelper::GetPlatforms(AzFramework::PlatformFlags::AllNamedPlatforms);
  190. for (const auto& platformName : allPlatformNames)
  191. {
  192. AZStd::string filePath = platformIndependentAbsolutePath;
  193. AddPlatformIdentifier(filePath, platformName);
  194. if (AZ::IO::FileIOBase::GetInstance()->Exists(filePath.c_str()))
  195. {
  196. platformFlags = platformFlags | AzFramework::PlatformHelper::GetPlatformFlag(platformName);
  197. }
  198. }
  199. return platformFlags;
  200. }
  201. AZStd::unordered_map<AZStd::string, AZStd::string> GetDefaultSeedListFiles(
  202. AZStd::string_view enginePath,
  203. AZStd::string_view projectPath,
  204. const AZStd::vector<AzFramework::GemInfo>& gemInfoList,
  205. AzFramework::PlatformFlags platformFlag)
  206. {
  207. AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
  208. AZ_Assert(fileIO, "AZ::IO::FileIOBase must be ready for use.\n");
  209. // Add all seed list files of enabled gems for the given project
  210. AZStd::unordered_map<AZStd::string, AZStd::string> defaultSeedLists = GetGemSeedListFilePathToGemNameMap(gemInfoList, platformFlag);
  211. // Add the engine seed list file
  212. AZ::IO::Path engineDirectory = AZ::IO::Path(enginePath) / EngineDirectoryName;
  213. auto absoluteEngineSeedFilePath = engineDirectory / EngineSeedFileName;
  214. absoluteEngineSeedFilePath.ReplaceExtension(AzToolsFramework::AssetSeedManager::GetSeedFileExtension());
  215. if (fileIO->Exists(absoluteEngineSeedFilePath.c_str()))
  216. {
  217. defaultSeedLists[absoluteEngineSeedFilePath.Native()] = EngineDirectoryName.String();
  218. }
  219. // Add Seed Lists from the Platforms directory
  220. Internal::AddPlatformsDirectorySeeds(engineDirectory, EngineDirectoryName.String(), defaultSeedLists, platformFlag);
  221. auto absoluteProjectDefaultSeedFilePath = AZ::IO::Path(projectPath) / EngineSeedFileName;
  222. absoluteProjectDefaultSeedFilePath.ReplaceExtension(AzToolsFramework::AssetSeedManager::GetSeedFileExtension());
  223. if (fileIO->Exists(absoluteProjectDefaultSeedFilePath.c_str()))
  224. {
  225. defaultSeedLists[absoluteProjectDefaultSeedFilePath.Native()] = projectPath;
  226. }
  227. return defaultSeedLists;
  228. }
  229. AZStd::vector<AZStd::string> GetDefaultSeeds(AZStd::string_view projectPath, AZStd::string_view projectName)
  230. {
  231. AZStd::vector<AZStd::string> defaultSeeds;
  232. defaultSeeds.emplace_back(GetProjectDependenciesAssetPath(projectPath, projectName));
  233. return defaultSeeds;
  234. }
  235. AZ::IO::Path GetProjectDependenciesFile(AZStd::string_view projectPath, AZStd::string_view projectName)
  236. {
  237. AZ::IO::Path projectDependenciesFilePath = projectPath;
  238. projectDependenciesFilePath /= AZStd::string::format("%.*s%s", aznumeric_cast<int>(projectName.size()), projectName.data(),
  239. DependenciesFileSuffix);
  240. projectDependenciesFilePath.ReplaceExtension(DependenciesFileExtension);
  241. return projectDependenciesFilePath.LexicallyNormal();
  242. }
  243. AZ::IO::Path GetProjectDependenciesAssetPath(AZStd::string_view projectPath, AZStd::string_view projectName)
  244. {
  245. AZ::IO::Path projectDependenciesFile = GetProjectDependenciesFile(projectPath, projectName);
  246. if (!AZ::IO::FileIOBase::GetInstance()->Exists(projectDependenciesFile.c_str()))
  247. {
  248. AZ_Error(AssetBundler::AppWindowName, false, "Project dependencies file %s doesn't exist.\n", projectDependenciesFile.c_str());
  249. }
  250. // Turn the absolute path into a cache-relative path
  251. AZ::IO::Path relativeProductPath = projectDependenciesFile.Filename().Native();
  252. AZStd::to_lower(relativeProductPath.Native().begin(), relativeProductPath.Native().end());
  253. return relativeProductPath;
  254. }
  255. AZStd::unordered_map<AZStd::string, AZStd::string> GetGemSeedListFilePathToGemNameMap(
  256. const AZStd::vector<AzFramework::GemInfo>& gemInfoList,
  257. AzFramework::PlatformFlags platformFlags)
  258. {
  259. AZStd::unordered_map<AZStd::string, AZStd::string> filePathToGemNameMap;
  260. for (const AzFramework::GemInfo& gemInfo : gemInfoList)
  261. {
  262. for (const AZ::IO::Path& gemAbsoluteSourcePath : gemInfo.m_absoluteSourcePaths)
  263. {
  264. AZ::IO::Path gemInfoAssetFilePath = gemAbsoluteSourcePath;
  265. gemInfoAssetFilePath /= gemInfo.GetGemAssetFolder();
  266. AZ::IO::Path absoluteGemSeedFilePath = gemInfoAssetFilePath / GemsSeedFileName;
  267. absoluteGemSeedFilePath.ReplaceExtension(AZ::IO::PathView{ AzToolsFramework::AssetSeedManager::GetSeedFileExtension() });
  268. absoluteGemSeedFilePath = absoluteGemSeedFilePath.LexicallyNormal();
  269. AZStd::string gemName = gemInfo.m_gemName + " Gem";
  270. if (AZ::IO::FileIOBase::GetInstance()->Exists(absoluteGemSeedFilePath.c_str()))
  271. {
  272. filePathToGemNameMap[absoluteGemSeedFilePath.Native()] = gemName;
  273. }
  274. Internal::AddPlatformsDirectorySeeds(gemInfoAssetFilePath.Native(), gemName, filePathToGemNameMap, platformFlags);
  275. }
  276. }
  277. return filePathToGemNameMap;
  278. }
  279. bool IsGemSeedFilePathValid(
  280. AZStd::string_view engineRoot,
  281. AZStd::string seedAbsoluteFilePath,
  282. const AZStd::vector<AzFramework::GemInfo>& gemInfoList,
  283. AzFramework::PlatformFlags platformFlags)
  284. {
  285. AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
  286. AZ_Assert(fileIO, "AZ::IO::FileIOBase must be ready for use.\n");
  287. if (!fileIO->Exists(seedAbsoluteFilePath.c_str()))
  288. {
  289. return false;
  290. }
  291. AZ::IO::Path gemsFolder{ engineRoot };
  292. gemsFolder /= GemsDirectoryName;
  293. gemsFolder = gemsFolder.LexicallyNormal();
  294. if (!AzFramework::StringFunc::StartsWith(seedAbsoluteFilePath, gemsFolder.Native()))
  295. {
  296. // if we are here it implies that this seed file does not live under the gems directory and
  297. // therefore we do not have to validate it
  298. return true;
  299. }
  300. AZ::IO::Path seedAbsolutePath{seedAbsoluteFilePath};
  301. for (const AzFramework::GemInfo& gemInfo : gemInfoList)
  302. {
  303. for (const AZ::IO::Path& gemAbsoluteSourcePath : gemInfo.m_absoluteSourcePaths)
  304. {
  305. // We want to check the path before going through the effort of creating the default Seed List file map
  306. if (!seedAbsolutePath.IsRelativeTo(gemAbsoluteSourcePath.LexicallyNormal()))
  307. {
  308. continue;
  309. }
  310. AZStd::unordered_map<AZStd::string, AZStd::string> seeds = GetGemSeedListFilePathToGemNameMap({ gemInfo }, platformFlags);
  311. if (seeds.find(seedAbsoluteFilePath) != seeds.end())
  312. {
  313. return true;
  314. }
  315. // If we have not validated the input path yet, we need to keep looking, or we will return false negatives
  316. // for Gems that have the same prefix in their name
  317. }
  318. }
  319. return false;
  320. }
  321. AzFramework::PlatformFlags GetEnabledPlatformFlags(
  322. AZStd::string_view engineRoot,
  323. AZStd::string_view projectPath)
  324. {
  325. auto settingsRegistry = AZ::SettingsRegistry::Get();
  326. if (settingsRegistry == nullptr)
  327. {
  328. AZ_Error(AssetBundler::AppWindowName, false, "Settings Registry is not available, enabled platform flags cannot be queried");
  329. return AzFramework::PlatformFlags::Platform_NONE;
  330. }
  331. auto configFiles = AzToolsFramework::AssetUtils::GetConfigFiles(engineRoot, projectPath, true, true, settingsRegistry);
  332. auto enabledPlatformList = AzToolsFramework::AssetUtils::GetEnabledPlatforms(*settingsRegistry, configFiles);
  333. AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_NONE;
  334. for (const auto& enabledPlatform : enabledPlatformList)
  335. {
  336. AzFramework::PlatformFlags platformFlag = AzFramework::PlatformHelper::GetPlatformFlag(enabledPlatform);
  337. if (platformFlag != AzFramework::PlatformFlags::Platform_NONE)
  338. {
  339. platformFlags = platformFlags | platformFlag;
  340. }
  341. else
  342. {
  343. AZ_Warning(AssetBundler::AppWindowName, false,
  344. "Platform Helper is not aware of the platform (%s).\n ", enabledPlatform.c_str());
  345. }
  346. }
  347. return platformFlags;
  348. }
  349. void ValidateOutputFilePath(FilePath filePath, const char* format, ...)
  350. {
  351. if (!filePath.IsValid())
  352. {
  353. char message[MaxErrorMessageLength] = {};
  354. va_list args;
  355. va_start(args, format);
  356. azvsnprintf(message, MaxErrorMessageLength, format, args);
  357. va_end(args);
  358. AZ_Error(AssetBundler::AppWindowName, false, message);
  359. }
  360. }
  361. AZ::Outcome<AZStd::string, AZStd::string> GetCurrentProjectName()
  362. {
  363. AZStd::string projectName{ AZStd::string_view(AZ::Utils::GetProjectName()) };
  364. if (!projectName.empty())
  365. {
  366. return AZ::Success(projectName);
  367. }
  368. else
  369. {
  370. return AZ::Failure(AZStd::string("Unable to obtain current project name from registry"));
  371. }
  372. }
  373. AZ::Outcome<AZ::IO::Path, AZStd::string> GetProjectFolderPath()
  374. {
  375. AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath();
  376. if (!projectPath.empty())
  377. {
  378. return AZ::Success(AZ::IO::Path{ AZ::IO::PathView(projectPath) });
  379. }
  380. else
  381. {
  382. return AZ::Failure(AZStd::string::format("Unable to obtain current project path from registry"));
  383. }
  384. }
  385. AZ::Outcome<AZ::IO::Path, AZStd::string> GetProjectCacheFolderPath()
  386. {
  387. AZ::IO::Path projectCacheFolderPath;
  388. auto settingsRegistry = AZ::SettingsRegistry::Get();
  389. if (settingsRegistry && settingsRegistry->Get(projectCacheFolderPath.Native(),
  390. AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder))
  391. {
  392. if (AZ::IO::FileIOBase::GetInstance()->Exists(projectCacheFolderPath.c_str()))
  393. {
  394. return AZ::Success(projectCacheFolderPath);
  395. }
  396. }
  397. return AZ::Failure(AZStd::string::format(
  398. "Unable to locate the Project Cache path from Settings Registry at key %s."
  399. " Please run the O3DE Asset Processor to generate a Cache and build assets.",
  400. AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder));
  401. }
  402. AZ::Outcome<AZ::IO::Path, AZStd::string> GetAssetCatalogFilePath()
  403. {
  404. AZ::IO::Path assetCatalogFilePath = GetPlatformSpecificCacheFolderPath();
  405. if (assetCatalogFilePath.empty())
  406. {
  407. return AZ::Failure(AZStd::string::format(
  408. "Unable to retrieve cache platform path from Settings Registry at key: %s. Please run the O3DE Asset Processor to generate platform-specific cache folders and build assets.",
  409. AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder));
  410. }
  411. assetCatalogFilePath /= AssetCatalogFilename;
  412. return AZ::Success(assetCatalogFilePath);
  413. }
  414. AZ::IO::Path GetPlatformSpecificCacheFolderPath()
  415. {
  416. AZ::IO::Path platformSpecificCacheFolderPath;
  417. if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
  418. {
  419. settingsRegistry->Get(
  420. platformSpecificCacheFolderPath.Native(),
  421. AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder);
  422. }
  423. return platformSpecificCacheFolderPath;
  424. }
  425. AZStd::string GenerateKeyFromAbsolutePath(const AZStd::string& absoluteFilePath)
  426. {
  427. AZStd::string key(absoluteFilePath);
  428. AzFramework::StringFunc::Path::Normalize(key);
  429. AzFramework::StringFunc::Path::StripDrive(key);
  430. return key;
  431. }
  432. void ConvertToRelativePath(AZStd::string_view parentFolderPath, AZStd::string& absoluteFilePath)
  433. {
  434. absoluteFilePath = AZ::IO::PathView(absoluteFilePath).LexicallyRelative(parentFolderPath).String();
  435. }
  436. AZ::Outcome<void, AZStd::string> MakePath(const AZStd::string& path)
  437. {
  438. // Create the folder if it does not already exist
  439. if (!AZ::IO::FileIOBase::GetInstance()->Exists(path.c_str()))
  440. {
  441. auto result = AZ::IO::FileIOBase::GetInstance()->CreatePath(path.c_str());
  442. if (!result)
  443. {
  444. return AZ::Failure(AZStd::string::format("Path creation failed. Input path: %s \n", path.c_str()));
  445. }
  446. }
  447. return AZ::Success();
  448. }
  449. WarningAbsorber::WarningAbsorber()
  450. {
  451. AZ::Debug::TraceMessageBus::Handler::BusConnect();
  452. }
  453. WarningAbsorber::~WarningAbsorber()
  454. {
  455. AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
  456. }
  457. bool WarningAbsorber::OnWarning(const char* window, const char* message)
  458. {
  459. AZ_UNUSED(window);
  460. AZ_UNUSED(message);
  461. return true; // do not forward
  462. }
  463. bool WarningAbsorber::OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message)
  464. {
  465. AZ_UNUSED(window);
  466. AZ_UNUSED(fileName);
  467. AZ_UNUSED(line);
  468. AZ_UNUSED(func);
  469. AZ_UNUSED(message);
  470. return true; // do not forward
  471. }
  472. FilePath::FilePath(const AZStd::string& filePath, AZStd::string platformIdentifier, bool checkFileCase, bool ignoreFileCase)
  473. {
  474. AZStd::string platform = platformIdentifier;
  475. if (!platform.empty())
  476. {
  477. AZStd::string filePlatform = AzToolsFramework::GetPlatformIdentifier(filePath);
  478. if (!filePlatform.empty())
  479. {
  480. // input file path already has a platform, no need to append a platform id
  481. platform = AZStd::string();
  482. if (!AzFramework::StringFunc::Equal(filePlatform.c_str(), platformIdentifier.c_str(), true))
  483. {
  484. // Platform identifier does not match the current platform
  485. return;
  486. }
  487. }
  488. }
  489. if (!filePath.empty())
  490. {
  491. m_validPath = true;
  492. m_absolutePath = AZ::IO::PathView(filePath).LexicallyNormal();
  493. m_originalPath = m_absolutePath;
  494. ComputeAbsolutePath(platform, checkFileCase, ignoreFileCase);
  495. }
  496. }
  497. FilePath::FilePath(const AZStd::string& filePath, bool checkFileCase, bool ignoreFileCase)
  498. :FilePath(filePath, AZStd::string(), checkFileCase, ignoreFileCase)
  499. {
  500. }
  501. const AZStd::string& FilePath::AbsolutePath() const
  502. {
  503. return m_absolutePath.Native();
  504. }
  505. const AZStd::string& FilePath::OriginalPath() const
  506. {
  507. return m_originalPath.Native();
  508. }
  509. bool FilePath::IsValid() const
  510. {
  511. return m_validPath;
  512. }
  513. AZStd::string FilePath::ErrorString() const
  514. {
  515. return m_errorString;
  516. }
  517. void FilePath::ComputeAbsolutePath(const AZStd::string& platformIdentifier, bool checkFileCase, bool ignoreFileCase)
  518. {
  519. if (AzToolsFramework::AssetFileInfoListComparison::IsTokenFile(m_absolutePath.Native()))
  520. {
  521. return;
  522. }
  523. if (!platformIdentifier.empty())
  524. {
  525. AssetBundler::AddPlatformIdentifier(m_absolutePath.Native(), platformIdentifier);
  526. }
  527. AZ::IO::Path enginePath = AZ::IO::PathView(AZ::Utils::GetEnginePath());
  528. m_absolutePath = enginePath / m_absolutePath;
  529. if (checkFileCase)
  530. {
  531. AZ::IO::Path relFilePath = m_absolutePath.LexicallyProximate(enginePath);
  532. if (AzToolsFramework::AssetUtils::UpdateFilePathToCorrectCase(enginePath.Native(), relFilePath.Native()))
  533. {
  534. if (ignoreFileCase)
  535. {
  536. m_absolutePath = (enginePath / relFilePath).String();
  537. }
  538. else
  539. {
  540. AZ::IO::Path absfilePath = (enginePath / relFilePath).LexicallyNormal();
  541. if (absfilePath != AZ::IO::PathView(m_absolutePath))
  542. {
  543. m_errorString = AZStd::string::format("File case mismatch, file ( %s ) does not exist on disk, did you mean file ( %s )."
  544. " Please run the command again with the correct file path or use ( --%s ) arg if you want to allow case insensitive file match.\n",
  545. m_absolutePath.c_str(), absfilePath.c_str(), IgnoreFileCaseFlag);
  546. m_validPath = false;
  547. }
  548. }
  549. }
  550. }
  551. }
  552. ScopedTraceHandler::ScopedTraceHandler()
  553. {
  554. BusConnect();
  555. }
  556. ScopedTraceHandler::~ScopedTraceHandler()
  557. {
  558. BusDisconnect();
  559. }
  560. bool ScopedTraceHandler::OnError(const char* window, const char* message)
  561. {
  562. AZ_UNUSED(window);
  563. if (m_reportingError)
  564. {
  565. // if we are reporting error than we dont want to store errors again.
  566. return false;
  567. }
  568. m_errors.emplace_back(message);
  569. return true;
  570. }
  571. int ScopedTraceHandler::GetErrorCount() const
  572. {
  573. return static_cast<int>(m_errors.size());
  574. }
  575. void ScopedTraceHandler::ReportErrors()
  576. {
  577. m_reportingError = true;
  578. #if defined(AZ_ENABLE_TRACING)
  579. for (const AZStd::string& error : m_errors)
  580. {
  581. AZ_Error(AssetBundler::AppWindowName, false, error.c_str());
  582. }
  583. #endif
  584. ClearErrors();
  585. m_reportingError = false;
  586. }
  587. void ScopedTraceHandler::ClearErrors()
  588. {
  589. m_errors = {};
  590. }
  591. AZ::Outcome<AzToolsFramework::AssetFileInfoListComparison::ComparisonType, AZStd::string> ParseComparisonType(
  592. const AZStd::string& comparisonType)
  593. {
  594. using namespace AzToolsFramework;
  595. const size_t numTypes = AZ_ARRAY_SIZE(AssetFileInfoListComparison::ComparisonTypeNames);
  596. int comparisonTypeIndex = 0;
  597. if (AzFramework::StringFunc::LooksLikeInt(comparisonType.c_str(), &comparisonTypeIndex))
  598. {
  599. // User passed in a number
  600. if (comparisonTypeIndex < numTypes)
  601. {
  602. return AZ::Success(static_cast<AssetFileInfoListComparison::ComparisonType>(comparisonTypeIndex));
  603. }
  604. }
  605. else
  606. {
  607. // User passed in the name of a ComparisonType
  608. for (size_t i = 0; i < numTypes; ++i)
  609. {
  610. if (AzFramework::StringFunc::Equal(comparisonType.c_str(), AssetFileInfoListComparison::ComparisonTypeNames[i]))
  611. {
  612. return AZ::Success(static_cast<AssetFileInfoListComparison::ComparisonType>(i));
  613. }
  614. }
  615. }
  616. // Failure case
  617. AZStd::string failureMessage = AZStd::string::format(
  618. "Invalid Comparison Type ( %s ). Valid types are: ", comparisonType.c_str());
  619. for (size_t i = 0; i < numTypes - 1; ++i)
  620. {
  621. failureMessage.append(AZStd::string::format("%s, ", AssetFileInfoListComparison::ComparisonTypeNames[i]));
  622. }
  623. failureMessage.append(AZStd::string::format("and %s.", AssetFileInfoListComparison::ComparisonTypeNames[numTypes - 1]));
  624. return AZ::Failure(failureMessage);
  625. }
  626. AZ::Outcome<AzToolsFramework::AssetFileInfoListComparison::FilePatternType, AZStd::string> ParseFilePatternType(
  627. const AZStd::string& filePatternType)
  628. {
  629. using namespace AzToolsFramework;
  630. const size_t numTypes = AZ_ARRAY_SIZE(AssetFileInfoListComparison::FilePatternTypeNames);
  631. int filePatternTypeIndex = 0;
  632. if (AzFramework::StringFunc::LooksLikeInt(filePatternType.c_str(), &filePatternTypeIndex))
  633. {
  634. // User passed in a number
  635. if (filePatternTypeIndex < numTypes)
  636. {
  637. return AZ::Success(static_cast<AssetFileInfoListComparison::FilePatternType>(filePatternTypeIndex));
  638. }
  639. }
  640. else
  641. {
  642. // User passed in the name of a FilePatternType
  643. for (size_t i = 0; i < numTypes; ++i)
  644. {
  645. if (AzFramework::StringFunc::Equal(filePatternType.c_str(), AssetFileInfoListComparison::FilePatternTypeNames[i]))
  646. {
  647. return AZ::Success(static_cast<AssetFileInfoListComparison::FilePatternType>(i));
  648. }
  649. }
  650. }
  651. // Failure case
  652. AZStd::string failureMessage = AZStd::string::format(
  653. "Invalid File Pattern Type ( %s ). Valid types are: ", filePatternType.c_str());
  654. for (size_t i = 0; i < numTypes - 1; ++i)
  655. {
  656. failureMessage.append(AZStd::string::format("%s, ", AssetFileInfoListComparison::FilePatternTypeNames[i]));
  657. }
  658. failureMessage.append(AZStd::string::format("and %s.", AssetFileInfoListComparison::FilePatternTypeNames[numTypes - 1]));
  659. return AZ::Failure(failureMessage);
  660. }
  661. bool LooksLikePath(const AZStd::string& inputString)
  662. {
  663. for (auto thisChar : inputString)
  664. {
  665. if (thisChar == '.' || thisChar == AZ_CORRECT_FILESYSTEM_SEPARATOR || thisChar == AZ_WRONG_FILESYSTEM_SEPARATOR)
  666. {
  667. return true;
  668. }
  669. }
  670. return false;
  671. }
  672. bool LooksLikeWildcardPattern(const AZStd::string& inputPattern)
  673. {
  674. for (auto thisChar : inputPattern)
  675. {
  676. if (thisChar == '*' || thisChar == '?')
  677. {
  678. return true;
  679. }
  680. }
  681. return false;
  682. }
  683. QJsonObject ReadJson(const AZStd::string& filePath)
  684. {
  685. QByteArray byteArray;
  686. QFile jsonFile;
  687. jsonFile.setFileName(filePath.c_str());
  688. jsonFile.open(QIODevice::ReadOnly | QIODevice::Text);
  689. byteArray = jsonFile.readAll();
  690. jsonFile.close();
  691. return QJsonDocument::fromJson(byteArray).object();
  692. }
  693. void SaveJson(const AZStd::string& filePath, const QJsonObject& jsonObject)
  694. {
  695. QFile jsonFile(filePath.c_str());
  696. QJsonDocument JsonDocument;
  697. JsonDocument.setObject(jsonObject);
  698. jsonFile.open(QFile::WriteOnly | QFile::Text | QFile::Truncate);
  699. jsonFile.write(JsonDocument.toJson());
  700. jsonFile.close();
  701. }
  702. }