RCBuilder.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702
  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 "RCBuilder.h"
  9. #include <QElapsedTimer>
  10. #include <QCoreApplication>
  11. #include <QRegularExpression>
  12. #include <AzCore/Component/Entity.h>
  13. #include <AzCore/Serialization/SerializeContext.h>
  14. #include <AzCore/IO/SystemFile.h>
  15. #include <AzCore/std/parallel/atomic.h>
  16. #include <AzCore/Utils/Utils.h>
  17. #include <AzFramework/StringFunc/StringFunc.h>
  18. #include <AzFramework/Application/Application.h>
  19. #include <AzFramework/Process/ProcessWatcher.h>
  20. #include <AzToolsFramework/Application/ToolsApplication.h>
  21. #include <AssetBuilderSDK/AssetBuilderSDK.h>
  22. #include <AssetBuilderSDK/AssetBuilderBusses.h>
  23. #include "native/resourcecompiler/rccontroller.h"
  24. #include "native/utilities/assetUtils.h"
  25. #include "native/utilities/AssetBuilderInfo.h"
  26. #include <AssetProcessor_Traits_Platform.h>
  27. namespace AssetProcessor
  28. {
  29. // Temporary solution to get around the fact that we don't have job dependencies
  30. static AZStd::atomic_bool s_TempSolution_CopyJobsFinished(false);
  31. static AZStd::atomic<int> s_TempSolution_CopyJobActivityCounter(0);
  32. static void TempSolution_TouchCopyJobActivity()
  33. {
  34. s_TempSolution_CopyJobActivityCounter++;
  35. s_TempSolution_CopyJobsFinished = false;
  36. }
  37. //! Special configuration keyword to mark a asset pattern for skipping
  38. const QString ASSET_PROCESSOR_CONFIG_KEYWORD_SKIP = "skip";
  39. //! Special configuration keyword to mark a asset pattern for copying
  40. const QString ASSET_PROCESSOR_CONFIG_KEYWORD_COPY = "copy";
  41. namespace Internal
  42. {
  43. void PopulateCommonDescriptorParams(AssetBuilderSDK::JobDescriptor& descriptor, const QString& platformIdentifier, const AssetInternalSpec& platformSpec, const InternalAssetRecognizer* const recognizer)
  44. {
  45. descriptor.m_jobKey = recognizer->m_name;
  46. descriptor.SetPlatformIdentifier(platformIdentifier.toUtf8().constData());
  47. descriptor.m_priority = recognizer->m_priority;
  48. descriptor.m_checkExclusiveLock = recognizer->m_testLockSource;
  49. AZStd::string extraInformationForFingerprinting;
  50. extraInformationForFingerprinting.append(platformSpec == AssetInternalSpec::Copy ? "copy" : "skip");
  51. extraInformationForFingerprinting.append(recognizer->m_version);
  52. // if we have specified the product asset type, changing it should cause
  53. if (!recognizer->m_productAssetType.IsNull())
  54. {
  55. char typeAsString[64] = { 0 };
  56. recognizer->m_productAssetType.ToString(typeAsString, AZ_ARRAY_SIZE(typeAsString));
  57. extraInformationForFingerprinting.append(typeAsString);
  58. }
  59. descriptor.m_priority = recognizer->m_priority;
  60. descriptor.m_additionalFingerprintInfo = extraInformationForFingerprinting;
  61. const bool isCopyJob = (platformSpec == AssetInternalSpec::Copy);
  62. // Temporary solution to get around the fact that we don't have job dependencies
  63. if (isCopyJob)
  64. {
  65. TempSolution_TouchCopyJobActivity();
  66. }
  67. // If this is a copy job or critical is set to true in the ini file, then its a critical job
  68. descriptor.m_critical = recognizer->m_isCritical || isCopyJob;
  69. descriptor.m_checkServer = recognizer->m_checkServer;
  70. // If the priority of copy job is default then we update it to 1
  71. // This will ensure that copy jobs will be processed before other critical jobs having default priority
  72. if (isCopyJob && recognizer->m_priority == 0)
  73. {
  74. descriptor.m_priority = 1;
  75. }
  76. }
  77. void RegisterInternalAssetRecognizerToMap(
  78. const AssetRecognizer& assetRecognizer,
  79. const AZStd::string& builderId,
  80. AZStd::unordered_map<AZStd::string, AssetInternalSpec>& sourceAssetInternalSpecs,
  81. AZStd::unordered_map<AZStd::string, InternalAssetRecognizerList>& internalRecognizerListByType)
  82. {
  83. // this is called to say that the internal builder with builderID is to handle assets recognized by the given recognizer.
  84. InternalAssetRecognizer* newAssetRecognizer = new InternalAssetRecognizer(assetRecognizer, builderId, sourceAssetInternalSpecs);
  85. // the list is keyed off the builderID.
  86. internalRecognizerListByType[builderId].push_back(newAssetRecognizer);
  87. }
  88. // Split all of the asset recognizers from a container into buckets based on their specific builder action type
  89. void BuildInternalAssetRecognizersByType(const RecognizerContainer& assetRecognizers, AZStd::unordered_map<AZStd::string, InternalAssetRecognizerList>& internalRecognizerListByType)
  90. {
  91. // Go through each asset recognizer's platform specs to determine which type bucket to create and put the converted internal
  92. // assert recognizer into
  93. for (const auto& assetRecognizer : assetRecognizers)
  94. {
  95. // these hashes are keyed on the same key as the incoming asset recognizers list, which is
  96. // [ name in ini file ] --> [regognizer details]
  97. // so like "rc png" --> [details]. Specifically, the QString key is the name of the entry in the INI file and NOT a platform name.
  98. AZStd::unordered_map<AZStd::string, AssetInternalSpec> copyAssetInternalSpecs;
  99. AZStd::unordered_map<AZStd::string, AssetInternalSpec> skipAssetInternalSpecs;
  100. // Go through the global asset recognizers and split them by operation keywords if they exist or by the main rc param
  101. for (auto iterSrcPlatformSpec = assetRecognizer.second.m_platformSpecs.begin();
  102. iterSrcPlatformSpec != assetRecognizer.second.m_platformSpecs.end();
  103. iterSrcPlatformSpec++)
  104. {
  105. auto platformSpec = (*iterSrcPlatformSpec).second;
  106. auto& platformId = (*iterSrcPlatformSpec).first;
  107. if (platformSpec == AssetInternalSpec::Copy)
  108. {
  109. copyAssetInternalSpecs[platformId] = platformSpec;
  110. }
  111. else if (platformSpec == AssetInternalSpec::Skip)
  112. {
  113. skipAssetInternalSpecs[platformId] = platformSpec;
  114. }
  115. }
  116. // Create separate internal asset recognizers based on whether or not they were detected
  117. if (copyAssetInternalSpecs.size() > 0)
  118. {
  119. RegisterInternalAssetRecognizerToMap(
  120. assetRecognizer.second,
  121. BUILDER_ID_COPY.GetId().toUtf8().data(),
  122. copyAssetInternalSpecs,
  123. internalRecognizerListByType);
  124. }
  125. if (skipAssetInternalSpecs.size() > 0)
  126. {
  127. RegisterInternalAssetRecognizerToMap(
  128. assetRecognizer.second,
  129. BUILDER_ID_SKIP.GetId().toUtf8().data(),
  130. skipAssetInternalSpecs,
  131. internalRecognizerListByType);
  132. }
  133. }
  134. }
  135. }
  136. BuilderIdAndName::BuilderIdAndName(QString builderName, QString builderId, Type type, QString rcParam /*=QString("")*/)
  137. : m_builderName(builderName)
  138. , m_builderId(builderId)
  139. , m_type(type)
  140. , m_rcParam(rcParam)
  141. {
  142. }
  143. BuilderIdAndName& BuilderIdAndName::operator=(const AssetProcessor::BuilderIdAndName& src)
  144. {
  145. this->m_builderId = src.m_builderId;
  146. this->m_builderName = src.m_builderName;
  147. this->m_type = src.m_type;
  148. this->m_rcParam = src.m_rcParam;
  149. return *this;
  150. }
  151. const QString& BuilderIdAndName::GetName() const
  152. {
  153. return this->m_builderName;
  154. }
  155. bool BuilderIdAndName::GetUuid(AZ::Uuid& builderUuid) const
  156. {
  157. if (this->m_type == Type::REGISTERED_BUILDER)
  158. {
  159. builderUuid = AZ::Uuid::CreateString(this->m_builderId.toUtf8().data());
  160. return true;
  161. }
  162. else
  163. {
  164. return false;
  165. }
  166. }
  167. const QString& BuilderIdAndName::GetRcParam() const
  168. {
  169. return this->m_rcParam;
  170. }
  171. const QString& BuilderIdAndName::GetId() const
  172. {
  173. return this->m_builderId;
  174. }
  175. const BuilderIdAndName::Type BuilderIdAndName::GetType() const
  176. {
  177. return this->m_type;
  178. }
  179. const char* INTERNAL_BUILDER_UUID_STR = "589BE398-2EBB-4E3C-BE66-C894E34C944D";
  180. const BuilderIdAndName BUILDER_ID_COPY("Internal Copy Builder", "31B74BFD-7046-47AC-A7DA-7D5167E9B2F8", BuilderIdAndName::Type::REGISTERED_BUILDER, ASSET_PROCESSOR_CONFIG_KEYWORD_COPY);
  181. const BuilderIdAndName BUILDER_ID_SKIP("Internal Skip Builder", "A033AF24-5041-4E24-ACEC-161A2E522BB6", BuilderIdAndName::Type::UNREGISTERED_BUILDER, ASSET_PROCESSOR_CONFIG_KEYWORD_SKIP);
  182. const QHash<QString, BuilderIdAndName> ALL_INTERNAL_BUILDER_BY_ID =
  183. {
  184. { BUILDER_ID_COPY.GetId(), BUILDER_ID_COPY },
  185. { BUILDER_ID_SKIP.GetId(), BUILDER_ID_SKIP }
  186. };
  187. InternalAssetRecognizer::InternalAssetRecognizer(const AssetRecognizer& src, const AZStd::string& builderId, const AZStd::unordered_map<AZStd::string, AssetInternalSpec>& AssetInternalSpecByPlatform)
  188. : AssetRecognizer(src.m_name, src.m_testLockSource, src.m_priority, src.m_isCritical, src.m_supportsCreateJobs, src.m_patternMatcher, src.m_version, src.m_productAssetType, src.m_outputProductDependencies, src.m_checkServer)
  189. , m_builderId(builderId)
  190. {
  191. // AssetInternalSpecByPlatform is a hash table like
  192. // "pc" --> (settings to compile on pc)
  193. // "ios" --> settings to compile on ios)
  194. // and so is m_platformSpecsByPlatform
  195. m_platformSpecsByPlatform = AssetInternalSpecByPlatform;
  196. m_paramID = CalculateCRC();
  197. }
  198. AZ::u32 InternalAssetRecognizer::CalculateCRC() const
  199. {
  200. AZ::Crc32 crc;
  201. crc.Add(m_name);
  202. crc.Add(m_builderId);
  203. crc.Add(const_cast<void*>(static_cast<const void*>(&m_testLockSource)), sizeof(m_testLockSource));
  204. crc.Add(const_cast<void*>(static_cast<const void*>(&m_priority)), sizeof(m_priority));
  205. crc.Add(m_patternMatcher.GetBuilderPattern().m_pattern.c_str());
  206. crc.Add(const_cast<void*>(static_cast<const void*>(&m_patternMatcher.GetBuilderPattern().m_type)), sizeof(m_patternMatcher.GetBuilderPattern().m_type));
  207. return static_cast<AZ::u32>(crc);
  208. }
  209. //! Constructor to initialize the internal builders and a general internal builder uuid that is used for bus
  210. //! registration. This constructor is helpful for deriving other classes from this builder for purposes like
  211. //! unit testing.
  212. InternalRecognizerBasedBuilder::InternalRecognizerBasedBuilder(QHash<QString, BuilderIdAndName> inputBuilderByIdMap, AZ::Uuid internalBuilderUuid)
  213. : m_isShuttingDown(false)
  214. , m_internalRecognizerBuilderUuid(internalBuilderUuid)
  215. {
  216. for (BuilderIdAndName builder : inputBuilderByIdMap)
  217. {
  218. m_builderById[builder.GetId()] = inputBuilderByIdMap[builder.GetId()];
  219. }
  220. AssetBuilderSDK::AssetBuilderCommandBus::Handler::BusConnect(m_internalRecognizerBuilderUuid);
  221. }
  222. //! Constructor to initialize the internal based builder to a present set of internal builders and fixed bus id
  223. InternalRecognizerBasedBuilder::InternalRecognizerBasedBuilder()
  224. : InternalRecognizerBasedBuilder(ALL_INTERNAL_BUILDER_BY_ID, AZ::Uuid::CreateString(INTERNAL_BUILDER_UUID_STR))
  225. {
  226. }
  227. InternalRecognizerBasedBuilder::~InternalRecognizerBasedBuilder()
  228. {
  229. AssetBuilderSDK::AssetBuilderCommandBus::Handler::BusDisconnect(m_internalRecognizerBuilderUuid);
  230. for (auto assetRecognizer : m_assetRecognizerDictionary)
  231. {
  232. delete assetRecognizer;
  233. }
  234. }
  235. AssetBuilderSDK::AssetBuilderDesc InternalRecognizerBasedBuilder::CreateBuilderDesc(const QString& builderId, const AZStd::vector<AssetBuilderSDK::AssetBuilderPattern>& builderPatterns)
  236. {
  237. const BuilderIdAndName& builder = m_builderById[builderId];
  238. AssetBuilderSDK::AssetBuilderDesc builderDesc;
  239. builderDesc.m_name = builder.GetName().toUtf8().data();
  240. builderDesc.m_version = 2;
  241. builderDesc.m_patterns = builderPatterns;
  242. builderDesc.m_builderType = AssetBuilderSDK::AssetBuilderDesc::AssetBuilderType::Internal;
  243. // Only set a bus id on the descriptor if the builder is a registered builder
  244. AZ::Uuid busId;
  245. if (builder.GetUuid(busId))
  246. {
  247. builderDesc.m_busId = AZ::Uuid::CreateString(builderId.toUtf8().data());
  248. }
  249. builderDesc.m_createJobFunction = AZStd::bind(&InternalRecognizerBasedBuilder::CreateJobs, this, AZStd::placeholders::_1, AZStd::placeholders::_2);
  250. builderDesc.m_processJobFunction = AZStd::bind(&InternalRecognizerBasedBuilder::ProcessJob, this, AZStd::placeholders::_1, AZStd::placeholders::_2);
  251. return builderDesc;
  252. }
  253. void InternalRecognizerBasedBuilder::ShutDown()
  254. {
  255. m_isShuttingDown = true;
  256. }
  257. bool InternalRecognizerBasedBuilder::Initialize(const RecognizerConfiguration& recognizerConfig)
  258. {
  259. InitializeAssetRecognizers(recognizerConfig.GetAssetRecognizerContainer());
  260. return true;
  261. }
  262. void InternalRecognizerBasedBuilder::InitializeAssetRecognizers(const RecognizerContainer& assetRecognizers)
  263. {
  264. // Split the asset recognizers that were scanned in into 'buckets' for each of the 3 builder ids based on
  265. // either the custom fixed rc params or the standard rc param ('copy','skip', or others)
  266. AZStd::unordered_map<AZStd::string, InternalAssetRecognizerList> internalRecognizerListByType;
  267. Internal::BuildInternalAssetRecognizersByType(assetRecognizers, internalRecognizerListByType);
  268. // note that the QString key to this map is actually the builder ID (as in the QString "Internal Copy Builder" for example)
  269. // and the key of the map is actually a AZStd::list for InternalAssetRecognizer* which belong to that builder
  270. // inside of each such recognizer is a map of [platform] --> options for that platform.
  271. // so visualizing this whole struct in summary might look something like
  272. // "Internal RC Builder" :
  273. // {
  274. // { <----- list of recognizers for that RC builder starts here
  275. // regex: "*.tif",
  276. // builderUUID : "12345-12354-123145",
  277. // platformSpecsByPlatform :
  278. // {
  279. // "pc" : "streaming = 1",
  280. // "ios" : "streaming = 0"
  281. // }
  282. // },
  283. // {
  284. // regex: "*.png",
  285. // builderUUID : "12345-12354-123145",
  286. // platformSpecsByPlatform :
  287. // {
  288. // "pc" : "split=1"
  289. // }
  290. // },
  291. // },
  292. // "Internal Copy Builder",
  293. // {
  294. // regex: "*.cfg",
  295. // builderUUID : "12345-12354-123145",
  296. // platformSpecsByPlatform :
  297. // {
  298. // "pc" : "copy",
  299. // "ios" : "copy"
  300. // }
  301. // },
  302. for (const auto& internalRecognizerList : internalRecognizerListByType)
  303. {
  304. QString builderId = internalRecognizerList.first.c_str();
  305. const BuilderIdAndName& builderInfo = m_builderById[builderId];
  306. QString builderName = builderInfo.GetName();
  307. AZStd::vector<AssetBuilderSDK::AssetBuilderPattern> builderPatterns;
  308. bool supportsCreateJobs = false;
  309. // intentionally using a set here, as we want it to be the same order each time for hashing.
  310. AZStd::set<AZStd::string> fingerprintRelevantParameters;
  311. for (auto* internalAssetRecognizer : internalRecognizerList.second)
  312. {
  313. // so referring to the structure explanation above, internalAssetRecognizer is
  314. // one of those objects that has the RegEx in it, (along with list of commands to apply per platform)
  315. if (internalAssetRecognizer->m_platformSpecsByPlatform.size() == 0)
  316. {
  317. delete internalAssetRecognizer;
  318. AZ_Warning(AssetProcessor::DebugChannel, false, "Skipping recognizer %s, no platforms supported\n", builderName.toUtf8().data());
  319. continue;
  320. }
  321. // Ignore duplicate recognizers
  322. // note that m_paramID is the CRC of a bunch of values inside the recognizer, so different recognizers should have a different paramID.
  323. if (m_assetRecognizerDictionary.contains(internalAssetRecognizer->m_paramID))
  324. {
  325. delete internalAssetRecognizer;
  326. AZ_Warning(AssetProcessor::DebugChannel, false, "Ignoring duplicate asset recognizer in configuration: %s\n", builderName.toUtf8().data());
  327. continue;
  328. }
  329. for (const auto& iteratorValue : internalAssetRecognizer->m_platformSpecsByPlatform)
  330. {
  331. fingerprintRelevantParameters.insert(
  332. AZStd::string::format(
  333. "%s-%s",
  334. iteratorValue.first.c_str(),
  335. iteratorValue.second == AssetInternalSpec::Copy ? "copy" : "skip"));
  336. }
  337. // note that the version number must be included here, despite the builder dirty-check function taking version into account
  338. // because the RC Builder is just a single builder (with version#0) that defers to these "internal" builders when called upon.
  339. if (!internalAssetRecognizer->m_version.empty())
  340. {
  341. fingerprintRelevantParameters.insert(internalAssetRecognizer->m_version);
  342. }
  343. fingerprintRelevantParameters.insert(internalAssetRecognizer->m_productAssetType.ToString<AZStd::string>());
  344. // Register the recognizer
  345. builderPatterns.push_back(internalAssetRecognizer->m_patternMatcher.GetBuilderPattern());
  346. m_assetRecognizerDictionary[internalAssetRecognizer->m_paramID] = internalAssetRecognizer;
  347. AZ_TracePrintf(AssetProcessor::DebugChannel, "Registering %s as a %s\n", internalAssetRecognizer->m_name.c_str(),
  348. builderName.toUtf8().data());
  349. supportsCreateJobs = supportsCreateJobs || (internalAssetRecognizer->m_supportsCreateJobs);
  350. }
  351. // Register the builder desc if its registrable
  352. if (builderInfo.GetType() == BuilderIdAndName::Type::REGISTERED_BUILDER)
  353. {
  354. AssetBuilderSDK::AssetBuilderDesc builderDesc = CreateBuilderDesc(builderId, builderPatterns);
  355. // RC Builder also needs to include its platforms and its RC command lines so that if you change this, the jobs
  356. // are re-evaluated.
  357. size_t currentHash = 0;
  358. for (const AZStd::string& element : fingerprintRelevantParameters)
  359. {
  360. AZStd::hash_combine<AZStd::string>(currentHash, element);
  361. }
  362. builderDesc.m_analysisFingerprint = AZStd::string::format("0x%zX", currentHash);
  363. // the "rc" builder can only emit dependencies if it has createjobs in a recognizer.
  364. if (!supportsCreateJobs)
  365. {
  366. // optimization: copy builder emits no dependencies since its just a copy builder.
  367. builderDesc.m_flags |= AssetBuilderSDK::AssetBuilderDesc::BF_EmitsNoDependencies;
  368. }
  369. AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBusTraits::RegisterBuilderInformation, builderDesc);
  370. }
  371. }
  372. }
  373. void InternalRecognizerBasedBuilder::UnInitialize()
  374. {
  375. for (BuilderIdAndName builder: m_builderById.values())
  376. {
  377. AZ::Uuid builderUuid;
  378. // Register the builder desc if its registrable
  379. if ((builder.GetType() == BuilderIdAndName::Type::REGISTERED_BUILDER) && (builder.GetUuid(builderUuid)))
  380. {
  381. AssetBuilderRegistrationBus::Broadcast(&AssetBuilderRegistrationBus::Events::UnRegisterBuilderDescriptor, builderUuid);
  382. }
  383. }
  384. }
  385. bool InternalRecognizerBasedBuilder::GetMatchingRecognizers(const AZStd::vector<AssetBuilderSDK::PlatformInfo>& platformInfos, const QString& fileName, InternalRecognizerPointerContainer& output) const
  386. {
  387. QByteArray fileNameUtf8 = fileName.toUtf8();
  388. AZ_Assert(fileName.contains('\\') == false, "fileName must not contain backslashes: %s", fileNameUtf8.constData());
  389. bool foundAny = false;
  390. // assetRecognizerDictionary is a key value pair dictionary where
  391. // [key] is m_paramID of a recognizer - that is, a unique id of an internal asset recognizer
  392. // and [value] is the actual recognizer.
  393. // inside recognizers are the pattern that they match, as well as the various platforms that they compile for.
  394. for (const InternalAssetRecognizer* recognizer : m_assetRecognizerDictionary)
  395. {
  396. // so this platform is supported. Check if the file matches the regex in MatchesPath.
  397. if (recognizer->m_patternMatcher.MatchesPath(fileNameUtf8.constData()))
  398. {
  399. // this recognizer does match that particular file name.
  400. // do we know how to compile it for any of the platforms?
  401. for (const AssetBuilderSDK::PlatformInfo& platformInfo : platformInfos)
  402. {
  403. // recognizer->m_platformSpecsByplatform is a dictionary like
  404. // ["pc"] -> what to do with the asset on PC.
  405. // ["ios"] -> what to do wiht the asset on ios
  406. if (recognizer->m_platformSpecsByPlatform.find(platformInfo.m_identifier.c_str()) != recognizer->m_platformSpecsByPlatform.end())
  407. {
  408. // yes, we have at least one platform that overlaps with the enabled platform list.
  409. output.push_back(recognizer);
  410. foundAny = true;
  411. break;
  412. }
  413. }
  414. }
  415. }
  416. return foundAny;
  417. }
  418. void InternalRecognizerBasedBuilder::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
  419. {
  420. if (m_isShuttingDown)
  421. {
  422. response.m_result = AssetBuilderSDK::CreateJobsResultCode::ShuttingDown;
  423. return;
  424. }
  425. // Convert the incoming builder id (AZ::Uuid) to the equivalent GUID from the asset recognizers
  426. AZStd::string azBuilderId;
  427. request.m_builderid.ToString(azBuilderId,false);
  428. QString requestedBuilderID = QString(azBuilderId.c_str());
  429. response.m_result = AssetBuilderSDK::CreateJobsResultCode::Failed;
  430. QDir watchFolder(request.m_watchFolder.c_str());
  431. QString normalizedPath = watchFolder.absoluteFilePath(request.m_sourceFile.c_str());
  432. normalizedPath = AssetUtilities::NormalizeFilePath(normalizedPath);
  433. // Locate recognizers that match the file
  434. InternalRecognizerPointerContainer recognizers;
  435. if (!GetMatchingRecognizers(request.m_enabledPlatforms, normalizedPath, recognizers))
  436. {
  437. AssetBuilderSDK::BuilderLog(m_internalRecognizerBuilderUuid, "Cannot find recognizer for %s.", request.m_sourceFile.c_str());
  438. if (request.m_enabledPlatforms.empty())
  439. {
  440. response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
  441. }
  442. return;
  443. }
  444. // First pass, check for simple jobs like 'copy'
  445. for (const InternalAssetRecognizer* recognizer : recognizers)
  446. {
  447. bool skippedByPlatform = false;
  448. // Iterate through the platform specific specs and apply the ones that match the platform flag
  449. for (const auto& platformSpec : recognizer->m_platformSpecsByPlatform)
  450. {
  451. if (request.HasPlatform(platformSpec.first.c_str()))
  452. {
  453. // Check if this is the 'skip' parameter
  454. if (platformSpec.second == AssetInternalSpec::Skip)
  455. {
  456. skippedByPlatform = true;
  457. }
  458. // The recognizer's builder id must match the job requests' builder id
  459. else if (requestedBuilderID.compare(recognizer->m_builderId.c_str()) == 0)
  460. {
  461. AssetBuilderSDK::JobDescriptor descriptor;
  462. Internal::PopulateCommonDescriptorParams(descriptor, platformSpec.first.c_str(), platformSpec.second, recognizer);
  463. // Job Parameter Value can be any arbitrary string since we are relying on the key to lookup
  464. // the parameter in the process job
  465. descriptor.m_jobParameters[recognizer->m_paramID] = descriptor.m_jobKey;
  466. response.m_createJobOutputs.push_back(descriptor);
  467. response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
  468. }
  469. }
  470. // Adjust response if we did not get any jobs, but one or more platforms were marked as skipped
  471. if ((response.m_result == AssetBuilderSDK::CreateJobsResultCode::Failed) && (skippedByPlatform))
  472. {
  473. response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
  474. }
  475. }
  476. }
  477. }
  478. void InternalRecognizerBasedBuilder::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
  479. {
  480. AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
  481. if (m_isShuttingDown)
  482. {
  483. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
  484. return;
  485. }
  486. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
  487. if (request.m_jobDescription.m_jobParameters.empty())
  488. {
  489. AZ_TracePrintf(AssetProcessor::ConsoleChannel,
  490. "Job request for %s in builder %s missing job parameters.",
  491. request.m_sourceFile.c_str(),
  492. request.m_builderGuid.ToFixedString().c_str());
  493. return;
  494. }
  495. for (auto jobParam = request.m_jobDescription.m_jobParameters.begin();
  496. jobParam != request.m_jobDescription.m_jobParameters.end();
  497. jobParam++)
  498. {
  499. if (jobCancelListener.IsCancelled())
  500. {
  501. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
  502. return;
  503. }
  504. if (m_assetRecognizerDictionary.find(jobParam->first) == m_assetRecognizerDictionary.end())
  505. {
  506. AZ_TracePrintf(AssetProcessor::ConsoleChannel,
  507. "Job request for %s in builder %s has invalid job parameter (%ld).",
  508. request.m_sourceFile.c_str(),
  509. request.m_builderGuid.ToFixedString().c_str(),
  510. jobParam->first);
  511. continue;
  512. }
  513. InternalAssetRecognizer* assetRecognizer = m_assetRecognizerDictionary[jobParam->first];
  514. if (!assetRecognizer->m_platformSpecsByPlatform.contains(request.m_jobDescription.GetPlatformIdentifier().c_str()))
  515. {
  516. // Skip due to platform restrictions
  517. continue;
  518. }
  519. auto internalJobType = assetRecognizer->m_platformSpecsByPlatform[request.m_jobDescription.GetPlatformIdentifier().c_str()];
  520. if (internalJobType == AssetInternalSpec::Copy)
  521. {
  522. ProcessCopyJob(request, assetRecognizer->m_productAssetType, assetRecognizer->m_outputProductDependencies, jobCancelListener, response);
  523. }
  524. else if (internalJobType == AssetInternalSpec::Skip)
  525. {
  526. // This should not occur because 'skipped' jobs should not be processed
  527. AZ_TracePrintf(AssetProcessor::DebugChannel, "Job ID %lld Failed, encountered an invalid 'skip' parameter during job processing\n", AssetProcessor::GetThreadLocalJobId());
  528. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
  529. }
  530. if (jobCancelListener.IsCancelled())
  531. {
  532. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
  533. }
  534. if (response.m_resultCode != AssetBuilderSDK::ProcessJobResult_Success)
  535. {
  536. // If anything other than a success occurred, break out of the loop and report the failed job
  537. return;
  538. }
  539. }
  540. }
  541. void InternalRecognizerBasedBuilder::ProcessCopyJob(
  542. const AssetBuilderSDK::ProcessJobRequest& request,
  543. AZ::Uuid productAssetType,
  544. bool outputProductDependencies,
  545. const AssetBuilderSDK::JobCancelListener& jobCancelListener,
  546. AssetBuilderSDK::ProcessJobResponse& response)
  547. {
  548. AssetBuilderSDK::JobProduct jobProduct(request.m_fullPath, productAssetType);
  549. if(!outputProductDependencies)
  550. {
  551. jobProduct.m_dependenciesHandled = true; // Copy jobs are meant to be used for assets that have no dependencies and just need to be copied.
  552. }
  553. response.m_outputProducts.push_back(jobProduct);
  554. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
  555. if (jobCancelListener.IsCancelled())
  556. {
  557. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
  558. return;
  559. }
  560. // Temporary solution to get around the fact that we don't have job dependencies
  561. TempSolution_TouchCopyJobActivity();
  562. }
  563. QFileInfoList InternalRecognizerBasedBuilder::GetFilesInDirectory(const QString& directoryPath)
  564. {
  565. QDir workingDir(directoryPath);
  566. QFileInfoList filesInDir(workingDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files));
  567. return filesInDir;
  568. }
  569. bool InternalRecognizerBasedBuilder::MatchTempFileToSkip(const QString& outputFilename)
  570. {
  571. // List of specific files to skip
  572. static const char* s_fileNamesToSkip[] = {
  573. AssetBuilderSDK::s_processJobRequestFileName,
  574. AssetBuilderSDK::s_processJobResponseFileName,
  575. "rc_createdfiles.txt",
  576. "rc_log.log",
  577. "rc_log_warnings.log",
  578. "rc_log_errors.log"
  579. };
  580. for (const char* filenameToSkip : s_fileNamesToSkip)
  581. {
  582. if (QString::compare(outputFilename, filenameToSkip, Qt::CaseInsensitive) == 0)
  583. {
  584. return true;
  585. }
  586. }
  587. // List of specific file name patters to skip
  588. static const QString s_filePatternsToSkip[] = {
  589. QString(".*\\.\\$.*"),
  590. QString("log.*\\.txt")
  591. };
  592. for (const QString& patternsToSkip : s_filePatternsToSkip)
  593. {
  594. QRegularExpression skipRegex(patternsToSkip, QRegularExpression::CaseInsensitiveOption);
  595. QRegularExpressionMatch match = skipRegex.match(outputFilename);
  596. if (match.hasMatch() && match.capturedLength() == outputFilename.length())
  597. {
  598. return true;
  599. }
  600. }
  601. return false;
  602. }
  603. }