ApplicationManager.cpp 29 KB

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