ApplicationManager.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809
  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 "native/utilities/ApplicationManager.h"
  9. #include <AzCore/Casting/lossy_cast.h>
  10. #include <AzCore/IO/Path/Path.h>
  11. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  12. #include <AzCore/Utils/Utils.h>
  13. #include <AzFramework/Logging/LoggingComponent.h>
  14. #include <AzFramework/Asset/AssetSystemComponent.h>
  15. #include <native/resourcecompiler/RCBuilder.h>
  16. #include <native/utilities/StatsCapture.h>
  17. #include <native/utilities/PlatformConfiguration.h>
  18. #include <QLocale>
  19. #include <QTranslator>
  20. #include <QCommandLineParser>
  21. #include <QSettings>
  22. #include <AzToolsFramework/Archive/ArchiveComponent.h>
  23. #include <AzFramework/Asset/AssetCatalogComponent.h>
  24. #include <AzToolsFramework/Entity/EditorEntityFixupComponent.h>
  25. #include <AzToolsFramework/ToolsComponents/ToolsAssetCatalogComponent.h>
  26. #include <AzToolsFramework/AssetBrowser/AssetBrowserComponent.h>
  27. #include <AzToolsFramework/SourceControl/PerforceComponent.h>
  28. #include <AzToolsFramework/Prefab/PrefabSystemComponent.h>
  29. #include <AzToolsFramework/Metadata/MetadataManager.h>
  30. #include <AzToolsFramework/Metadata/UuidUtils.h>
  31. namespace AssetProcessor
  32. {
  33. void MessageHandler(QtMsgType type, [[maybe_unused]] const QMessageLogContext& context, [[maybe_unused]] const QString& msg)
  34. {
  35. switch (type)
  36. {
  37. case QtDebugMsg:
  38. AZ_TracePrintf(AssetProcessor::DebugChannel, "Qt-Debug: %s (%s:%u, %s)\n", msg.toUtf8().constData(), context.file, context.line, context.function);
  39. break;
  40. case QtWarningMsg:
  41. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Qt-Warning: %s (%s:%u, %s)\n", msg.toUtf8().constData(), context.file, context.line, context.function);
  42. break;
  43. case QtCriticalMsg:
  44. AZ_Warning(AssetProcessor::ConsoleChannel, false, "Qt-Critical: %s (%s:%u, %s)\n", msg.toUtf8().constData(), context.file, context.line, context.function);
  45. break;
  46. case QtFatalMsg:
  47. AZ_Error(AssetProcessor::ConsoleChannel, false, "Qt-Fatal: %s (%s:%u, %s)\n", msg.toUtf8().constData(), context.file, context.line, context.function);
  48. abort();
  49. }
  50. }
  51. //! we filter the main app logs to only include non-job-thread messages:
  52. class FilteredLogComponent
  53. : public AzFramework::LogComponent
  54. , public AssetUtilities::AssetProcessorUserSettingsNotificationBus::Handler
  55. {
  56. public:
  57. AZ_CLASS_ALLOCATOR(FilteredLogComponent, AZ::SystemAllocator)
  58. void OutputMessage(AzFramework::LogFile::SeverityLevel severity, const char* window, const char* message) override
  59. {
  60. // if we receive an exception it means we are likely to crash. in that case, even if it occurred in a job thread
  61. // it occurred in THIS PROCESS, which will now die. So we log these even in the case of them happening in a job thread.
  62. if ((m_inException)||(severity == AzFramework::LogFile::SEV_EXCEPTION))
  63. {
  64. if (!m_inException)
  65. {
  66. m_inException = true; // from this point on, consume all messages regardless of what severity they are.
  67. AZ::Debug::Trace::HandleExceptions(false);
  68. }
  69. AzFramework::LogComponent::OutputMessage(AzFramework::LogFile::SEV_EXCEPTION, ConsoleChannel, message);
  70. // note that OutputMessage will only output to the log, we want this kind of info to make its way into the
  71. // regular stderr too
  72. fprintf(stderr, "Exception log: %s - %s", window, message);
  73. fflush(stderr);
  74. return;
  75. }
  76. if (AssetProcessor::GetThreadLocalJobId() != 0)
  77. {
  78. // we are in a job thread - return early to make it so that the global log file does not get this message
  79. // there will also be a log listener in the actual job log thread which will get the message too, and that one
  80. // will write it to the individual log.
  81. return;
  82. }
  83. if ((m_verboseMode) || (severity >= AzFramework::LogFile::SEV_NORMAL))
  84. {
  85. AzFramework::LogComponent::OutputMessage(severity, window, message);
  86. }
  87. }
  88. void Activate() override
  89. {
  90. if (auto* registry = AZ::SettingsRegistry::Get())
  91. {
  92. m_verboseMode = AssetUtilities::GetUserSetting("logVerbosity", false);
  93. }
  94. AssetUtilities::AssetProcessorUserSettingsNotificationBus::Handler::BusConnect();
  95. AzFramework::LogComponent::Activate();
  96. }
  97. void Deactivate() override
  98. {
  99. AzFramework::LogComponent::Deactivate();
  100. AssetUtilities::AssetProcessorUserSettingsNotificationBus::Handler::BusDisconnect();
  101. }
  102. void OnSettingChanged(const AZStd::string_view& settingName) override
  103. {
  104. if (settingName == "logVerbosity")
  105. {
  106. m_verboseMode = AssetUtilities::GetUserSetting("logVerbosity", false);
  107. }
  108. }
  109. protected:
  110. bool m_inException = false;
  111. bool m_verboseMode = false;
  112. };
  113. }
  114. uint qHash(const AZ::Uuid& key, uint seed)
  115. {
  116. (void) seed;
  117. return azlossy_caster(AZStd::hash<AZ::Uuid>()(key));
  118. }
  119. namespace AssetProcessorBuildTarget
  120. {
  121. // Added a declaration of GetBuiderTargetName which retrieves the name of the build target
  122. // The build target changes depending on which shared library/executable ApplicationManager.cpp
  123. // is linked to
  124. AZStd::string_view GetBuildTargetName();
  125. }
  126. AssetProcessorAZApplication::AssetProcessorAZApplication(int* argc, char*** argv, QObject* parent)
  127. : AssetProcessorAZApplication(argc, argv, parent, {})
  128. {
  129. }
  130. AssetProcessorAZApplication::AssetProcessorAZApplication(int* argc, char*** argv, AZ::ComponentApplicationSettings componentAppSettings)
  131. : AssetProcessorAZApplication(argc, argv, nullptr, AZStd::move(componentAppSettings))
  132. {
  133. }
  134. AssetProcessorAZApplication::AssetProcessorAZApplication(int* argc, char*** argv, QObject* parent, AZ::ComponentApplicationSettings componentAppSettings)
  135. : QObject(parent)
  136. , AzToolsFramework::ToolsApplication(argc, argv, AZStd::move(componentAppSettings))
  137. {
  138. // The settings registry has been created at this point, so add the CMake target
  139. // specialization to the settings
  140. AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization(
  141. *AZ::SettingsRegistry::Get(), AssetProcessorBuildTarget::GetBuildTargetName());
  142. // Adding the PreModuleLoad event to the AssetProcessor application for logging when a gem loads
  143. m_preModuleLoadHandler = AZ::ModuleManagerRequests::PreModuleLoadEvent::Handler{
  144. []([[maybe_unused]] AZStd::string_view modulePath)
  145. {
  146. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Loading (Gem) Module '%.*s'...\n", aznumeric_cast<int>(modulePath.size()), modulePath.data());
  147. }
  148. };
  149. m_preModuleLoadHandler.Connect(m_moduleManager->m_preModuleLoadEvent);
  150. }
  151. AZ::ComponentTypeList AssetProcessorAZApplication::GetRequiredSystemComponents() const
  152. {
  153. AZ::ComponentTypeList components = AzFramework::Application::GetRequiredSystemComponents();
  154. for (auto iter = components.begin(); iter != components.end();)
  155. {
  156. if (*iter == azrtti_typeid<AzFramework::AssetSystem::AssetSystemComponent>() // AP does not need asset system component to handle AssetRequestBus calls
  157. || *iter == azrtti_typeid<AzFramework::AssetCatalogComponent>() // AP will use its own AssetCatalogComponent
  158. || *iter == AZ::Uuid("{624a7be2-3c7e-4119-aee2-1db2bdb6cc89}") // ScriptDebugAgent
  159. || *iter == AZ::Uuid("{CAF3A025-FAC9-4537-B99E-0A800A9326DF}") // InputSystemComponent
  160. || *iter == azrtti_typeid<AssetProcessor::ToolsAssetCatalogComponent>()
  161. )
  162. {
  163. // AP does not require the above components to be active
  164. iter = components.erase(iter);
  165. }
  166. else
  167. {
  168. ++iter;
  169. }
  170. }
  171. components.push_back(azrtti_typeid<AzToolsFramework::PerforceComponent>());
  172. components.push_back(azrtti_typeid<AzToolsFramework::Prefab::PrefabSystemComponent>());
  173. components.push_back(azrtti_typeid<AzToolsFramework::ArchiveComponent>()); // AP manages compressed files using ArchiveComponent
  174. components.push_back(azrtti_typeid<AzToolsFramework::MetadataManager>());
  175. components.push_back(azrtti_typeid<AzToolsFramework::UuidUtilComponent>());
  176. return components;
  177. }
  178. void AssetProcessorAZApplication::RegisterCoreComponents()
  179. {
  180. AzToolsFramework::ToolsApplication::RegisterCoreComponents();
  181. RegisterComponentDescriptor(AzToolsFramework::EditorEntityFixupComponent::CreateDescriptor());
  182. RegisterComponentDescriptor(AzToolsFramework::AssetBrowser::AssetBrowserComponent::CreateDescriptor());
  183. }
  184. void AssetProcessorAZApplication::ResolveModulePath(AZ::OSString& modulePath)
  185. {
  186. AssetProcessor::AssetProcessorStatusEntry entry(AssetProcessor::AssetProcessorStatus::Initializing_Gems, 0, QString(modulePath.c_str()));
  187. Q_EMIT AssetProcessorStatus(entry);
  188. AzFramework::Application::ResolveModulePath(modulePath);
  189. }
  190. void AssetProcessorAZApplication::SetSettingsRegistrySpecializations(AZ::SettingsRegistryInterface::Specializations& specializations)
  191. {
  192. AzToolsFramework::ToolsApplication::SetSettingsRegistrySpecializations(specializations);
  193. specializations.Append("assetprocessor");
  194. }
  195. ApplicationManager::ApplicationManager(int* argc, char*** argv, QObject* parent)
  196. : ApplicationManager(argc, argv, parent, {})
  197. {
  198. }
  199. ApplicationManager::ApplicationManager(int* argc, char*** argv, AZ::ComponentApplicationSettings componentAppSettings)
  200. : ApplicationManager(argc, argv, nullptr, AZStd::move(componentAppSettings))
  201. {
  202. }
  203. ApplicationManager::ApplicationManager(int* argc, char*** argv, QObject* parent, AZ::ComponentApplicationSettings componentAppSettings)
  204. : QObject(parent)
  205. , m_frameworkApp(argc, argv, AZStd::move(componentAppSettings))
  206. {
  207. qInstallMessageHandler(&AssetProcessor::MessageHandler);
  208. }
  209. ApplicationManager::~ApplicationManager()
  210. {
  211. // if any of the threads are running, destroy them
  212. // Any QObjects that have these ThreadWorker as parent will also be deleted
  213. if (m_runningThreads.size())
  214. {
  215. for (int idx = 0; idx < m_runningThreads.size(); idx++)
  216. {
  217. m_runningThreads.at(idx)->Destroy();
  218. }
  219. }
  220. for (int idx = 0; idx < m_appDependencies.size(); idx++)
  221. {
  222. delete m_appDependencies[idx];
  223. }
  224. // end stats capture (dump and shutdown)
  225. AssetProcessor::StatsCapture::Dump();
  226. AssetProcessor::StatsCapture::Shutdown();
  227. qInstallMessageHandler(nullptr);
  228. //deleting QCoreApplication/QApplication
  229. delete m_qApp;
  230. if (m_entity)
  231. {
  232. //Deactivate all the components
  233. m_entity->Deactivate();
  234. delete m_entity;
  235. m_entity = nullptr;
  236. }
  237. //Unregistering and deleting all the components
  238. AZ::ComponentDescriptorBus::Event(azrtti_typeid<AzFramework::LogComponent>(), &AZ::ComponentDescriptorBus::Events::ReleaseDescriptor);
  239. //Stop AZFramework
  240. m_frameworkApp.Stop();
  241. AZ::Debug::Trace::HandleExceptions(false);
  242. }
  243. bool ApplicationManager::InitiatedShutdown() const
  244. {
  245. return m_duringShutdown;
  246. }
  247. QDir ApplicationManager::GetSystemRoot() const
  248. {
  249. return m_systemRoot;
  250. }
  251. QString ApplicationManager::GetProjectPath() const
  252. {
  253. auto projectPath = AZ::Utils::GetProjectPath();
  254. if (!projectPath.empty())
  255. {
  256. return QString::fromUtf8(projectPath.c_str(), aznumeric_cast<int>(projectPath.size()));
  257. }
  258. AZ_Warning("AssetUtils", false, "Unable to obtain the Project Path from the settings registry.");
  259. return {};
  260. }
  261. QCoreApplication* ApplicationManager::GetQtApplication()
  262. {
  263. return m_qApp;
  264. }
  265. void ApplicationManager::RegisterObjectForQuit(QObject* source, bool insertInFront)
  266. {
  267. Q_ASSERT(!m_duringShutdown);
  268. if (m_duringShutdown)
  269. {
  270. AZ_Warning(AssetProcessor::DebugChannel, false, "You may not register objects for quit during shutdown.\n");
  271. return;
  272. }
  273. QuitPair quitPair(source, false);
  274. if (!m_objectsToNotify.contains(quitPair))
  275. {
  276. if (insertInFront)
  277. {
  278. m_objectsToNotify.push_front(quitPair);
  279. }
  280. else
  281. {
  282. m_objectsToNotify.push_back(quitPair);
  283. }
  284. if (!connect(source, SIGNAL(ReadyToQuit(QObject*)), this, SLOT(ReadyToQuit(QObject*))))
  285. {
  286. AZ_Warning(AssetProcessor::DebugChannel, false, "ApplicationManager::RegisterObjectForQuit was passed an object of type %s which has no ReadyToQuit(QObject*) signal.\n", source->metaObject()->className());
  287. }
  288. connect(source, SIGNAL(destroyed(QObject*)), this, SLOT(ObjectDestroyed(QObject*)));
  289. }
  290. }
  291. void ApplicationManager::ObjectDestroyed(QObject* source)
  292. {
  293. for (int notifyIdx = 0; notifyIdx < m_objectsToNotify.size(); ++notifyIdx)
  294. {
  295. if (m_objectsToNotify[notifyIdx].first == source)
  296. {
  297. m_objectsToNotify.erase(m_objectsToNotify.begin() + notifyIdx);
  298. if (m_duringShutdown)
  299. {
  300. if (!m_queuedCheckQuit)
  301. {
  302. QTimer::singleShot(0, this, SLOT(CheckQuit()));
  303. m_queuedCheckQuit = true;
  304. }
  305. }
  306. return;
  307. }
  308. }
  309. }
  310. void ApplicationManager::QuitRequested()
  311. {
  312. if (m_duringShutdown)
  313. {
  314. AZ_TracePrintf(AssetProcessor::DebugChannel, "QuitRequested() - already during shutdown\n");
  315. return;
  316. }
  317. if (m_duringStartup)
  318. {
  319. AZ_TracePrintf(AssetProcessor::DebugChannel, "QuitRequested() - during startup - waiting\n");
  320. // if we're still starting up, spin until we're ready to shut down.
  321. QMetaObject::invokeMethod(this, "QuitRequested", Qt::QueuedConnection);
  322. return;
  323. }
  324. AZ_TracePrintf(AssetProcessor::DebugChannel, "QuitRequested() - ready!\n");
  325. m_duringShutdown = true;
  326. //Inform all the builders to shutdown
  327. AssetBuilderSDK::AssetBuilderCommandBus::Broadcast(&AssetBuilderSDK::AssetBuilderCommandBus::Events::ShutDown);
  328. // the following call will invoke on the main thread of the application, since its a direct bus call.
  329. AssetProcessor::ApplicationManagerNotifications::Bus::Broadcast(
  330. &AssetProcessor::ApplicationManagerNotifications::Bus::Events::ApplicationShutdownRequested);
  331. // while it may be tempting to just collapse all of this to a bus call, Qt Objects have the advantage of being
  332. // able to automatically queue calls onto their own thread, and a lot of these involved objects are in fact
  333. // on their own threads. So even if we used a bus call we'd ultimately still have to invoke a queued
  334. // call there anyway.
  335. for (const QuitPair& quitter : m_objectsToNotify)
  336. {
  337. if (!quitter.second)
  338. {
  339. QMetaObject::invokeMethod(quitter.first, "QuitRequested", Qt::QueuedConnection);
  340. }
  341. }
  342. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "App quit requested %d listeners notified.\n", m_objectsToNotify.size());
  343. if (!m_queuedCheckQuit)
  344. {
  345. QTimer::singleShot(0, this, SLOT(CheckQuit()));
  346. m_queuedCheckQuit = true;
  347. }
  348. }
  349. void ApplicationManager::CheckQuit()
  350. {
  351. m_queuedCheckQuit = false;
  352. for (const QuitPair& quitter : m_objectsToNotify)
  353. {
  354. if (!quitter.second)
  355. {
  356. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "App Quit: Object of type %s is not yet ready to quit.\n", quitter.first->metaObject()->className());
  357. return;
  358. }
  359. }
  360. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "App quit requested, and all objects are ready. Quitting app.\n");
  361. // We will now loop over all the running threads and destroy them
  362. // Any QObjects that have these ThreadWorker as parent will also be deleted
  363. for (int idx = 0; idx < m_runningThreads.size(); idx++)
  364. {
  365. m_runningThreads.at(idx)->Destroy();
  366. }
  367. m_runningThreads.clear();
  368. // all good.
  369. qApp->quit();
  370. }
  371. void ApplicationManager::CheckForUpdate()
  372. {
  373. for (int idx = 0; idx < m_appDependencies.size(); ++idx)
  374. {
  375. ApplicationDependencyInfo* fileDependencyInfo = m_appDependencies[idx];
  376. QString fileName = fileDependencyInfo->FileName();
  377. QFileInfo fileInfo(fileName);
  378. if (fileInfo.exists())
  379. {
  380. QDateTime fileLastModifiedTime = fileInfo.lastModified();
  381. bool hasTimestampChanged = (fileDependencyInfo->Timestamp() != fileLastModifiedTime);
  382. if (hasTimestampChanged)
  383. {
  384. QuitRequested();
  385. }
  386. }
  387. else
  388. {
  389. // if one of the files is not present we construct a null datetime for it and
  390. // continue checking
  391. fileDependencyInfo->SetTimestamp(QDateTime());
  392. }
  393. }
  394. }
  395. void ApplicationManager::PopulateApplicationDependencies()
  396. {
  397. connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(CheckForUpdate()));
  398. m_updateTimer.start(5000);
  399. QString currentDir(QCoreApplication::applicationDirPath());
  400. QDir dir(currentDir);
  401. QString applicationPath = QCoreApplication::applicationFilePath();
  402. m_filesOfInterest.push_back(applicationPath);
  403. // add some known-dependent files (this can be removed when they are no longer a dependency)
  404. // Note that its not necessary for any of these files to actually exist. It is considered a "change" if they
  405. // change their file modtime, or if they go from existing to not existing, or if they go from not existing, to existing.
  406. // any of those should cause AP to drop.
  407. for (QString pathName : { "CrySystem",
  408. "SceneCore", "SceneData",
  409. "SceneBuilder", "AzQtComponents"
  410. })
  411. {
  412. QString pathWithPlatformExtension = pathName + QString(AZ_DYNAMIC_LIBRARY_EXTENSION);
  413. m_filesOfInterest.push_back(dir.absoluteFilePath(pathWithPlatformExtension));
  414. }
  415. QDir assetRoot;
  416. AssetUtilities::ComputeAssetRoot(assetRoot);
  417. QString globalConfigPath = assetRoot.filePath("Registry/AssetProcessorPlatformConfig.setreg");
  418. m_filesOfInterest.push_back(globalConfigPath);
  419. QString gamePlatformConfigPath = QDir(AssetUtilities::ComputeProjectPath()).filePath("AssetProcessorGamePlatformConfig.setreg");
  420. m_filesOfInterest.push_back(gamePlatformConfigPath);
  421. // add app modules
  422. AZ::ModuleManagerRequestBus::Broadcast(&AZ::ModuleManagerRequestBus::Events::EnumerateModules,
  423. [this](const AZ::ModuleData& moduleData)
  424. {
  425. AZ::DynamicModuleHandle* handle = moduleData.GetDynamicModuleHandle();
  426. if (handle)
  427. {
  428. QFileInfo fi(handle->GetFilename());
  429. if (fi.exists())
  430. {
  431. m_filesOfInterest.push_back(fi.absoluteFilePath());
  432. }
  433. }
  434. return true; // keep iterating.
  435. });
  436. //find timestamps of all the files
  437. for (int idx = 0; idx < m_filesOfInterest.size(); idx++)
  438. {
  439. QString fileName = m_filesOfInterest.at(idx);
  440. QFileInfo fileInfo(fileName);
  441. QDateTime fileLastModifiedTime = fileInfo.lastModified();
  442. ApplicationDependencyInfo* applicationDependencyInfo = new ApplicationDependencyInfo(fileName, fileLastModifiedTime);
  443. // if some file does not exist than null datetime will be stored
  444. m_appDependencies.push_back(applicationDependencyInfo);
  445. }
  446. }
  447. bool ApplicationManager::StartAZFramework()
  448. {
  449. AzFramework::Application::Descriptor appDescriptor;
  450. AZ::ComponentApplication::StartupParameters params;
  451. QDir projectPath{ AssetUtilities::ComputeProjectPath() };
  452. if (!projectPath.exists("project.json"))
  453. {
  454. AZStd::string errorMsg = AZStd::string::format("Path '%s' is not a valid project path.", projectPath.path().toUtf8().constData());
  455. AssetProcessor::MessageInfoBus::Broadcast(&AssetProcessor::MessageInfoBus::Events::OnErrorMessage, errorMsg.c_str());
  456. return false;
  457. }
  458. QString projectName = AssetUtilities::ComputeProjectName();
  459. // Prevent loading of gems in the Create method of the ComponentApplication
  460. params.m_loadDynamicModules = false;
  461. // Prevent script reflection warnings from bringing down the AssetProcessor
  462. appDescriptor.m_enableScriptReflection = false;
  463. // start listening for exceptions occurring so if something goes wrong we have at least SOME output...
  464. AZ::Debug::Trace::HandleExceptions(true);
  465. m_frameworkApp.Start(appDescriptor, params);
  466. //Registering all the Components
  467. m_frameworkApp.RegisterComponentDescriptor(AzFramework::LogComponent::CreateDescriptor());
  468. Reflect();
  469. const AzFramework::CommandLine* commandLine = nullptr;
  470. AzFramework::ApplicationRequests::Bus::BroadcastResult(commandLine, &AzFramework::ApplicationRequests::GetCommandLine);
  471. if (commandLine && commandLine->HasSwitch("logDir"))
  472. {
  473. AZ::IO::FileIOBase::GetInstance()->SetAlias("@log@", commandLine->GetSwitchValue("logDir", 0).c_str());
  474. }
  475. else if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
  476. {
  477. AZ::IO::Path projectUserPath;
  478. settingsRegistry->Get(projectUserPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectUserPath);
  479. AZ::IO::Path logUserPath = projectUserPath / "log";
  480. auto fileIo = AZ::IO::FileIOBase::GetInstance();
  481. fileIo->SetAlias("@log@", logUserPath.c_str());
  482. }
  483. m_entity = aznew AZ::Entity("Application Entity");
  484. if (m_entity)
  485. {
  486. AssetProcessor::FilteredLogComponent* logger = aznew AssetProcessor::FilteredLogComponent();
  487. m_entity->AddComponent(logger);
  488. if (logger)
  489. {
  490. // Prevent files overwriting each other if you run batch at same time as GUI (unit tests, for example)
  491. logger->SetLogFileBaseName(GetLogBaseName());
  492. }
  493. //Activate all the components
  494. m_entity->Init();
  495. m_entity->Activate();
  496. return true;
  497. }
  498. else
  499. {
  500. //aznew failed
  501. return false;
  502. }
  503. }
  504. bool ApplicationManager::ActivateModules()
  505. {
  506. AssetProcessor::StatsCapture::BeginCaptureStat("LoadingModules");
  507. // we load the editor xml for our modules since it contains the list of gems we need for tools to function (not just runtime)
  508. connect(&m_frameworkApp, &AssetProcessorAZApplication::AssetProcessorStatus, this,
  509. [this](AssetProcessor::AssetProcessorStatusEntry entry)
  510. {
  511. Q_EMIT AssetProcessorStatusChanged(entry);
  512. QCoreApplication::processEvents(QEventLoop::AllEvents);
  513. });
  514. QDir assetRoot;
  515. if (!AssetUtilities::ComputeAssetRoot(assetRoot))
  516. {
  517. AZ_Error(AssetProcessor::ConsoleChannel, false, "Cannot compute the asset root folder. Is AssetProcessor being run from the appropriate folder?");
  518. return false;
  519. }
  520. m_frameworkApp.LoadDynamicModules();
  521. AssetProcessor::StatsCapture::EndCaptureStat("LoadingModules");
  522. return true;
  523. }
  524. void ApplicationManager::addRunningThread(AssetProcessor::ThreadWorker* thread)
  525. {
  526. m_runningThreads.push_back(thread);
  527. }
  528. ApplicationManager::BeforeRunStatus ApplicationManager::BeforeRun()
  529. {
  530. // Create the Qt Application
  531. CreateQtApplication();
  532. if (!StartAZFramework())
  533. {
  534. return ApplicationManager::BeforeRunStatus::Status_Failure;
  535. }
  536. if (!AssetUtilities::ComputeEngineRoot(m_systemRoot))
  537. {
  538. return ApplicationManager::BeforeRunStatus::Status_Failure;
  539. }
  540. if (!AssetUtilities::UpdateBranchToken())
  541. {
  542. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Asset Processor was unable to open the bootstrap file and verify/update the branch token. \
  543. Please ensure that the bootstrap.cfg file is present and not locked by any other program.\n");
  544. return ApplicationManager::BeforeRunStatus::Status_Failure;
  545. }
  546. return ApplicationManager::BeforeRunStatus::Status_Success;
  547. }
  548. bool ApplicationManager::Activate()
  549. {
  550. // enable stats capture from this point on
  551. AssetProcessor::StatsCapture::Initialize();
  552. if (!AssetUtilities::ComputeAssetRoot(m_systemRoot))
  553. {
  554. AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable to compute the asset root for the project, this application cannot launch until this is fixed.");
  555. return false;
  556. }
  557. auto projectName = AssetUtilities::ComputeProjectName();
  558. if (projectName.isEmpty())
  559. {
  560. AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable to detect name of current game project. Configure your game project name to launch this application.");
  561. return false;
  562. }
  563. // the following controls what registry keys (or on mac or linux what entries in home folder) are used
  564. // so they should not be translated!
  565. qApp->setOrganizationName(GetOrganizationName());
  566. qApp->setOrganizationDomain("o3de.org");
  567. qApp->setApplicationName(GetApplicationName());
  568. return true;
  569. }
  570. QString ApplicationManager::GetOrganizationName() const
  571. {
  572. return "O3DE";
  573. }
  574. QString ApplicationManager::GetApplicationName() const
  575. {
  576. return "O3DE Asset Processor";
  577. }
  578. bool ApplicationManager::PostActivate()
  579. {
  580. return true;
  581. }
  582. bool ApplicationManager::NeedRestart() const
  583. {
  584. return m_needRestart;
  585. }
  586. void ApplicationManager::Restart()
  587. {
  588. if (m_needRestart)
  589. {
  590. AZ_TracePrintf(AssetProcessor::DebugChannel, "Restart() - already restarting\n");
  591. return;
  592. }
  593. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "AssetProcessor is restarting.\n");
  594. m_needRestart = true;
  595. m_updateTimer.stop();
  596. QuitRequested();
  597. }
  598. void ApplicationManager::ReadyToQuit(QObject* source)
  599. {
  600. if (!source)
  601. {
  602. return;
  603. }
  604. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "App Quit Object of type %s indicates it is ready.\n", source->metaObject()->className());
  605. for (int notifyIdx = 0; notifyIdx < m_objectsToNotify.size(); ++notifyIdx)
  606. {
  607. if (m_objectsToNotify[notifyIdx].first == source)
  608. {
  609. // replace it.
  610. m_objectsToNotify[notifyIdx] = QuitPair(m_objectsToNotify[notifyIdx].first, true);
  611. }
  612. }
  613. if (!m_queuedCheckQuit)
  614. {
  615. QTimer::singleShot(0, this, SLOT(CheckQuit()));
  616. m_queuedCheckQuit = true;
  617. }
  618. }
  619. void ApplicationManager::RegisterComponentDescriptor(const AZ::ComponentDescriptor* descriptor)
  620. {
  621. AZ_Assert(descriptor, "descriptor cannot be null");
  622. this->m_frameworkApp.RegisterComponentDescriptor(descriptor);
  623. }
  624. ApplicationManager::RegistryCheckInstructions ApplicationManager::CheckForRegistryProblems(QWidget* /*parentWidget*/, [[maybe_unused]] bool showPopupMessage)
  625. {
  626. #if defined(AZ_PLATFORM_WINDOWS)
  627. // There's a bug that prevents rc.exe from closing properly, making it appear
  628. // that jobs never complete. The issue is that Windows sometimes decides to put
  629. // an exe into a special compatibility mode and tells FreeLibrary calls to stop
  630. // doing anything. Once the registry entry for this is written, it never gets
  631. // removed unless the user goes and does it manually in RegEdit.
  632. // To prevent this from being a problem, we check for that registry key
  633. // and tell the user to remove it.
  634. // Here's a link with the same problem reported: https://social.msdn.microsoft.com/Forums/vstudio/en-US/3abe477b-ba6f-49d2-894f-efd42165e620/why-windows-generates-an-ignorefreelibrary-entry-in-appcompatflagslayers-registry-?forum=windowscompatibility
  635. // Here's a link to someone else with the same problem mentioning the problem registry key: https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/606006
  636. QString compatibilityRegistryGroupName = "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers";
  637. QSettings settings(compatibilityRegistryGroupName, QSettings::NativeFormat);
  638. auto keys = settings.childKeys();
  639. for (auto key : keys)
  640. {
  641. if (key.contains("rc.exe", Qt::CaseInsensitive))
  642. {
  643. // Windows will allow us to see that there is an entry, but won't allow us to
  644. // read the entry or to modify it, so we'll have to warn the user instead
  645. // Qt displays the key with the slashes flipped; flip them back because we're on windows
  646. QString windowsFriendlyRegPath = key.replace('/', '\\');
  647. QString warningText = QObject::tr(
  648. "The AssetProcessor will not function correctly with certain registry settings. To correct the problem, please:\n"
  649. "1) Open RegEdit\n"
  650. "2) When Windows asks if you'd like to allow the app to make changes to your device, click \"Yes\"\n"
  651. "3) Open the registry group for the path %0\n"
  652. "4) Delete the key for %1\n"
  653. "5) %2"
  654. ).arg(compatibilityRegistryGroupName, windowsFriendlyRegPath);
  655. if (showPopupMessage)
  656. {
  657. return PopupRegistryProblemsMessage(warningText);
  658. }
  659. else
  660. {
  661. warningText = warningText.arg(tr("Restart the Asset Processor"));
  662. QByteArray warningUtf8 = warningText.toUtf8();
  663. AZ_TracePrintf(AssetProcessor::ConsoleChannel, warningUtf8.data());
  664. }
  665. return RegistryCheckInstructions::Exit;
  666. }
  667. }
  668. #endif
  669. return RegistryCheckInstructions::Continue;
  670. }
  671. QDateTime ApplicationDependencyInfo::Timestamp() const
  672. {
  673. return m_timestamp;
  674. }
  675. void ApplicationDependencyInfo::SetTimestamp(const QDateTime& timestamp)
  676. {
  677. m_timestamp = timestamp;
  678. }
  679. QString ApplicationDependencyInfo::FileName() const
  680. {
  681. return m_fileName;
  682. }
  683. void ApplicationDependencyInfo::SetFileName(QString fileName)
  684. {
  685. m_fileName = fileName;
  686. }