RCBuilder.cpp 31 KB

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