BatchApplicationManager.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  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 "BatchApplicationManager.h"
  9. #include <native/resourcecompiler/rccontroller.h>
  10. #include <native/AssetManager/assetScanner.h>
  11. #include <native/utilities/BatchApplicationServer.h>
  12. #include <AzToolsFramework/UI/Logging/LogLine.h>
  13. #include <QCoreApplication>
  14. // in batch mode, we are going to show the log files of up to N failures.
  15. // in order to not spam the logs, we limit this - its possible that something fundamental is broken and EVERY asset is failing
  16. // and we don't want to thus write gigabytes of logs out.
  17. const int s_MaximumFailuresToReport = 10;
  18. #if defined(AZ_PLATFORM_WINDOWS)
  19. namespace BatchApplicationManagerPrivate
  20. {
  21. BatchApplicationManager* g_appManager = nullptr;
  22. BOOL WINAPI CtrlHandlerRoutine(DWORD dwCtrlType)
  23. {
  24. (void)dwCtrlType;
  25. AZ_Printf("AssetProcessor", "Asset Processor Batch Processing Interrupted. Quitting.\n");
  26. QMetaObject::invokeMethod(g_appManager, "QuitRequested", Qt::QueuedConnection);
  27. return TRUE;
  28. }
  29. }
  30. #endif //#if defined(AZ_PLATFORM_WINDOWS)
  31. BatchApplicationManager::BatchApplicationManager(int* argc, char*** argv, QObject* parent)
  32. : BatchApplicationManager(argc, argv, parent, {})
  33. {
  34. }
  35. BatchApplicationManager::BatchApplicationManager(int* argc, char*** argv, AZ::ComponentApplicationSettings componentAppSettings)
  36. : BatchApplicationManager(argc, argv, nullptr, AZStd::move(componentAppSettings))
  37. {
  38. }
  39. BatchApplicationManager::BatchApplicationManager(int* argc, char*** argv, QObject* parent, AZ::ComponentApplicationSettings componentAppSettings)
  40. : ApplicationManagerBase(argc, argv, parent, AZStd::move(componentAppSettings))
  41. {
  42. AssetProcessor::MessageInfoBus::Handler::BusConnect();
  43. }
  44. BatchApplicationManager::~BatchApplicationManager()
  45. {
  46. AssetProcessor::MessageInfoBus::Handler::BusDisconnect();
  47. }
  48. void BatchApplicationManager::Destroy()
  49. {
  50. #if defined(AZ_PLATFORM_WINDOWS)
  51. SetConsoleCtrlHandler((PHANDLER_ROUTINE)BatchApplicationManagerPrivate::CtrlHandlerRoutine, FALSE);
  52. BatchApplicationManagerPrivate::g_appManager = nullptr;
  53. #endif //#if defined(AZ_PLATFORM_WINDOWS)
  54. ApplicationManagerBase::Destroy();
  55. }
  56. bool BatchApplicationManager::Activate()
  57. {
  58. #if defined(AZ_PLATFORM_WINDOWS)
  59. BatchApplicationManagerPrivate::g_appManager = this;
  60. SetConsoleCtrlHandler((PHANDLER_ROUTINE)BatchApplicationManagerPrivate::CtrlHandlerRoutine, TRUE);
  61. #endif //defined(AZ_PLATFORM_WINDOWS)
  62. return ApplicationManagerBase::Activate();
  63. }
  64. void BatchApplicationManager::OnErrorMessage([[maybe_unused]] const char* error)
  65. {
  66. AZ_Error("AssetProcessor", false, "%s", error);
  67. }
  68. void BatchApplicationManager::Reflect()
  69. {
  70. ApplicationManagerBase::Reflect();
  71. AZ::SerializeContext* context = nullptr;
  72. AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
  73. AZ_Assert(context, "No serialize context");
  74. AssetProcessor::PlatformConfiguration::Reflect(context);
  75. }
  76. const char* BatchApplicationManager::GetLogBaseName()
  77. {
  78. return "AP_Batch";
  79. }
  80. ApplicationManager::RegistryCheckInstructions BatchApplicationManager::PopupRegistryProblemsMessage(QString warningText)
  81. {
  82. return RegistryCheckInstructions::Exit;
  83. }
  84. void BatchApplicationManager::InitSourceControl()
  85. {
  86. bool enableSourceControl = false;
  87. const AzFramework::CommandLine* commandLine = nullptr;
  88. AzFramework::ApplicationRequests::Bus::BroadcastResult(commandLine, &AzFramework::ApplicationRequests::GetCommandLine);
  89. if (commandLine->HasSwitch("enablescm"))
  90. {
  91. enableSourceControl = true;
  92. }
  93. if (enableSourceControl)
  94. {
  95. AzToolsFramework::SourceControlConnectionRequestBus::Broadcast(&AzToolsFramework::SourceControlConnectionRequestBus::Events::EnableSourceControl, true);
  96. }
  97. else
  98. {
  99. Q_EMIT SourceControlReady();
  100. }
  101. }
  102. void BatchApplicationManager::InitUuidManager()
  103. {
  104. m_uuidManager = AZStd::make_unique<AssetProcessor::UuidManager>();
  105. m_assetProcessorManager->SetMetaCreationDelay(0);
  106. // Note that batch does not set any enabled types and has 0 delay because batch mode is not expected to generate metadata files or handle moving/renaming while running.
  107. }
  108. void BatchApplicationManager::MakeActivationConnections()
  109. {
  110. QObject::connect(m_rcController, &AssetProcessor::RCController::FileCompiled,
  111. m_assetProcessorManager, [this](AssetProcessor::JobEntry entry, AssetBuilderSDK::ProcessJobResponse /*response*/)
  112. {
  113. m_processedAssetCount++;
  114. // If a file fails and later succeeds, don't count it as a failure.
  115. // This avoids marking the entire run as a failure (returning non-zero) when everything compiled successfully *eventually*
  116. m_failedAssets.erase(entry.GetAbsoluteSourcePath().toUtf8().constData());
  117. AssetProcessor::JobDiagnosticInfo info{};
  118. AssetProcessor::JobDiagnosticRequestBus::BroadcastResult(info, &AssetProcessor::JobDiagnosticRequestBus::Events::GetDiagnosticInfo, entry.m_jobRunKey);
  119. m_warningCount += info.m_warningCount;
  120. m_errorCount += info.m_errorCount;
  121. });
  122. QObject::connect(m_rcController, &AssetProcessor::RCController::FileFailed,
  123. m_assetProcessorManager, [this](AssetProcessor::JobEntry entry)
  124. {
  125. m_failedAssets.emplace(entry.GetAbsoluteSourcePath().toUtf8().constData());
  126. AssetProcessor::JobDiagnosticInfo info{};
  127. AssetProcessor::JobDiagnosticRequestBus::BroadcastResult(info, &AssetProcessor::JobDiagnosticRequestBus::Events::GetDiagnosticInfo, entry.m_jobRunKey);
  128. m_warningCount += info.m_warningCount;
  129. m_errorCount += info.m_errorCount;
  130. using AssetJobLogRequest = AzToolsFramework::AssetSystem::AssetJobLogRequest;
  131. using AssetJobLogResponse = AzToolsFramework::AssetSystem::AssetJobLogResponse;
  132. if (m_failedAssets.size() < s_MaximumFailuresToReport) // if we're in the situation where many assets are failing we need to stop spamming after a few
  133. {
  134. AssetJobLogRequest request;
  135. AssetJobLogResponse response;
  136. request.m_jobRunKey = entry.m_jobRunKey;
  137. QMetaObject::invokeMethod(GetAssetProcessorManager(), "ProcessGetAssetJobLogRequest", Qt::DirectConnection, Q_ARG(const AssetJobLogRequest&, request), Q_ARG(AssetJobLogResponse&, response));
  138. if (response.m_isSuccess)
  139. {
  140. // write the log to console!
  141. AzToolsFramework::Logging::LogLine::ParseLog(response.m_jobLog.c_str(), response.m_jobLog.size(),
  142. [](AzToolsFramework::Logging::LogLine& target)
  143. {
  144. // We're going to output *everything* because when a non-obvious error occurs, even mundane info output can be helpful for diagnosing the cause of the error
  145. AZStd::string logString = target.ToString();
  146. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "JOB LOG: %s", logString.c_str());
  147. });
  148. }
  149. }
  150. else if (m_failedAssets.size() == s_MaximumFailuresToReport)
  151. {
  152. // notify the user that we're done here, and will not be notifying any more.
  153. AZ_Printf(AssetProcessor::ConsoleChannel, "%s\n", QCoreApplication::translate("Batch Mode", "Too Many Compile Errors - not printing out full logs for remaining errors").toUtf8().constData());
  154. }
  155. });
  156. m_connectionsToRemoveOnShutdown << QObject::connect(m_assetScanner, &AssetProcessor::AssetScanner::AssetScanningStatusChanged, this,
  157. [this](AssetProcessor::AssetScanningStatus status)
  158. {
  159. if ((status == AssetProcessor::AssetScanningStatus::Completed) || (status == AssetProcessor::AssetScanningStatus::Stopped))
  160. {
  161. AZ_Printf(AssetProcessor::ConsoleChannel, QCoreApplication::translate("Batch Mode", "Analyzing scanned files for changes...\n").toUtf8().constData());
  162. CheckForIdle();
  163. }
  164. });
  165. }
  166. void BatchApplicationManager::TryScanProductDependencies()
  167. {
  168. if (!m_dependencyScanPattern.isEmpty())
  169. {
  170. m_assetProcessorManager->ScanForMissingProductDependencies(m_dependencyScanPattern, m_fileDependencyScanPattern, m_dependencyAddtionalScanFolders, m_dependencyScanMaxIteration);
  171. m_dependencyScanPattern.clear();
  172. }
  173. }
  174. void BatchApplicationManager::TryHandleFileRelocation()
  175. {
  176. HandleFileRelocation();
  177. }
  178. bool BatchApplicationManager::InitApplicationServer()
  179. {
  180. m_applicationServer = new BatchApplicationServer();
  181. return true;
  182. }