3dec5d3b71
* LYN-2537 Moved the Engine and Editor folder to be within the EngineAssets folder * Fixed Documentation in bootstrap.cfg to correct the path to the user project specific registry file * Adding a newline to the output of AssetCatalog 'Registering asset..., but type is not set' message * Updating the AssetProcessorPlatformConfig.setreg Scan Folder to detect the @ENGINEROOT@/EngineAssets/Engine path for engine runtime assets and @ENGINEROOT@/EngineAssets/Editor path for engine tool assets * Updating references to Icons and other assets to account for moving the Engine and Editor folder under a single EngineAssets folder * Moving the Engine Settings Registry folder from Engine/Registry -> Registry * Removed the LY_PROJECT_CMAKE_PATH define as it is not portable to other locations. It is hard coded to the project location that was used for the CMake configuration. Furthermore it paths with backslashes within it are treated as escape characters and not a path separator * Updated the LyTestTools asset_processor.py script to copy the exclude.filetag from the EngineAssets/Engine directory now * Fixed Atom Shader Preprocessing when running using an External Project * Updated the TSGenerateAction.cpp to fix the build error with using a renamed variable * Updated the Install_Common.cmake ly_setup_others function to install the EngineAssets directory and the each of the Gem's Assets directory while maintaining the relative directory structure to the Engine Root Also updated the install step to install the Registry folder at the engine root * Fixed the copying of the Registry folder to be in the install root, instead of under a second 'Registry' folder * Moving the AssetProcessorPlatformConfig.setreg file over to the Registry folder * Updated the LyTestTools and C++ code to point that the new location of the AssetProcessorPlatformConfig.setreg file inside of the Registry folder * Renamed Test AssetProcessor*Config.ini files to have the .setreg extension * Converted the AssetProcessor test setreg files from ini format to json format using the SerializeContextTools convert-ini command * Updated the AssetProcessor CMakeLists.txt to copy over the test setreg files to the build folder * Updated the assetprocessor test file list to point at the renamed AsssetProcessor*Config setreg filenames * Removed the Output Prefix code from the AssetProcessor. The complexity that it brought to the AP code is not needed, as users can replicate the behavior by just moving there assets underneath a another folder, underneath the scan folder * Adding back support to read the AssetProcessorPlatformConfig.setreg file from the asset root. This is only needed for C++ UnitTests as they run in an environment where the accessing the Engine Settings Registry is not available * Updating the Install_common.cmake logic to copy any "Assets" folder to the install layout. The Script has also been updated to copy over the "Assets" folder in the Engine Root to the install layout instead of an "EngineAssets" folder * Updating References to EngineAssets source asset folder in code to be the Assets source folder * Moved the Engine Source Asset folder of 'EngineAssets' to a new folder name of 'Assets'. This is inline with the naming scheme we use for Gem asset folders * Adding the EngineFinder.cmake to the AutomatedTesting project to allow it to work in a project centric manner * Updating the LyTestTools copy_assets_to_project function to be able to copy assets with folders to the temporary project root Fixed an issue in LyTestTools where the temporary log directory could have shutil.rmtree being called twice on it leading to an exception which fails an automated test Updated the asset_procesor_gui_tests_2 AddScanFolder test to not use the output prefix, but instead place the source asset root into a subdirectory * Correct the AssetProcessorPlatformConfig Scan Folders for the EngineAssets directory to point at the Assets directory * Updated the asset procesor batch dependency test scan folder to point at the 'Assets' folder instead of 'EngineAssets'
783 lines
28 KiB
C++
783 lines
28 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
|
|
#include "native/utilities/ApplicationManager.h"
|
|
|
|
#include <AzCore/Casting/lossy_cast.h>
|
|
#include <AzCore/IO/Path/Path.h>
|
|
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
|
#include <AzCore/Utils/Utils.h>
|
|
|
|
#include <AzFramework/Logging/LoggingComponent.h>
|
|
#include <AzFramework/Asset/AssetSystemComponent.h>
|
|
|
|
#include "native/resourcecompiler/RCBuilder.h"
|
|
|
|
#include <QLocale>
|
|
#include <QTranslator>
|
|
#include <QCommandLineParser>
|
|
|
|
#include <QSettings>
|
|
|
|
#include <AzFramework/Asset/AssetCatalogComponent.h>
|
|
#include <AzToolsFramework/Entity/EditorEntityFixupComponent.h>
|
|
#include <AzToolsFramework/ToolsComponents/ToolsAssetCatalogComponent.h>
|
|
#include <AzToolsFramework/AssetBrowser/AssetBrowserComponent.h>
|
|
#include <AzToolsFramework/SourceControl/PerforceComponent.h>
|
|
#include <AzToolsFramework/Prefab/PrefabSystemComponent.h>
|
|
|
|
namespace AssetProcessor
|
|
{
|
|
void MessageHandler(QtMsgType type, [[maybe_unused]] const QMessageLogContext& context, [[maybe_unused]] const QString& msg)
|
|
{
|
|
switch (type)
|
|
{
|
|
case QtDebugMsg:
|
|
AZ_TracePrintf(AssetProcessor::DebugChannel, "Qt-Debug: %s (%s:%u, %s)\n", msg.toUtf8().constData(), context.file, context.line, context.function);
|
|
break;
|
|
case QtWarningMsg:
|
|
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Qt-Warning: %s (%s:%u, %s)\n", msg.toUtf8().constData(), context.file, context.line, context.function);
|
|
break;
|
|
case QtCriticalMsg:
|
|
AZ_Warning(AssetProcessor::ConsoleChannel, false, "Qt-Critical: %s (%s:%u, %s)\n", msg.toUtf8().constData(), context.file, context.line, context.function);
|
|
break;
|
|
case QtFatalMsg:
|
|
AZ_Error(AssetProcessor::ConsoleChannel, false, "Qt-Fatal: %s (%s:%u, %s)\n", msg.toUtf8().constData(), context.file, context.line, context.function);
|
|
abort();
|
|
}
|
|
}
|
|
|
|
//! we filter the main app logs to only include non-job-thread messages:
|
|
class FilteredLogComponent
|
|
: public AzFramework::LogComponent
|
|
{
|
|
public:
|
|
void OutputMessage(AzFramework::LogFile::SeverityLevel severity, const char* window, const char* message) override
|
|
{
|
|
// if we receive an exception it means we are likely to crash. in that case, even if it occurred in a job thread
|
|
// it occurred in THIS PROCESS, which will now die. So we log these even in the case of them happening in a job thread.
|
|
if ((m_inException)||(severity == AzFramework::LogFile::SEV_EXCEPTION))
|
|
{
|
|
if (!m_inException)
|
|
{
|
|
m_inException = true; // from this point on, consume all messages regardless of what severity they are.
|
|
AZ::Debug::Trace::HandleExceptions(false);
|
|
}
|
|
AzFramework::LogComponent::OutputMessage(AzFramework::LogFile::SEV_EXCEPTION, ConsoleChannel, message);
|
|
// note that OutputMessage will only output to the log, we want this kind of info to make its way into the
|
|
// regular stderr too
|
|
fprintf(stderr, "Exception log: %s - %s", window, message);
|
|
fflush(stderr);
|
|
return;
|
|
}
|
|
|
|
if (AssetProcessor::GetThreadLocalJobId() != 0)
|
|
{
|
|
// we are in a job thread - return early to make it so that the global log file does not get this message
|
|
// there will also be a log listener in the actual job log thread which will get the message too, and that one
|
|
// will write it to the individual log.
|
|
return;
|
|
}
|
|
|
|
AzFramework::LogComponent::OutputMessage(severity, window, message);
|
|
}
|
|
|
|
protected:
|
|
bool m_inException = false;
|
|
};
|
|
}
|
|
|
|
uint qHash(const AZ::Uuid& key, uint seed)
|
|
{
|
|
(void) seed;
|
|
return azlossy_caster(AZStd::hash<AZ::Uuid>()(key));
|
|
}
|
|
|
|
namespace AssetProcessorBuildTarget
|
|
{
|
|
// Added a declaration of GetBuiderTargetName which retrieves the name of the build target
|
|
// The build target changes depending on which shared library/executable ApplicationManager.cpp
|
|
// is linked to
|
|
AZStd::string_view GetBuildTargetName();
|
|
}
|
|
|
|
|
|
AssetProcessorAZApplication::AssetProcessorAZApplication(int* argc, char*** argv, QObject* parent)
|
|
: QObject(parent)
|
|
, AzToolsFramework::ToolsApplication(argc, argv)
|
|
{
|
|
// The settings registry has been created at this point, so add the CMake target
|
|
// specialization to the settings
|
|
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization(
|
|
*AZ::SettingsRegistry::Get(), AssetProcessorBuildTarget::GetBuildTargetName());
|
|
|
|
// Adding the PreModuleLoad event to the AssetProcessor application for logging when a gem loads
|
|
m_preModuleLoadHandler = AZ::ModuleManagerRequests::PreModuleLoadEvent::Handler{
|
|
[]([[maybe_unused]] AZStd::string_view modulePath)
|
|
{
|
|
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Loading (Gem) Module '%.*s'...\n", aznumeric_cast<int>(modulePath.size()), modulePath.data());
|
|
}
|
|
};
|
|
|
|
m_preModuleLoadHandler.Connect(m_moduleManager->m_preModuleLoadEvent);
|
|
}
|
|
|
|
AZ::ComponentTypeList AssetProcessorAZApplication::GetRequiredSystemComponents() const
|
|
{
|
|
AZ::ComponentTypeList components = AzFramework::Application::GetRequiredSystemComponents();
|
|
|
|
for (auto iter = components.begin(); iter != components.end();)
|
|
{
|
|
if (*iter == azrtti_typeid<AzFramework::AssetSystem::AssetSystemComponent>() // AP does not need asset system component to handle AssetRequestBus calls
|
|
|| *iter == azrtti_typeid<AzFramework::AssetCatalogComponent>() // AP will use its own AssetCatalogComponent
|
|
|| *iter == AZ::Uuid("{624a7be2-3c7e-4119-aee2-1db2bdb6cc89}") // ScriptDebugAgent
|
|
|| *iter == AZ::Uuid("{CAF3A025-FAC9-4537-B99E-0A800A9326DF}") // InputSystemComponent
|
|
|| *iter == azrtti_typeid<AssetProcessor::ToolsAssetCatalogComponent>()
|
|
)
|
|
{
|
|
// AP does not require the above components to be active
|
|
iter = components.erase(iter);
|
|
}
|
|
else
|
|
{
|
|
++iter;
|
|
}
|
|
}
|
|
|
|
components.push_back(azrtti_typeid<AzToolsFramework::PerforceComponent>());
|
|
components.push_back(azrtti_typeid<AzToolsFramework::Prefab::PrefabSystemComponent>());
|
|
|
|
return components;
|
|
}
|
|
|
|
void AssetProcessorAZApplication::RegisterCoreComponents()
|
|
{
|
|
AzToolsFramework::ToolsApplication::RegisterCoreComponents();
|
|
|
|
RegisterComponentDescriptor(AzToolsFramework::EditorEntityFixupComponent::CreateDescriptor());
|
|
RegisterComponentDescriptor(AzToolsFramework::AssetBrowser::AssetBrowserComponent::CreateDescriptor());
|
|
}
|
|
|
|
void AssetProcessorAZApplication::ResolveModulePath(AZ::OSString& modulePath)
|
|
{
|
|
AssetProcessor::AssetProcessorStatusEntry entry(AssetProcessor::AssetProcessorStatus::Initializing_Gems, 0, QString(modulePath.c_str()));
|
|
Q_EMIT AssetProcessorStatus(entry);
|
|
|
|
AzFramework::Application::ResolveModulePath(modulePath);
|
|
}
|
|
|
|
void AssetProcessorAZApplication::SetSettingsRegistrySpecializations(AZ::SettingsRegistryInterface::Specializations& specializations)
|
|
{
|
|
AzToolsFramework::ToolsApplication::SetSettingsRegistrySpecializations(specializations);
|
|
specializations.Append("assetprocessor");
|
|
}
|
|
|
|
ApplicationManager::ApplicationManager(int* argc, char*** argv, QObject* parent)
|
|
: QObject(parent)
|
|
, m_frameworkApp(argc, argv)
|
|
{
|
|
qInstallMessageHandler(&AssetProcessor::MessageHandler);
|
|
}
|
|
|
|
ApplicationManager::~ApplicationManager()
|
|
{
|
|
// if any of the threads are running, destroy them
|
|
// Any QObjects that have these ThreadWorker as parent will also be deleted
|
|
if (m_runningThreads.size())
|
|
{
|
|
for (int idx = 0; idx < m_runningThreads.size(); idx++)
|
|
{
|
|
m_runningThreads.at(idx)->Destroy();
|
|
}
|
|
}
|
|
for (int idx = 0; idx < m_appDependencies.size(); idx++)
|
|
{
|
|
delete m_appDependencies[idx];
|
|
}
|
|
|
|
qInstallMessageHandler(nullptr);
|
|
|
|
//deleting QCoreApplication/QApplication
|
|
delete m_qApp;
|
|
|
|
if (m_entity)
|
|
{
|
|
//Deactivate all the components
|
|
m_entity->Deactivate();
|
|
delete m_entity;
|
|
m_entity = nullptr;
|
|
}
|
|
|
|
//Unregistering and deleting all the components
|
|
EBUS_EVENT_ID(azrtti_typeid<AzFramework::LogComponent>(), AZ::ComponentDescriptorBus, ReleaseDescriptor);
|
|
|
|
//Stop AZFramework
|
|
m_frameworkApp.Stop();
|
|
AZ::Debug::Trace::HandleExceptions(false);
|
|
}
|
|
|
|
bool ApplicationManager::InitiatedShutdown() const
|
|
{
|
|
return m_duringShutdown;
|
|
}
|
|
|
|
void ApplicationManager::GetExternalBuilderFileList(QStringList& externalBuilderModules)
|
|
{
|
|
externalBuilderModules.clear();
|
|
|
|
static const char* builder_folder_name = "Builders";
|
|
|
|
// LY_ASSET_BUILDERS is defined by the CMakeLists.txt. The asset builders add themselves to a variable that
|
|
// is populated to allow selective building of those asset builder targets.
|
|
// This allows left over Asset builders in the output directory to not be loaded by the AssetProcessor
|
|
#if !defined(LY_ASSET_BUILDERS)
|
|
#error LY_ASSET_BUILDERS was not defined for ApplicationManager.cpp
|
|
#endif
|
|
|
|
QDir builderDir = QDir::toNativeSeparators(QString(this->m_frameworkApp.GetExecutableFolder()));
|
|
builderDir.cd(QString(builder_folder_name));
|
|
if (builderDir.exists())
|
|
{
|
|
AZStd::vector<AZStd::string> tokens;
|
|
AZ::StringFunc::Tokenize(AZStd::string_view(LY_ASSET_BUILDERS), tokens, ',');
|
|
AZStd::string builderLibrary;
|
|
for (const AZStd::string& token : tokens)
|
|
{
|
|
QString assetBuilderPath(token.c_str());
|
|
if (builderDir.exists(assetBuilderPath))
|
|
{
|
|
externalBuilderModules.push_back(builderDir.absoluteFilePath(assetBuilderPath));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (externalBuilderModules.empty())
|
|
{
|
|
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "AssetProcessor was unable to locate any external builders\n");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
QDir ApplicationManager::GetSystemRoot() const
|
|
{
|
|
return m_systemRoot;
|
|
}
|
|
QString ApplicationManager::GetProjectPath() const
|
|
{
|
|
auto projectPath = AZ::Utils::GetProjectPath();
|
|
if (!projectPath.empty())
|
|
{
|
|
return QString::fromUtf8(projectPath.c_str(), aznumeric_cast<int>(projectPath.size()));
|
|
}
|
|
|
|
AZ_Warning("AssetUtils", false, "Unable to obtain the Project Path from the settings registry.");
|
|
return {};
|
|
}
|
|
|
|
QCoreApplication* ApplicationManager::GetQtApplication()
|
|
{
|
|
return m_qApp;
|
|
}
|
|
|
|
void ApplicationManager::RegisterObjectForQuit(QObject* source, bool insertInFront)
|
|
{
|
|
Q_ASSERT(!m_duringShutdown);
|
|
|
|
if (m_duringShutdown)
|
|
{
|
|
AZ_Warning(AssetProcessor::DebugChannel, false, "You may not register objects for quit during shutdown.\n");
|
|
return;
|
|
}
|
|
|
|
QuitPair quitPair(source, false);
|
|
if (!m_objectsToNotify.contains(quitPair))
|
|
{
|
|
if (insertInFront)
|
|
{
|
|
m_objectsToNotify.push_front(quitPair);
|
|
}
|
|
else
|
|
{
|
|
m_objectsToNotify.push_back(quitPair);
|
|
}
|
|
if (!connect(source, SIGNAL(ReadyToQuit(QObject*)), this, SLOT(ReadyToQuit(QObject*))))
|
|
{
|
|
AZ_Warning(AssetProcessor::DebugChannel, false, "ApplicationManager::RegisterObjectForQuit was passed an object of type %s which has no ReadyToQuit(QObject*) signal.\n", source->metaObject()->className());
|
|
}
|
|
connect(source, SIGNAL(destroyed(QObject*)), this, SLOT(ObjectDestroyed(QObject*)));
|
|
}
|
|
}
|
|
|
|
void ApplicationManager::ObjectDestroyed(QObject* source)
|
|
{
|
|
for (int notifyIdx = 0; notifyIdx < m_objectsToNotify.size(); ++notifyIdx)
|
|
{
|
|
if (m_objectsToNotify[notifyIdx].first == source)
|
|
{
|
|
m_objectsToNotify.erase(m_objectsToNotify.begin() + notifyIdx);
|
|
if (m_duringShutdown)
|
|
{
|
|
if (!m_queuedCheckQuit)
|
|
{
|
|
QTimer::singleShot(0, this, SLOT(CheckQuit()));
|
|
m_queuedCheckQuit = true;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ApplicationManager::QuitRequested()
|
|
{
|
|
if (m_duringShutdown)
|
|
{
|
|
AZ_TracePrintf(AssetProcessor::DebugChannel, "QuitRequested() - already during shutdown\n");
|
|
return;
|
|
}
|
|
|
|
if (m_duringStartup)
|
|
{
|
|
AZ_TracePrintf(AssetProcessor::DebugChannel, "QuitRequested() - during startup - waiting\n");
|
|
// if we're still starting up, spin until we're ready to shut down.
|
|
QMetaObject::invokeMethod(this, "QuitRequested", Qt::QueuedConnection);
|
|
return;
|
|
}
|
|
AZ_TracePrintf(AssetProcessor::DebugChannel, "QuitRequested() - ready!\n");
|
|
m_duringShutdown = true;
|
|
|
|
//Inform all the builders to shutdown
|
|
EBUS_EVENT(AssetBuilderSDK::AssetBuilderCommandBus, ShutDown);
|
|
|
|
// the following call will invoke on the main thread of the application, since its a direct bus call.
|
|
EBUS_EVENT(AssetProcessor::ApplicationManagerNotifications::Bus, ApplicationShutdownRequested);
|
|
|
|
// while it may be tempting to just collapse all of this to a bus call, Qt Objects have the advantage of being
|
|
// able to automatically queue calls onto their own thread, and a lot of these involved objects are in fact
|
|
// on their own threads. So even if we used a bus call we'd ultimately still have to invoke a queued
|
|
// call there anyway.
|
|
|
|
for (const QuitPair& quitter : m_objectsToNotify)
|
|
{
|
|
if (!quitter.second)
|
|
{
|
|
QMetaObject::invokeMethod(quitter.first, "QuitRequested", Qt::QueuedConnection);
|
|
}
|
|
}
|
|
|
|
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "App quit requested %d listeners notified.\n", m_objectsToNotify.size());
|
|
|
|
if (!m_queuedCheckQuit)
|
|
{
|
|
QTimer::singleShot(0, this, SLOT(CheckQuit()));
|
|
m_queuedCheckQuit = true;
|
|
}
|
|
}
|
|
|
|
void ApplicationManager::CheckQuit()
|
|
{
|
|
m_queuedCheckQuit = false;
|
|
for (const QuitPair& quitter : m_objectsToNotify)
|
|
{
|
|
if (!quitter.second)
|
|
{
|
|
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "App Quit: Object of type %s is not yet ready to quit.\n", quitter.first->metaObject()->className());
|
|
return;
|
|
}
|
|
}
|
|
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "App quit requested, and all objects are ready. Quitting app.\n");
|
|
|
|
// We will now loop over all the running threads and destroy them
|
|
// Any QObjects that have these ThreadWorker as parent will also be deleted
|
|
for (int idx = 0; idx < m_runningThreads.size(); idx++)
|
|
{
|
|
m_runningThreads.at(idx)->Destroy();
|
|
}
|
|
|
|
m_runningThreads.clear();
|
|
// all good.
|
|
qApp->quit();
|
|
}
|
|
|
|
void ApplicationManager::CheckForUpdate()
|
|
{
|
|
for (int idx = 0; idx < m_appDependencies.size(); ++idx)
|
|
{
|
|
ApplicationDependencyInfo* fileDependencyInfo = m_appDependencies[idx];
|
|
QString fileName = fileDependencyInfo->FileName();
|
|
QFileInfo fileInfo(fileName);
|
|
if (fileInfo.exists())
|
|
{
|
|
QDateTime fileLastModifiedTime = fileInfo.lastModified();
|
|
bool hasTimestampChanged = (fileDependencyInfo->Timestamp() != fileLastModifiedTime);
|
|
if (hasTimestampChanged)
|
|
{
|
|
QuitRequested();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if one of the files is not present we construct a null datetime for it and
|
|
// continue checking
|
|
fileDependencyInfo->SetTimestamp(QDateTime());
|
|
}
|
|
}
|
|
}
|
|
|
|
void ApplicationManager::PopulateApplicationDependencies()
|
|
{
|
|
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(CheckForUpdate()));
|
|
m_updateTimer.start(5000);
|
|
|
|
QString currentDir(QCoreApplication::applicationDirPath());
|
|
QDir dir(currentDir);
|
|
QString applicationPath = QCoreApplication::applicationFilePath();
|
|
|
|
m_filesOfInterest.push_back(applicationPath);
|
|
|
|
// add some known-dependent files (this can be removed when they are no longer a dependency)
|
|
// Note that its not necessary for any of these files to actually exist. It is considered a "change" if they
|
|
// change their file modtime, or if they go from existing to not existing, or if they go from not existing, to existing.
|
|
// any of those should cause AP to drop.
|
|
for (const QString& pathName : { "CrySystem",
|
|
"SceneCore", "SceneData",
|
|
"FbxSceneBuilder", "AzQtComponents"
|
|
})
|
|
{
|
|
QString pathWithPlatformExtension = pathName + QString(AZ_DYNAMIC_LIBRARY_EXTENSION);
|
|
m_filesOfInterest.push_back(dir.absoluteFilePath(pathWithPlatformExtension));
|
|
}
|
|
|
|
// Get the external builder modules to add to the files of interest
|
|
QStringList builderModuleFileList;
|
|
GetExternalBuilderFileList(builderModuleFileList);
|
|
for (const QString& builderModuleFile : builderModuleFileList)
|
|
{
|
|
m_filesOfInterest.push_back(builderModuleFile);
|
|
}
|
|
|
|
|
|
QDir assetRoot;
|
|
AssetUtilities::ComputeAssetRoot(assetRoot);
|
|
|
|
QString globalConfigPath = assetRoot.filePath("Registry/AssetProcessorPlatformConfig.setreg");
|
|
m_filesOfInterest.push_back(globalConfigPath);
|
|
|
|
QString gamePlatformConfigPath = QDir(AssetUtilities::ComputeProjectPath()).filePath("AssetProcessorGamePlatformConfig.setreg");
|
|
m_filesOfInterest.push_back(gamePlatformConfigPath);
|
|
|
|
// add app modules
|
|
AZ::ModuleManagerRequestBus::Broadcast(&AZ::ModuleManagerRequestBus::Events::EnumerateModules,
|
|
[this](const AZ::ModuleData& moduleData)
|
|
{
|
|
AZ::DynamicModuleHandle* handle = moduleData.GetDynamicModuleHandle();
|
|
if (handle)
|
|
{
|
|
QFileInfo fi(handle->GetFilename().c_str());
|
|
if (fi.exists())
|
|
{
|
|
m_filesOfInterest.push_back(fi.absoluteFilePath());
|
|
}
|
|
}
|
|
return true; // keep iterating.
|
|
});
|
|
|
|
//find timestamps of all the files
|
|
for (int idx = 0; idx < m_filesOfInterest.size(); idx++)
|
|
{
|
|
QString fileName = m_filesOfInterest.at(idx);
|
|
QFileInfo fileInfo(fileName);
|
|
QDateTime fileLastModifiedTime = fileInfo.lastModified();
|
|
ApplicationDependencyInfo* applicationDependencyInfo = new ApplicationDependencyInfo(fileName, fileLastModifiedTime);
|
|
// if some file does not exist than null datetime will be stored
|
|
m_appDependencies.push_back(applicationDependencyInfo);
|
|
}
|
|
}
|
|
|
|
bool ApplicationManager::StartAZFramework()
|
|
{
|
|
AzFramework::Application::Descriptor appDescriptor;
|
|
AZ::ComponentApplication::StartupParameters params;
|
|
|
|
QString projectName = AssetUtilities::ComputeProjectName();
|
|
|
|
// Prevent loading of gems in the Create method of the ComponentApplication
|
|
params.m_loadDynamicModules = false;
|
|
|
|
// Prevent script reflection warnings from bringing down the AssetProcessor
|
|
appDescriptor.m_enableScriptReflection = false;
|
|
// start listening for exceptions occurring so if something goes wrong we have at least SOME output...
|
|
AZ::Debug::Trace::HandleExceptions(true);
|
|
|
|
m_frameworkApp.Start(appDescriptor, params);
|
|
|
|
//Registering all the Components
|
|
m_frameworkApp.RegisterComponentDescriptor(AzFramework::LogComponent::CreateDescriptor());
|
|
|
|
|
|
Reflect();
|
|
|
|
const AzFramework::CommandLine* commandLine = nullptr;
|
|
AzFramework::ApplicationRequests::Bus::BroadcastResult(commandLine, &AzFramework::ApplicationRequests::GetCommandLine);
|
|
if (commandLine && commandLine->HasSwitch("logDir"))
|
|
{
|
|
AZ::IO::FileIOBase::GetInstance()->SetAlias("@log@", commandLine->GetSwitchValue("logDir", 0).c_str());
|
|
}
|
|
else if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
|
{
|
|
AZ::IO::Path projectUserPath;
|
|
settingsRegistry->Get(projectUserPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectUserPath);
|
|
|
|
AZ::IO::Path logUserPath = projectUserPath / "log";
|
|
auto fileIo = AZ::IO::FileIOBase::GetInstance();
|
|
fileIo->SetAlias("@log@", logUserPath.c_str());
|
|
}
|
|
m_entity = aznew AZ::Entity("Application Entity");
|
|
if (m_entity)
|
|
{
|
|
AssetProcessor::FilteredLogComponent* logger = aznew AssetProcessor::FilteredLogComponent();
|
|
m_entity->AddComponent(logger);
|
|
if (logger)
|
|
{
|
|
// Prevent files overwriting each other if you run batch at same time as GUI (unit tests, for example)
|
|
logger->SetLogFileBaseName(GetLogBaseName());
|
|
}
|
|
|
|
//Activate all the components
|
|
m_entity->Init();
|
|
m_entity->Activate();
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
//aznew failed
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool ApplicationManager::ActivateModules()
|
|
{
|
|
// we load the editor xml for our modules since it contains the list of gems we need for tools to function (not just runtime)
|
|
connect(&m_frameworkApp, &AssetProcessorAZApplication::AssetProcessorStatus, this,
|
|
[this](AssetProcessor::AssetProcessorStatusEntry entry)
|
|
{
|
|
Q_EMIT AssetProcessorStatusChanged(entry);
|
|
QCoreApplication::processEvents(QEventLoop::AllEvents);
|
|
});
|
|
|
|
QDir assetRoot;
|
|
if (!AssetUtilities::ComputeAssetRoot(assetRoot))
|
|
{
|
|
AZ_Error(AssetProcessor::ConsoleChannel, false, "Cannot compute the asset root folder. Is AssetProcessor being run from the appropriate folder?");
|
|
return false;
|
|
}
|
|
|
|
m_frameworkApp.LoadDynamicModules();
|
|
return true;
|
|
}
|
|
|
|
void ApplicationManager::addRunningThread(AssetProcessor::ThreadWorker* thread)
|
|
{
|
|
m_runningThreads.push_back(thread);
|
|
}
|
|
|
|
|
|
ApplicationManager::BeforeRunStatus ApplicationManager::BeforeRun()
|
|
{
|
|
// Create the Qt Application
|
|
CreateQtApplication();
|
|
|
|
if (!StartAZFramework())
|
|
{
|
|
return ApplicationManager::BeforeRunStatus::Status_Failure;
|
|
}
|
|
|
|
if (!AssetUtilities::ComputeEngineRoot(m_systemRoot))
|
|
{
|
|
return ApplicationManager::BeforeRunStatus::Status_Failure;
|
|
}
|
|
|
|
if (!AssetUtilities::UpdateBranchToken())
|
|
{
|
|
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Asset Processor was unable to open the bootstrap file and verify/update the branch token. \
|
|
Please ensure that the bootstrap.cfg file is present and not locked by any other program.\n");
|
|
return ApplicationManager::BeforeRunStatus::Status_Failure;
|
|
}
|
|
|
|
return ApplicationManager::BeforeRunStatus::Status_Success;
|
|
}
|
|
|
|
bool ApplicationManager::Activate()
|
|
{
|
|
if (!AssetUtilities::ComputeAssetRoot(m_systemRoot))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
auto projectName = AssetUtilities::ComputeProjectName();
|
|
if (projectName.isEmpty())
|
|
{
|
|
AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable to detect name of current game project. Is bootstrap.cfg appropriately configured?");
|
|
return false;
|
|
}
|
|
|
|
// the following controls what registry keys (or on mac or linux what entries in home folder) are used
|
|
// so they should not be translated!
|
|
qApp->setOrganizationName(GetOrganizationName());
|
|
qApp->setOrganizationDomain("amazon.com");
|
|
qApp->setApplicationName(GetApplicationName());
|
|
|
|
return true;
|
|
}
|
|
|
|
QString ApplicationManager::GetOrganizationName() const
|
|
{
|
|
return "Amazon";
|
|
}
|
|
|
|
QString ApplicationManager::GetApplicationName() const
|
|
{
|
|
return "Asset Processor";
|
|
}
|
|
|
|
bool ApplicationManager::PostActivate()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool ApplicationManager::NeedRestart() const
|
|
{
|
|
return m_needRestart;
|
|
}
|
|
|
|
void ApplicationManager::Restart()
|
|
{
|
|
if (m_needRestart)
|
|
{
|
|
AZ_TracePrintf(AssetProcessor::DebugChannel, "Restart() - already restarting\n");
|
|
return;
|
|
}
|
|
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "AssetProcessor is restarting.\n");
|
|
m_needRestart = true;
|
|
m_updateTimer.stop();
|
|
QuitRequested();
|
|
}
|
|
|
|
void ApplicationManager::ReadyToQuit(QObject* source)
|
|
{
|
|
if (!source)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "App Quit Object of type %s indicates it is ready.\n", source->metaObject()->className());
|
|
|
|
for (int notifyIdx = 0; notifyIdx < m_objectsToNotify.size(); ++notifyIdx)
|
|
{
|
|
if (m_objectsToNotify[notifyIdx].first == source)
|
|
{
|
|
// replace it.
|
|
m_objectsToNotify[notifyIdx] = QuitPair(m_objectsToNotify[notifyIdx].first, true);
|
|
}
|
|
}
|
|
if (!m_queuedCheckQuit)
|
|
{
|
|
QTimer::singleShot(0, this, SLOT(CheckQuit()));
|
|
m_queuedCheckQuit = true;
|
|
}
|
|
}
|
|
|
|
void ApplicationManager::RegisterComponentDescriptor(const AZ::ComponentDescriptor* descriptor)
|
|
{
|
|
AZ_Assert(descriptor, "descriptor cannot be null");
|
|
this->m_frameworkApp.RegisterComponentDescriptor(descriptor);
|
|
}
|
|
|
|
ApplicationManager::RegistryCheckInstructions ApplicationManager::CheckForRegistryProblems(QWidget* /*parentWidget*/, [[maybe_unused]] bool showPopupMessage)
|
|
{
|
|
#if defined(AZ_PLATFORM_WINDOWS)
|
|
// There's a bug that prevents rc.exe from closing properly, making it appear
|
|
// that jobs never complete. The issue is that Windows sometimes decides to put
|
|
// an exe into a special compatibility mode and tells FreeLibrary calls to stop
|
|
// doing anything. Once the registry entry for this is written, it never gets
|
|
// removed unless the user goes and does it manually in RegEdit.
|
|
// To prevent this from being a problem, we check for that registry key
|
|
// and tell the user to remove it.
|
|
// 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
|
|
// 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
|
|
|
|
QString compatibilityRegistryGroupName = "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers";
|
|
QSettings settings(compatibilityRegistryGroupName, QSettings::NativeFormat);
|
|
|
|
auto keys = settings.childKeys();
|
|
for (auto key : keys)
|
|
{
|
|
if (key.contains("rc.exe", Qt::CaseInsensitive))
|
|
{
|
|
// Windows will allow us to see that there is an entry, but won't allow us to
|
|
// read the entry or to modify it, so we'll have to warn the user instead
|
|
|
|
// Qt displays the key with the slashes flipped; flip them back because we're on windows
|
|
QString windowsFriendlyRegPath = key.replace('/', '\\');
|
|
|
|
QString warningText = QObject::tr(
|
|
"The AssetProcessor will not function correctly with certain registry settings. To correct the problem, please:\n"
|
|
"1) Open RegEdit\n"
|
|
"2) When Windows asks if you'd like to allow the app to make changes to your device, click \"Yes\"\n"
|
|
"3) Open the registry group for the path %0\n"
|
|
"4) Delete the key for %1\n"
|
|
"5) %2"
|
|
).arg(compatibilityRegistryGroupName, windowsFriendlyRegPath);
|
|
if (showPopupMessage)
|
|
{
|
|
return PopupRegistryProblemsMessage(warningText);
|
|
}
|
|
else
|
|
{
|
|
warningText = warningText.arg(tr("Restart the Asset Processor"));
|
|
QByteArray warningUtf8 = warningText.toUtf8();
|
|
AZ_TracePrintf(AssetProcessor::ConsoleChannel, warningUtf8.data());
|
|
}
|
|
|
|
return RegistryCheckInstructions::Exit;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return RegistryCheckInstructions::Continue;
|
|
}
|
|
|
|
|
|
QDateTime ApplicationDependencyInfo::Timestamp() const
|
|
{
|
|
return m_timestamp;
|
|
}
|
|
|
|
void ApplicationDependencyInfo::SetTimestamp(const QDateTime& timestamp)
|
|
{
|
|
m_timestamp = timestamp;
|
|
}
|
|
|
|
QString ApplicationDependencyInfo::FileName() const
|
|
{
|
|
return m_fileName;
|
|
}
|
|
|
|
void ApplicationDependencyInfo::SetFileName(QString fileName)
|
|
{
|
|
m_fileName = fileName;
|
|
}
|
|
|
|
|