Integrating latest from github/staging

Integrating up through commit 5e1bdae
This commit is contained in:
alexpete
2021-03-26 14:31:50 -07:00
parent 9c54341af8
commit 36c4e827bd
764 changed files with 11453 additions and 20251 deletions
@@ -13,6 +13,7 @@
#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>
@@ -83,7 +84,7 @@ namespace AssetProcessor
// 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;
return;
}
AzFramework::LogComponent::OutputMessage(severity, window, message);
@@ -119,29 +120,14 @@ AssetProcessorAZApplication::AssetProcessorAZApplication(int* argc, char*** argv
*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 = 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);
// Override the /Amazon/AzCore/Bootstrap/sys_game_folder entry in the Settings Registry using the -gamefolder parameter
auto settingsRegistry = AZ::SettingsRegistry::Get();
if (m_commandLine.GetNumSwitchValues("gamefolder") > 0)
{
const AZStd::string& gameFolderOverride = m_commandLine.GetSwitchValue("gamefolder", 0);
auto gameFolderCommandLineOverride = AZStd::string::format("--regset=%s/sys_game_folder=%s", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey,
gameFolderOverride.c_str());
AZ::CommandLine::ParamContainer commandLineArgs;
m_commandLine.Dump(commandLineArgs);
commandLineArgs.emplace_back(gameFolderCommandLineOverride);
m_commandLine.Parse(commandLineArgs);
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*settingsRegistry, m_commandLine, false);
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry);
}
}
AZ::ComponentTypeList AssetProcessorAZApplication::GetRequiredSystemComponents() const
@@ -155,7 +141,7 @@ AZ::ComponentTypeList AssetProcessorAZApplication::GetRequiredSystemComponents()
|| *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);
@@ -274,7 +260,7 @@ void ApplicationManager::GetExternalBuilderFileList(QStringList& externalBuilder
if (externalBuilderModules.empty())
{
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "AssetProcessor was unable to locate any builders\n");
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "AssetProcessor was unable to locate any external builders\n");
}
}
@@ -284,9 +270,16 @@ QDir ApplicationManager::GetSystemRoot() const
{
return m_systemRoot;
}
QString ApplicationManager::GetGameName() const
QString ApplicationManager::GetProjectPath() const
{
return m_gameName;
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()
@@ -451,7 +444,7 @@ void ApplicationManager::PopulateApplicationDependencies()
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
// 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",
@@ -475,11 +468,10 @@ void ApplicationManager::PopulateApplicationDependencies()
QDir assetRoot;
AssetUtilities::ComputeAssetRoot(assetRoot);
QString globalConfigPath = assetRoot.filePath("AssetProcessorPlatformConfig.ini");
QString globalConfigPath = assetRoot.filePath("AssetProcessorPlatformConfig.setreg");
m_filesOfInterest.push_back(globalConfigPath);
QString gameName = AssetUtilities::ComputeGameName();
QString gamePlatformConfigPath = assetRoot.filePath(gameName + "/AssetProcessorGamePlatformConfig.ini");
QString gamePlatformConfigPath = QDir(AssetUtilities::ComputeProjectPath()).filePath("AssetProcessorGamePlatformConfig.setreg");
m_filesOfInterest.push_back(gamePlatformConfigPath);
// add app modules
@@ -510,43 +502,14 @@ void ApplicationManager::PopulateApplicationDependencies()
}
}
bool ApplicationManager::StartAZFramework(QString appRootOverride)
bool ApplicationManager::StartAZFramework()
{
AzFramework::Application::Descriptor appDescriptor;
AZ::ComponentApplication::StartupParameters params;
QString gameName = AssetUtilities::ComputeGameName();
QString projectName = AssetUtilities::ComputeProjectName();
AZ::SettingsRegistryInterface& registry = *AZ::SettingsRegistry::Get();
// Add the supplied gameName as specialization key in the registry
if (!gameName.isEmpty())
{
auto gameNameSpecialization = QString("%1/%2").arg(AZ::SettingsRegistryMergeUtils::SpecializationsRootKey).arg(gameName);
QByteArray specializationByteArray = gameNameSpecialization.toUtf8();
registry.Set(AZStd::string_view(specializationByteArray.data(), specializationByteArray.size()), true);
}
else
{
// Add the project name as a registry specialization
auto projectKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/sys_game_folder", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey);
if (AZ::SettingsRegistryInterface::FixedValueString bootstrapProjectName; registry.Get(bootstrapProjectName, projectKey) && !bootstrapProjectName.empty())
{
registry.Set(AZ::SettingsRegistryInterface::FixedValueString::format("%s/%s",
AZ::SettingsRegistryMergeUtils::SpecializationsRootKey, bootstrapProjectName.c_str()),
true);
}
}
// The application will live in a bin folder one level up from the app root.
static char s_storageForRootPath[AZ_MAX_PATH_LEN] = { 0 };
if (!appRootOverride.isEmpty())
{
azstrcpy(s_storageForRootPath, AZ_MAX_PATH_LEN, appRootOverride.toUtf8().data());
params.m_appRootOverride = s_storageForRootPath;
}
else
{
params.m_appRootOverride = nullptr;
}
// Prevent loading of gems in the Create method of the ComponentApplication
params.m_loadDynamicModules = false;
@@ -556,31 +519,27 @@ bool ApplicationManager::StartAZFramework(QString appRootOverride)
AZ::Debug::Trace::HandleExceptions(true);
m_frameworkApp.Start(appDescriptor, params);
//Registering all the Components
m_frameworkApp.RegisterComponentDescriptor(AzFramework::LogComponent::CreateDescriptor());
Reflect();
QDir engineRoot;
AssetUtilities::ComputeEngineRoot(engineRoot);
AZ::IO::FileIOBase::GetInstance()->SetAlias("@devroot@", engineRoot.absolutePath().toUtf8().data());
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
else if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
char executableDirectory[AZ_MAX_PATH_LEN];
if (AZ::Utils::GetExecutableDirectory(executableDirectory, AZStd::size(executableDirectory)) == AZ::Utils::ExecutablePathResult::Success)
{
AZ::IO::FileIOBase::GetInstance()->SetAlias("@log@", executableDirectory);
}
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)
@@ -622,7 +581,6 @@ bool ApplicationManager::ActivateModules()
AZ_Error(AssetProcessor::ConsoleChannel, false, "Cannot compute the asset root folder. Is AssetProcessor being run from the appropriate folder?");
return false;
}
assetRoot.cd(AssetUtilities::ComputeGameName());
m_frameworkApp.LoadDynamicModules();
return true;
@@ -633,89 +591,13 @@ void ApplicationManager::addRunningThread(AssetProcessor::ThreadWorker* thread)
m_runningThreads.push_back(thread);
}
QString ApplicationManager::ParseOptionAppRootArgument()
{
AZ_Assert(m_qApp!=nullptr,"m_qApp not initialized. QT application must be created before this call.")
// Parse any parameters.
static const char* app_root_parameter = "app-root";
static const char* app_root_parameter_desc = "Optional external path outside of the current engine to set as the application root.";
QCommandLineOption appRootPathOption(QString(app_root_parameter), tr(app_root_parameter_desc), QString("path"));
QCommandLineParser parser;
parser.setApplicationDescription("Asset Processor");
parser.addOption(appRootPathOption);
parser.parse(m_qApp->arguments());
QString appRootArgValue = parser.value(appRootPathOption);
appRootArgValue.remove(QChar('\"'));
return appRootArgValue.trimmed();
}
bool ApplicationManager::ValidateExternalAppRoot(QString appRootPath) const
{
static const char* bootstrap_cfg_name = "bootstrap.cfg";
QDir testAppRootPath(appRootPath);
// Make sure the path exists
if (!testAppRootPath.exists())
{
AZ_Warning(AssetProcessor::ConsoleChannel, testAppRootPath.exists(), "Invalid Application Root path override (--app-root): %s. Directory does not exist.\n", appRootPath.toUtf8().data());
return false;
}
// Make sure the path contains bootstrap.cfg
if (!testAppRootPath.exists(bootstrap_cfg_name))
{
AZ_Warning(AssetProcessor::ConsoleChannel, testAppRootPath.exists(), "Invalid Application Root path override (--app-root): %s. Directory does not contain %s.\n", appRootPath.toUtf8().data(), bootstrap_cfg_name);
return false;
}
// Make sure we can read the 'sys_game_folder' settings from bootstrap.cfg
QSettings settings(testAppRootPath.absoluteFilePath(bootstrap_cfg_name), QSettings::Format::IniFormat);
static const char* sysGameFolderKeyName = "sys_game_folder";
auto sysGameFolderSettings = settings.value(sysGameFolderKeyName);
if (!sysGameFolderSettings.isValid() || sysGameFolderSettings.isNull())
{
AZ_Warning(AssetProcessor::ConsoleChannel, testAppRootPath.exists(), "Invalid Application Root path override (--app-root): %s. %s in the path is not valid.\n", appRootPath.toUtf8().data(), bootstrap_cfg_name);
return false;
}
// Make sure the 'sys_game_folder' value in the external bootstrap.cfg points to a valid subfolder in that path
QString sysGameFolder = sysGameFolderSettings.toString();
QDir gameFolderPath(appRootPath);
if (!gameFolderPath.cd(sysGameFolder))
{
AZ_Warning(AssetProcessor::ConsoleChannel, testAppRootPath.exists(), "Invalid Application Root path override (--app-root): %s. Configured Game folder %s in the path is not valid.\n", appRootPath.toUtf8().data(), sysGameFolder.toUtf8().data());
return false;
}
return true;
}
ApplicationManager::BeforeRunStatus ApplicationManager::BeforeRun()
{
// Create the Qt Application
CreateQtApplication();
// Calculate the override app root path if provided and validate it before passing it along
QString overrideAppRootPath = ParseOptionAppRootArgument();
if (!overrideAppRootPath.isEmpty())
{
if (ValidateExternalAppRoot(overrideAppRootPath))
{
QDir overrideAppRoot(overrideAppRootPath);
QDir resultAppRoot;
AssetUtilities::ComputeAssetRoot(resultAppRoot, &overrideAppRoot);
}
else
{
AZ_Error(AssetProcessor::ConsoleChannel, false, "Invalid override app root folder '%s'.", overrideAppRootPath.toUtf8().data());
return ApplicationManager::BeforeRunStatus::Status_Failure;
}
}
if (!StartAZFramework(overrideAppRootPath))
if (!StartAZFramework())
{
return ApplicationManager::BeforeRunStatus::Status_Failure;
}
@@ -742,14 +624,13 @@ bool ApplicationManager::Activate()
return false;
}
m_gameName = AssetUtilities::ComputeGameName();
if (m_gameName.isEmpty())
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());
@@ -101,7 +101,7 @@ public:
QCoreApplication* GetQtApplication();
QDir GetSystemRoot() const;
QString GetGameName() const;
QString GetProjectPath() const;
void RegisterComponentDescriptor(const AZ::ComponentDescriptor* descriptor);
@@ -163,9 +163,7 @@ protected:
virtual const char* GetLogBaseName() = 0;
virtual RegistryCheckInstructions PopupRegistryProblemsMessage(QString warningText) = 0;
private:
bool StartAZFramework(QString appRootOverride);
bool ValidateExternalAppRoot(QString appRootPath) const;
QString ParseOptionAppRootArgument();
bool StartAZFramework();
// QuitPair - Object pointer and "is ready" boolean pair.
typedef QPair<QObject*, bool> QuitPair;
@@ -178,7 +176,6 @@ private:
bool m_needRestart = false;
bool m_queuedCheckQuit = false;
QDir m_systemRoot;
QString m_gameName;
AZ::Entity* m_entity = nullptr;
};
@@ -337,13 +337,13 @@ bool ApplicationManagerBase::InitPlatformConfiguration()
m_platformConfiguration = new AssetProcessor::PlatformConfiguration();
QDir assetRoot;
AssetUtilities::ComputeAssetRoot(assetRoot);
return m_platformConfiguration->InitializeFromConfigFiles(GetSystemRoot().absolutePath(), assetRoot.absolutePath(), GetGameName());
return m_platformConfiguration->InitializeFromConfigFiles(GetSystemRoot().absolutePath(), assetRoot.absolutePath(), GetProjectPath());
}
bool ApplicationManagerBase::InitBuilderConfiguration()
{
m_builderConfig = AZStd::make_unique<AssetProcessor::BuilderConfigurationManager>();
QString configFile = GetSystemRoot().absoluteFilePath(GetGameName() + "/" + AssetProcessor::BuilderConfigFile);
QString configFile = QDir(GetProjectPath()).absoluteFilePath(AssetProcessor::BuilderConfigFile);
if (!QFile::exists(configFile))
{
@@ -1193,7 +1193,8 @@ bool ApplicationManagerBase::Activate()
return false;
}
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "AssetProcessor will process assets from gameproject %s.\n", AssetUtilities::ComputeGameName().toUtf8().data());
AZ_TracePrintf(AssetProcessor::ConsoleChannel,
"AssetProcessor will process assets from project root %s.\n", AssetUtilities::ComputeProjectPath().toUtf8().data());
// Shutdown if the disk has less than 128MB of free space
if (!CheckSufficientDiskSpace(projectCache.absolutePath(), 128 * 1024 * 1024, true))
@@ -1219,7 +1220,7 @@ bool ApplicationManagerBase::Activate()
if (!InitPlatformConfiguration())
{
AZ_Error("AssetProcessor", false, "Failed to Initialize from AssetProcessorPlatformConfig.ini - check the log files in the logs/ subfolder for more information.");
AZ_Error("AssetProcessor", false, "Failed to Initialize from AssetProcessorPlatformConfig.setreg - check the log files in the logs/ subfolder for more information.");
return false;
}
@@ -1401,7 +1402,7 @@ bool ApplicationManagerBase::InitializeExternalBuilders()
return true;
}
bool ApplicationManagerBase::WaitForBuilderExit(AzToolsFramework::ProcessWatcher* processWatcher, AssetBuilderSDK::JobCancelListener* jobCancelListener, AZ::u32 processTimeoutLimitInSeconds)
bool ApplicationManagerBase::WaitForBuilderExit(AzFramework::ProcessWatcher* processWatcher, AssetBuilderSDK::JobCancelListener* jobCancelListener, AZ::u32 processTimeoutLimitInSeconds)
{
AZ::u32 exitCode = 0;
bool finishedOK = false;
@@ -177,7 +177,7 @@ protected:
AssetProcessor::AssetCatalog* GetAssetCatalog() const { return m_assetCatalog; }
static bool WaitForBuilderExit(AzToolsFramework::ProcessWatcher* processWatcher, AssetBuilderSDK::JobCancelListener* jobCancelListener, AZ::u32 processTimeoutLimitInSeconds);
static bool WaitForBuilderExit(AzFramework::ProcessWatcher* processWatcher, AssetBuilderSDK::JobCancelListener* jobCancelListener, AZ::u32 processTimeoutLimitInSeconds);
ApplicationServer* m_applicationServer = nullptr;
ConnectionManager* m_connectionManager = nullptr;
@@ -142,10 +142,6 @@ namespace AssetProcessor
bool Builder::Start()
{
// Get the app root to locate the builders
AZStd::string appRootString;
AzFramework::ApplicationRequests::Bus::BroadcastResult(appRootString, &AzFramework::ApplicationRequests::GetAppRoot);
// Get the current BinXXX folder based on the current running AP
QString applicationDir = QCoreApplication::instance()->applicationDirPath();
@@ -190,24 +186,26 @@ namespace AssetProcessor
QDir projectCacheRoot;
AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot);
QString gameName = AssetUtilities::ComputeGameName();
AZStd::string appRootString;
AzFramework::ApplicationRequests::Bus::BroadcastResult(appRootString, &AzFramework::ApplicationRequests::GetAppRoot);
QDir appRoot(QString(appRootString.c_str()));
QString gameRoot = appRoot.absoluteFilePath(gameName);
QString gameName = AssetUtilities::ComputeProjectName();
QString projectPath = AssetUtilities::ComputeProjectPath();
QDir engineRoot;
AssetUtilities::ComputeEngineRoot(engineRoot);
int portNumber = 0;
ApplicationServerBus::BroadcastResult(portNumber, &ApplicationServerBus::Events::GetServerListeningPort);
AZStd::string params;
#if !AZ_TRAIT_OS_PLATFORM_APPLE && !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
params = AZStd::string::format(R"(-task=%s -id="%s" -gamename="%s" -gamecache="%s" -gameroot="%s" -port %d)",
task, builderGuid.c_str(), gameName.toUtf8().constData(), projectCacheRoot.absolutePath().toUtf8().constData(), gameRoot.toUtf8().constData(), portNumber);
#else
params = AZStd::string::format(R"(-task=%s -id="%s" -gamename="\"%s\"" -gamecache="\"%s\"" -gameroot="\"%s\"" -port %d)",
task, builderGuid.c_str(), gameName.toUtf8().constData(), projectCacheRoot.absolutePath().toUtf8().constData(), gameRoot.toUtf8().constData(), portNumber);
#endif // !AZ_TRAIT_OS_PLATFORM_APPLE && !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
#if !AZ_TRAIT_OS_PLATFORM_APPLE && !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
params = AZStd::string::format(
R"(-task=%s -id="%s" -project-name="%s" -project-cache-path="%s" -project-path="%s" -engine-path="%s" -port %d)", task,
builderGuid.c_str(), gameName.toUtf8().constData(), projectCacheRoot.absolutePath().toUtf8().constData(),
projectPath.toUtf8().constData(), engineRoot.absolutePath().toUtf8().constData(), portNumber);
#else
params = AZStd::string::format(
R"(-task=%s -id="%s" -project-name="\"%s\"" -project-cache-path="\"%s\"" -project-path="\"%s\"" -engine-path="\"%s\"" -port %d)",
task, builderGuid.c_str(), gameName.toUtf8().constData(), projectCacheRoot.absolutePath().toUtf8().constData(),
projectPath.toUtf8().constData(), engineRoot.absolutePath().toUtf8().constData(), portNumber);
#endif // !AZ_TRAIT_OS_PLATFORM_APPLE && !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
if (moduleFilePath && moduleFilePath[0])
{
@@ -230,17 +228,17 @@ namespace AssetProcessor
return params;
}
AZStd::unique_ptr<AzToolsFramework::ProcessWatcher> Builder::LaunchProcess(const char* fullExePath, const AZStd::string& params) const
AZStd::unique_ptr<AzFramework::ProcessWatcher> Builder::LaunchProcess(const char* fullExePath, const AZStd::string& params) const
{
AzToolsFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
processLaunchInfo.m_processExecutableString = fullExePath;
processLaunchInfo.m_commandlineParameters = AZStd::string::format("\"%s\" %s", fullExePath, params.c_str());
processLaunchInfo.m_showWindow = false;
processLaunchInfo.m_processPriority = AzToolsFramework::ProcessPriority::PROCESSPRIORITY_IDLE;
processLaunchInfo.m_processPriority = AzFramework::ProcessPriority::PROCESSPRIORITY_IDLE;
AZ_TracePrintf(AssetProcessor::DebugChannel, "Executing AssetBuilder with parameters: %s\n", processLaunchInfo.m_commandlineParameters.c_str());
auto processWatcher = AZStd::unique_ptr<AzToolsFramework::ProcessWatcher>(AzToolsFramework::ProcessWatcher::LaunchProcess(processLaunchInfo, AzToolsFramework::COMMUNICATOR_TYPE_STDINOUT));
auto processWatcher = AZStd::unique_ptr<AzFramework::ProcessWatcher>(AzFramework::ProcessWatcher::LaunchProcess(processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_STDINOUT));
AZ_Error(AssetProcessor::ConsoleChannel, processWatcher, "Failed to start %s", fullExePath);
@@ -13,7 +13,7 @@
#include <AzCore/std/string/string.h>
#include <AzCore/std/parallel/binary_semaphore.h>
#include <AzToolsFramework/Process/ProcessWatcher.h>
#include <AzFramework/Process/ProcessWatcher.h>
#include <AssetBuilderSDK/AssetBuilderSDK.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <QString>
@@ -108,7 +108,7 @@ namespace AssetProcessor
void SetConnection(AZ::u32 connId);
AZStd::string BuildParams(const char* task, const char* moduleFilePath, const AZStd::string& builderGuid, const AZStd::string& jobDescriptionFile, const AZStd::string& jobResponseFile) const;
AZStd::unique_ptr<AzToolsFramework::ProcessWatcher> LaunchProcess(const char* fullExePath, const AZStd::string& params) const;
AZStd::unique_ptr<AzFramework::ProcessWatcher> LaunchProcess(const char* fullExePath, const AZStd::string& params) const;
//! Waits for the builder exe to send the job response and pumps stdout/err
BuilderRunJobOutcome WaitForBuilderResponse(AssetBuilderSDK::JobCancelListener* jobCancelListener, AZ::u32 processTimeoutLimitInSeconds, AZStd::binary_semaphore* waitEvent) const;
@@ -128,7 +128,7 @@ namespace AssetProcessor
AZStd::binary_semaphore m_connectionEvent;
//! Optional process watcher
AZStd::unique_ptr<AzToolsFramework::ProcessWatcher> m_processWatcher = nullptr;
AZStd::unique_ptr<AzFramework::ProcessWatcher> m_processWatcher = nullptr;
//! Optional communicator, only available if we have a process watcher
AZStd::unique_ptr<CommunicatorTracePrinter> m_tracePrinter = nullptr;
@@ -12,7 +12,7 @@
#include "CommunicatorTracePrinter.h"
CommunicatorTracePrinter::CommunicatorTracePrinter(AzToolsFramework::ProcessCommunicator* communicator, const char* window) :
CommunicatorTracePrinter::CommunicatorTracePrinter(AzFramework::ProcessCommunicator* communicator, const char* window) :
m_communicator(communicator),
m_window(window)
{
@@ -12,14 +12,14 @@
#pragma once
#include <AzToolsFramework/Process/ProcessCommunicator.h>
#include <AzFramework/Process/ProcessCommunicator.h>
//! CommunicatorTracePrinter listens to stderr and stdout of a running process and writes its output to the AZ_Trace system
//! Importantly, it does not do any blocking operations.
class CommunicatorTracePrinter
{
public:
CommunicatorTracePrinter(AzToolsFramework::ProcessCommunicator* communicator, const char* window);
CommunicatorTracePrinter(AzFramework::ProcessCommunicator* communicator, const char* window);
~CommunicatorTracePrinter();
// call this periodically to drain the buffers and write them.
@@ -32,7 +32,7 @@ public:
private:
AZStd::string m_window;
AzToolsFramework::ProcessCommunicator* m_communicator;
AzFramework::ProcessCommunicator* m_communicator;
char m_streamBuffer[128];
AZStd::string m_stringBeingConcatenated;
AZStd::string m_errorStringBeingConcatenated;
@@ -130,37 +130,36 @@ ApplicationManager::BeforeRunStatus GUIApplicationManager::BeforeRun()
m_qtFileWatcher.addPath(assetDbPath);
// if our Gems file changes, make sure we watch that, too.
QString gameName = AssetUtilities::ComputeGameName();
QString gemsConfigFile = devRoot.filePath(gameName + "/gems.json");
m_qtFileWatcher.addPath(gemsConfigFile);
QString projectPath = AssetUtilities::ComputeProjectPath();
QObject::connect(&m_qtFileWatcher, &QFileSystemWatcher::fileChanged, this, &GUIApplicationManager::FileChanged);
QObject::connect(&m_qtFileWatcher, &QFileSystemWatcher::directoryChanged, this, &GUIApplicationManager::DirectoryChanged);
// Register a notifier for when the sys_game_folder property changes within the SettingsRegistry
// Register a notifier for when the project_path property changes within the SettingsRegistry
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
auto onBootStrapGameFolderChanged = [this, cachedGameName = gameName](AZStd::string_view path, AZ::SettingsRegistryInterface::Type type)
// Needs to be updated to project_path.
auto OnProjectPathChanged = [this, cachedProjectPath = projectPath](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
{
constexpr auto projectKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey)
+ "/sys_game_folder";
if (projectKey == path && type == AZ::SettingsRegistryInterface::Type::String)
constexpr auto projectPathKey =
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey)
+ "/project_path";
if (projectPathKey == path)
{
auto registry = AZ::SettingsRegistry::Get();
AZ::SettingsRegistryInterface::FixedValueString newGameName;
if (registry->Get(newGameName, path))
AZ::SettingsRegistryInterface::FixedValueString newProjectPath;
if (auto registry = AZ::SettingsRegistry::Get(); registry && registry->Get(newProjectPath, path))
{
// we only have to quit if the actual project name has changed, not if just the bootstrap has changed.
if (cachedGameName.compare(newGameName.c_str()) != 0)
// we only have to quit if the project path has changed, not if just the bootstrap has changed.
if (cachedProjectPath.compare(newProjectPath.c_str()) != 0)
{
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Bootstrap.cfg Game Name changed from %s to %s. Quitting\n", cachedGameName.toUtf8().constData(), newGameName.c_str());
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "bootstrap.cfg Project Path changed from %s to %s. Quitting\n",
cachedProjectPath.toUtf8().constData(), newProjectPath.c_str());
QMetaObject::invokeMethod(this, "QuitRequested", Qt::QueuedConnection);
}
}
}
};
m_bootstrapGameFolderChangedHandler = settingsRegistry->RegisterNotifier(AZStd::move(onBootStrapGameFolderChanged));
m_bootstrapGameFolderChangedHandler = settingsRegistry->RegisterNotifier(AZStd::move(OnProjectPathChanged));
}
return ApplicationManager::BeforeRunStatus::Status_Success;
@@ -189,8 +188,13 @@ bool GUIApplicationManager::Run()
qRegisterMetaType<AZ::u32>("AZ::u32");
qRegisterMetaType<AZ::Uuid>("AZ::Uuid");
AZ::IO::FixedMaxPath engineRootPath;
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
settingsRegistry->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
}
AzQtComponents::StyleManager* styleManager = new AzQtComponents::StyleManager(qApp);
styleManager->Initialize(qApp);
styleManager->initialize(qApp, engineRootPath);
QDir engineRoot;
AssetUtilities::ComputeAssetRoot(engineRoot);
@@ -198,7 +202,8 @@ bool GUIApplicationManager::Run()
AzQtComponents::StyleManager::addSearchPaths(
QStringLiteral("style"),
engineRoot.filePath(QStringLiteral("Code/Tools/AssetProcessor/native/ui/style")),
QStringLiteral(":/AssetProcessor/style"));
QStringLiteral(":/AssetProcessor/style"),
engineRootPath);
m_mainWindow = new MainWindow(this);
auto wrapper = new AzQtComponents::WindowDecorationWrapper(
@@ -485,7 +490,7 @@ bool GUIApplicationManager::OnError(const char* /*window*/, const char* message)
QVariant settingValue = loader.value("Game Projects/enabled_game_projects");
QStringList compiledProjects = settingValue.toStringList();
if(compiledProjects.isEmpty())
if (compiledProjects.isEmpty())
{
QByteArray byteArray;
QFile jsonFile;
@@ -497,12 +502,12 @@ bool GUIApplicationManager::OnError(const char* /*window*/, const char* message)
QJsonObject settingsObject = QJsonDocument::fromJson(byteArray).object();
QJsonArray projectsArray = settingsObject["Game Projects"].toArray();
if(!projectsArray.isEmpty())
if (!projectsArray.isEmpty())
{
auto projectObject = projectsArray[0].toObject();
QString projects = projectObject["default_value"].toString();
if(!projects.isEmpty())
if (!projects.isEmpty())
{
compiledProjects = projects.split(',');
usingDefaults = true;
@@ -515,13 +520,13 @@ bool GUIApplicationManager::OnError(const char* /*window*/, const char* message)
compiledProjects[i] = compiledProjects[i].trimmed();
}
QString enabledProject = AssetUtilities::ComputeGameName();
QString enabledProject = AssetUtilities::ComputeProjectName();
if(!compiledProjects.contains(enabledProject))
if (!compiledProjects.contains(enabledProject))
{
QString projectSourceLine;
if(usingDefaults)
if (usingDefaults)
{
projectSourceLine = QString("The currently compiled projects according to the defaults in %1 are '%2'").arg(defaultSettingsFile);
}
@@ -531,7 +536,6 @@ bool GUIApplicationManager::OnError(const char* /*window*/, const char* message)
}
projectSourceLine = projectSourceLine.arg(compiledProjects.join(", "));
friendlyErrorMessage = QString("An error occurred while loading gems.\n"
"The enabled game project is not in the list of compiled projects.\n"
"Please configure the enabled project to be compiled and rebuild or change the enabled project.\n"
@@ -539,13 +543,12 @@ bool GUIApplicationManager::OnError(const char* /*window*/, const char* message)
"%2\n"
"Full error text:\n"
"%3"
).arg(enabledProject).arg(projectSourceLine).arg(message).arg(AssetUtilities::GameFolderOverrideParameter);
).arg(enabledProject).arg(projectSourceLine).arg(message).arg(AssetUtilities::ProjectPathOverrideParameter);
}
}
if(friendlyErrorMessage.isEmpty())
if (friendlyErrorMessage.isEmpty())
{
friendlyErrorMessage = QString("An error occurred while loading gems.\n"
"This can happen when new gems are added to a project, but those gems need to be built in order to function.\n"
"This can also happen when switching to a different project, one which uses gems which are not yet built.\n"
@@ -631,34 +634,18 @@ void GUIApplicationManager::CreateQtApplication()
m_qApp = new QApplication(*m_frameworkApp.GetArgC(), *m_frameworkApp.GetArgV());
}
void GUIApplicationManager::DirectoryChanged(QString path)
void GUIApplicationManager::DirectoryChanged([[maybe_unused]] QString path)
{
AZ_UNUSED(path);
QDir devRoot = ApplicationManager::GetSystemRoot();
QString cacheRoot = devRoot.filePath("Cache");
if (!QDir(cacheRoot).exists())
QDir projectCacheRoot;
AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot);
if (!projectCacheRoot.exists() || !projectCacheRoot.exists("assetdb.sqlite"))
{
//Cache directory is removed we need to restart
// If either the Cache directory or database file has been removed, we need to restart
QTimer::singleShot(200, this, [this]()
{
QMetaObject::invokeMethod(this, "Restart", Qt::QueuedConnection);
});
}
else
{
QDir projectCacheRoot;
AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot);
QString assetDbPath = projectCacheRoot.filePath("assetdb.sqlite");
if (!QFile::exists(assetDbPath))
{
// even if cache directory exists but the the database file is missing we need to restart
QTimer::singleShot(200, this, [this]()
{
QMetaObject::invokeMethod(this, "Restart", Qt::QueuedConnection);
});
}
}
}
void GUIApplicationManager::FileChanged(QString path)
@@ -696,70 +683,6 @@ void GUIApplicationManager::FileChanged(QString path)
});
}
}
else if (AssetUtilities::NormalizeFilePath(path).endsWith("gems.json", Qt::CaseInsensitive))
{
AZStd::vector<AzToolsFramework::AssetUtils::GemInfo> oldGemsList = GetPlatformConfiguration()->GetGemsInformation();
AZStd::vector<AzToolsFramework::AssetUtils::GemInfo> newGemsList;
QDir assetRoot;
AssetUtilities::ComputeAssetRoot(assetRoot);
AzToolsFramework::AssetUtils::GetGemsInfo(GetSystemRoot().absolutePath().toUtf8().constData(), assetRoot.absolutePath().toUtf8().constData(), GetGameName().toUtf8().constData(), newGemsList);
for (auto oldGemIter = oldGemsList.begin(); oldGemIter != oldGemsList.end();)
{
bool gemMatch = false;
for (auto newGemIter = newGemsList.begin(); newGemIter != newGemsList.end();)
{
if (AzFramework::StringFunc::Equal(oldGemIter->m_identifier.c_str(), newGemIter->m_identifier.c_str()))
{
gemMatch = true;
newGemIter = newGemsList.erase(newGemIter);
break;
}
newGemIter++;
}
if (gemMatch)
{
oldGemIter = oldGemsList.erase(oldGemIter);
}
else
{
oldGemIter++;
}
}
// oldGemslist should contain the list of gems that got removed and newGemsList should contain the list of gems that were added to the project
// if the project requires to be built again then we will quit otherwise we can restart
bool exitApp = false;
for (const AzToolsFramework::AssetUtils::GemInfo& gemInfo : newGemsList)
{
if (!gemInfo.m_assetOnly)
{
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Gem %s was added to the project and require building. Quitting\n", gemInfo.m_gemName.c_str());
exitApp = true;
}
}
if (exitApp)
{
QuitRequested();
}
else
{
if (oldGemsList.size() || newGemsList.size())
{
if (oldGemsList.size())
{
AZ_TracePrintf(AssetProcessor::DebugChannel, "Gem(s) were removed from the project. Restarting\n");
}
else if (newGemsList.size())
{
AZ_TracePrintf(AssetProcessor::DebugChannel, "Assets only gem(s) were added to the project. Restarting\n");
}
QMetaObject::invokeMethod(this, "Restart", Qt::QueuedConnection);
}
}
}
}
bool GUIApplicationManager::InitApplicationServer()
@@ -20,43 +20,48 @@ AZ_POP_DISABLE_WARNING
#include "native/AssetDatabase/AssetDatabase.h"
#include "native/assetprocessor.h"
#include <AzCore/Component/TickBus.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/FileTag/FileTagBus.h>
#include <AzCore/std/string/wildcard.h>
#include <AzCore/XML/rapidxml.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/FileTag/FileTag.h>
#include <AzFramework/FileTag/FileTagBus.h>
#include <AzFramework/IO/LocalFileIO.h>
namespace AssetProcessor
{
const char* EngineFolder = "Engine";
AZStd::string GetXMLDependenciesFile(const AZStd::string& fullPath, const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, AZStd::string& tokenName)
AZStd::string GetXMLDependenciesFile(const AZStd::string& fullPath, const AZStd::vector<AzFramework::GemInfo>& gemInfoList, AZStd::string& tokenName)
{
AZStd::string xmlDependenciesFileFullPath;
AZ::IO::Path xmlDependenciesFileFullPath;
tokenName = EngineFolder;
for (const AzToolsFramework::AssetUtils::GemInfo& gemElement : gemInfoList)
for (const AzFramework::GemInfo& gemElement : gemInfoList)
{
if (AzFramework::StringFunc::StartsWith(fullPath.c_str(), gemElement.m_absoluteFilePath.c_str()) || AzFramework::StringFunc::Equal(gemElement.m_absoluteFilePath.c_str(), fullPath.c_str()))
for (const AZ::IO::Path& absoluteSourcePath : gemElement.m_absoluteSourcePaths)
{
AZStd::string fileName = AZStd::string::format("%s_Dependencies.xml", gemElement.m_gemName.c_str());
AzFramework::StringFunc::Path::ConstructFull(gemElement.m_absoluteFilePath.c_str(), AzToolsFramework::AssetUtils::GemInfo::GetGemAssetFolder().c_str(), fileName.c_str() , "xml", xmlDependenciesFileFullPath);
if (AZ::IO::FileIOBase::GetInstance()->Exists(xmlDependenciesFileFullPath.c_str()))
if (AZ::StringFunc::StartsWith(fullPath, absoluteSourcePath.Native()) || AZ::StringFunc::Equal(absoluteSourcePath.Native(), fullPath))
{
tokenName = gemElement.m_gemName;
return xmlDependenciesFileFullPath;
xmlDependenciesFileFullPath /= AzFramework::GemInfo::GetGemAssetFolder();
xmlDependenciesFileFullPath /= AZStd::string::format("%s_Dependencies.xml", gemElement.m_gemName.c_str());;
if (AZ::IO::FileIOBase::GetInstance()->Exists(xmlDependenciesFileFullPath.c_str()))
{
tokenName = gemElement.m_gemName;
return xmlDependenciesFileFullPath.Native();
}
}
}
}
// if we are here than either the %gemName%_Dependencies.xml file does not exists or the user inputted path is not inside a gems folder,
// in both the cases we will return the engine dependencies file
const char* devRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devroot@");
AzFramework::StringFunc::Path::ConstructFull(devRoot, EngineFolder, "Engine_Dependencies.xml", "xml", xmlDependenciesFileFullPath);
xmlDependenciesFileFullPath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devroot@");
xmlDependenciesFileFullPath /= EngineFolder;
xmlDependenciesFileFullPath /= "Engine_Dependencies.xml";
return xmlDependenciesFileFullPath;
return xmlDependenciesFileFullPath.Native();
}
const int MissingDependencyScanner::DefaultMaxScanIteration = 800;
@@ -731,7 +736,7 @@ namespace AssetProcessor
}
}
bool MissingDependencyScanner::PopulateRulesForScanFolder(const AZStd::string& scanFolderPath, const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, AZStd::string& dependencyTokenName)
bool MissingDependencyScanner::PopulateRulesForScanFolder(const AZStd::string& scanFolderPath, const AZStd::vector<AzFramework::GemInfo>& gemInfoList, AZStd::string& dependencyTokenName)
{
AZStd::string xmlDependenciesFullFilePath = GetXMLDependenciesFile(scanFolderPath, gemInfoList, dependencyTokenName);
if (xmlDependenciesFullFilePath.empty())
@@ -131,7 +131,7 @@ namespace AssetProcessor
void RegisterSpecializedScanner(AZStd::shared_ptr<SpecializedDependencyScanner> scanner);
bool PopulateRulesForScanFolder(const AZStd::string& scanFolderPath, const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, AZStd::string& dependencyTokenName);
bool PopulateRulesForScanFolder(const AZStd::string& scanFolderPath, const AZStd::vector<AzFramework::GemInfo>& gemInfoList, AZStd::string& dependencyTokenName);
protected:
bool RunScan(
File diff suppressed because it is too large Load Diff
@@ -22,6 +22,7 @@
#include <QVector>
#include <QSet>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/std/string/string.h>
#include <native/utilities/assetUtils.h>
#include <native/AssetManager/assetScanFolderInfo.h>
@@ -29,10 +30,15 @@
#include <AzToolsFramework/Asset/AssetUtils.h>
#endif
class QSettings;
namespace AZ
{
class SettingsRegistryInterface;
}
namespace AssetProcessor
{
inline constexpr const char* AssetProcessorSettingsKey{ "/Amazon/AssetProcessor/Settings" };
class PlatformConfiguration;
class ScanFolderInfo;
extern const char AssetConfigPlatformDir[];
@@ -105,6 +111,73 @@ namespace AssetProcessor
virtual const ExcludeRecognizerContainer& GetExcludeAssetRecognizerContainer() const = 0;
};
//! Visitor for reading the "/Amazon/AssetProcessor/Settings/ScanFolder *" entries from the Settings Registry
//! Expects the key to path to the visitor to be "/Amazon/AssetProcessor/Settings"
struct ScanFolderVisitor
: AZ::SettingsRegistryInterface::Visitor
{
AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName,
AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type) override;
void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value) override;
void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override;
struct ScanFolderInfo
{
AZStd::string m_scanFolderIdentifier;
AZStd::string m_scanFolderDisplayName;
AZ::IO::Path m_watchPath{ AZ::IO::PosixPathSeparator };
AZStd::vector<AZStd::string> m_includeIdentifiers;
AZStd::vector<AZStd::string> m_excludeIdentifiers;
AZStd::string m_outputPrefix;
int m_scanOrder{};
bool m_isRecursive{};
};
AZStd::vector<ScanFolderInfo> m_scanFolderInfos;
private:
AZStd::stack<AZStd::string> m_scanFolderStack;
};
struct ExcludeVisitor
: AZ::SettingsRegistryInterface::Visitor
{
AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName,
AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type) override;
void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override;
AZStd::vector<ExcludeAssetRecognizer> m_excludeAssetRecognizers;
private:
AZStd::stack<AZStd::string> m_excludeNameStack;
};
struct RCVisitor
: AZ::SettingsRegistryInterface::Visitor
{
RCVisitor(const AZ::SettingsRegistryInterface& settingsRegistry, const AZStd::vector<AssetBuilderSDK::PlatformInfo>& enabledPlatforms)
: m_registry(settingsRegistry)
, m_enabledPlatforms(enabledPlatforms)
{
}
AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName,
AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type) override;
void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, bool value) override;
void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value) override;
void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override;
struct RCAssetRecognizer
{
AssetRecognizer m_recognizer;
AZStd::string m_defaultParams;
bool m_ignore{};
};
AZStd::vector<RCAssetRecognizer> m_assetRecognizers;
private:
void ApplyParamsOverrides(AZStd::string_view path);
AZStd::stack<AZStd::string> m_rcNameStack;
const AZ::SettingsRegistryInterface& m_registry;
const AZStd::vector<AssetBuilderSDK::PlatformInfo>& m_enabledPlatforms;
};
/** Reads the platform ini configuration file to determine
* platforms for which assets needs to be build
*/
@@ -127,22 +200,23 @@ namespace AssetProcessor
* Note that order of the config files is relevant - later files override settings in
* files that are earlier.
**/
bool InitializeFromConfigFiles(QString absoluteSystemRoot, QString absoluteAssetRoot, QString gameName, bool addPlatformConfigs = true, bool addGemsConfigs = true);
bool InitializeFromConfigFiles(const QString& absoluteSystemRoot, const QString& absoluteAssetRoot, const QString& projectPath, bool addPlatformConfigs = true, bool addGemsConfigs = true);
QString PlatformName(unsigned int platformCrc) const;
QString RendererName(unsigned int rendererCrc) const;
//! Merge an AssetProcessor*Config.ini path to the Settings Registry
//! The settings are anchored underneath the AssetProcessor::AssetProcessorSettingsKey JSON pointer
static bool MergeConfigFileToSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry, const AZ::IO::PathView& filePathView);
const AZStd::vector<AssetBuilderSDK::PlatformInfo>& GetEnabledPlatforms() const;
const AssetBuilderSDK::PlatformInfo* const GetPlatformByIdentifier(const char* identifier) const;
//! Add AssetProcessor config files from platform specific folders
bool AddPlatformConfigFilePaths(QStringList& configList);
bool AddPlatformConfigFilePaths(AZStd::vector<AZ::IO::Path>& configList);
int MetaDataFileTypesCount() const { return m_metaDataFileTypes.count(); }
// Metadata file types are (meta file extension, original file extension - or blank if its tacked on the end instead of replacing).
// so for example if its
// blah.tif + blah.tif.metadata, then its ("metadata", "")
// but if its blah.tif + blah.metadata (rplacing tif, data is lost) then its ("metadata", "tif")
// but if its blah.tif + blah.metadata (replacing tif, data is lost) then its ("metadata", "tif")
QPair<QString, QString> GetMetaDataFileTypeAt(int pos) const;
// Metadata extensions can also be a real file, to create a dependency on file types if a specific file changes
@@ -160,7 +234,7 @@ namespace AssetProcessor
int GetScanFolderCount() const;
//! Return the gems info list
AZStd::vector<AzToolsFramework::AssetUtils::GemInfo> GetGemsInformation() const;
AZStd::vector<AzFramework::GemInfo> GetGemsInformation() const;
//! Retrieve the scan folder at a given index.
AssetProcessor::ScanFolderInfo& GetScanFolderAt(int index);
@@ -249,18 +323,18 @@ namespace AssetProcessor
protected:
// call this first, to populate the list of platform informations
void ReadPlatformInfosFromConfigFile(QString fileSource);
void ReadPlatformInfosFromSettingsRegistry();
// call this next, in order to find out what platforms are enabled
void PopulateEnabledPlatforms(QStringList configFiles);
// finaly, call this, in order to delete the platforminfos for non-enabled platforms
void PopulateEnabledPlatforms();
// finally, call this, in order to delete the platforminfos for non-enabled platforms
void FinalizeEnabledPlatforms();
// iterate over all the gems and add their folders to the "scan folders" list as appropriate.
void AddGemScanFolders(const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList);
void AddGemScanFolders(const AZStd::vector<AzFramework::GemInfo>& gemInfoList);
void ReadEnabledPlatformsFromConfigFile(QString fileSource);
bool ReadRecognizersFromConfigFile(QString fileSource, bool skipScanFolders = false, QStringList scanFolderPatterns = QStringList() );
void ReadMetaDataFromConfigFile(QString fileSource);
void ReadEnabledPlatformsFromSettingsRegistry();
bool ReadRecognizersFromSettingsRegistry(const QString& assetRoot, bool skipScanFolders = false, QStringList scanFolderPatterns = QStringList() );
void ReadMetaDataFromSettingsRegistry();
private:
AZStd::vector<AssetBuilderSDK::PlatformInfo> m_enabledPlatforms;
@@ -269,15 +343,13 @@ namespace AssetProcessor
AZStd::vector<AssetProcessor::ScanFolderInfo> m_scanFolders;
QList<QPair<QString, QString> > m_metaDataFileTypes;
QSet<QString> m_metaDataRealFiles;
AZStd::vector<AzToolsFramework::AssetUtils::GemInfo> m_gemInfoList;
AZStd::vector<AzFramework::GemInfo> m_gemInfoList;
int m_minJobs = 1;
int m_maxJobs = 3;
// used only during file read, keeps the total running list of all the enabled platforms from all config files and command lines
QStringList m_tempEnabledPlatforms;
bool ReadRecognizerFromConfig(AssetRecognizer& target, QSettings& loader); // assumes the group is already selected
AZStd::vector<AZStd::string> m_tempEnabledPlatforms;
///! if non-empty, fatalError contains the error that occurred during read.
///! it will be printed out to the log when
@@ -22,7 +22,6 @@
#include <QElapsedTimer>
#include <QTemporaryDir>
#include <QTextStream>
#include <QSettings>
#include <QTimeZone>
#include <QRandomGenerator>
@@ -38,7 +37,9 @@
#include <AzCore/JSON/document.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/Platform/PlatformDefaults.h>
#include <AzToolsFramework/UI/Logging/LogLine.h>
#include <xxhash/xxhash.h>
@@ -77,7 +78,7 @@ namespace AssetUtilsInternal
{
if (waitTimeInSeconds < 0)
{
AZ_Warning("Asset Processor", waitTimeInSeconds >= 0, "Invalid timeout specified by the user")
AZ_Warning("Asset Processor", waitTimeInSeconds >= 0, "Invalid timeout specified by the user");
waitTimeInSeconds = 0;
}
bool failureOccurredOnce = false; // used for logging.
@@ -152,12 +153,64 @@ namespace AssetUtilsInternal
return true;
}
static bool DumpAssetProcessorUserSettingsToFile(AZ::SettingsRegistryInterface& settingsRegistry,
const AZ::IO::FixedMaxPath& setregPath)
{
// The AssetProcessor settings are currently under the Bootstrap object(This may change in the future
constexpr AZStd::string_view AssetProcessorUserSettingsRootKey = AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey;
AZStd::string apSettingsJson;
AZ::IO::ByteContainerStream apSettingsStream(&apSettingsJson);
AZ::SettingsRegistryMergeUtils::DumperSettings apDumperSettings;
apDumperSettings.m_prettifyOutput = true;
apDumperSettings.m_includeFilter = [&AssetProcessorUserSettingsRootKey](AZStd::string_view path)
{
// The AssetUtils only updates the following keys in the registry
// Dump them all out to the setreg file
auto allowedListKey = AZ::SettingsRegistryInterface::FixedValueString(AssetProcessorUserSettingsRootKey)
+ "/allowed_list";
auto branchTokenKey = AZ::SettingsRegistryInterface::FixedValueString(AssetProcessorUserSettingsRootKey)
+ "/assetProcessor_branch_token";
// The objects leading up to the keys to dump must be included in order the keys to be dumped
return allowedListKey.starts_with(path.substr(0, allowedListKey.size()))
|| branchTokenKey.starts_with(path.substr(0, branchTokenKey.size()));
};
apDumperSettings.m_jsonPointerPrefix = AssetProcessorUserSettingsRootKey;
if (AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(settingsRegistry, AssetProcessorUserSettingsRootKey,
apSettingsStream, apDumperSettings))
{
constexpr auto modeFlags = AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY | AZ::IO::SystemFile::SF_OPEN_CREATE
| AZ::IO::SystemFile::SF_OPEN_CREATE_PATH;
if (AZ::IO::SystemFile apSetregFile; apSetregFile.Open(setregPath.c_str(), modeFlags))
{
size_t bytesWritten = apSetregFile.Write(apSettingsJson.data(), apSettingsJson.size());
return bytesWritten == apSettingsJson.size();
}
else
{
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Unable to open AssetProcessor user setreg file (%s)\n", setregPath.c_str());
}
}
else
{
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Dump of AssetProcessor User Settings failed at JSON pointer %.*s \n",
aznumeric_cast<int>(AssetProcessorUserSettingsRootKey.size()), AssetProcessorUserSettingsRootKey.data());
}
return false;
}
}
namespace AssetUtilities
{
constexpr AZStd::string_view AssetProcessorUserSetregRelPath = "user/Registry/asset_processor.setreg";
// do not place Qt objects in global scope, they allocate and refcount threaded data.
AZ::SettingsRegistryInterface::FixedValueString s_gameName;
AZ::SettingsRegistryInterface::FixedValueString s_projectPath;
AZ::SettingsRegistryInterface::FixedValueString s_projectName;
AZ::SettingsRegistryInterface::FixedValueString s_assetRoot;
AZ::SettingsRegistryInterface::FixedValueString s_assetServerAddress;
AZ::SettingsRegistryInterface::FixedValueString s_cachedEngineRoot;
@@ -191,7 +244,7 @@ namespace AssetUtilities
void ResetGameName()
{
s_gameName = {};
s_projectName = {};
}
bool CopyDirectory(QDir source, QDir destination)
@@ -245,7 +298,7 @@ namespace AssetUtilities
return true;
}
bool ComputeAssetRoot(QDir& root, const QDir* appRootOverride)
bool ComputeAssetRoot(QDir& root, const QDir* rootOverride)
{
if (!s_assetRoot.empty())
{
@@ -253,10 +306,10 @@ namespace AssetUtilities
return true;
}
// Use the appRoot if supplied is supplied and not an empty string
if (appRootOverride && !appRootOverride->path().isEmpty())
// Use the override if supplied and not an empty string
if (rootOverride && !rootOverride->path().isEmpty())
{
root = *appRootOverride;
root = *rootOverride;
s_assetRoot = root.absolutePath().toUtf8().constData();
return true;
}
@@ -288,7 +341,7 @@ namespace AssetUtilities
return true;
}
// The EngineRootFolder Key has not been found in the SettingsRegistry, log an warning about
// The EngineRootFolder Key has not been found in the SettingsRegistry
auto engineRootError = AZ::SettingsRegistryInterface::FixedValueString::format("The EngineRootFolder is not set in the SettingsRegistry at key %s.",
AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
AssetProcessor::MessageInfoBus::Broadcast(&AssetProcessor::MessageInfoBusTraits::OnErrorMessage, engineRootError.c_str());
@@ -296,7 +349,7 @@ namespace AssetUtilities
return false;
}
//! Get the engine root folder
//! Get the external engine root folder if the engine is external to the current root folder.
//! If the current root folder is also the engine folder, then this behaves the same as ComputeEngineRoot
bool ComputeEngineRoot(QDir& root, const QDir* engineRootOverride)
{
@@ -312,6 +365,7 @@ namespace AssetUtilities
AssetUtilities::ComputeAssetRoot(root, engineRootOverride);
}
AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
// Use the engineRootOverride if supplied and not empty
if (engineRootOverride && !engineRootOverride->path().isEmpty())
{
@@ -320,11 +374,11 @@ namespace AssetUtilities
return true;
}
AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
if (settingsRegistry == nullptr)
{
return false;
}
AZ::IO::FixedMaxPathString engineRootFolder;
if (settingsRegistry->Get(engineRootFolder, AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
{
@@ -336,7 +390,7 @@ namespace AssetUtilities
return false;
}
bool MakeFileWritable(QString fileName)
bool MakeFileWritable(const QString& fileName)
{
#if defined WIN32
DWORD fileAttributes = GetFileAttributesA(fileName.toUtf8());
@@ -386,7 +440,7 @@ namespace AssetUtilities
#endif
}
bool CheckCanLock(QString fileName)
bool CheckCanLock(const QString& fileName)
{
#if defined(AZ_PLATFORM_WINDOWS)
AZStd::wstring usableFileName;
@@ -419,42 +473,55 @@ namespace AssetUtilities
#endif
}
QString ComputeGameName(QString gameNameOverride, bool force)
QString ComputeProjectName(QString gameNameOverride, bool force)
{
if (force || s_gameName.empty())
if (force || s_projectName.empty())
{
// if its been specified on the command line, then ignore bootstrap:
// Override Game Name if a non-empty override string has been supplied
if (!gameNameOverride.isEmpty())
{
s_projectName = gameNameOverride.toUtf8().constData();
}
else
{
s_projectName = AZ::Utils::GetProjectName();
}
}
return QString::fromUtf8(s_projectName.c_str(), aznumeric_cast<int>(s_projectName.size()));
}
QString ComputeProjectPath()
{
if (s_projectPath.empty())
{
// Check command-line args first
QStringList args = QCoreApplication::arguments();
for (QString arg : args)
{
if (arg.contains(QString("/%1=").arg(GameFolderOverrideParameter), Qt::CaseInsensitive) || arg.contains(QString("--%1=").arg(GameFolderOverrideParameter), Qt::CaseInsensitive))
if (arg.contains(QString("/%1=").arg(ProjectPathOverrideParameter), Qt::CaseInsensitive)
|| arg.contains(QString("--%1=").arg(ProjectPathOverrideParameter), Qt::CaseInsensitive))
{
QString rawValueString = arg.split("=")[1].trimmed();
if (!rawValueString.isEmpty())
{
s_gameName = rawValueString.toUtf8().constData();
return rawValueString;
QDir path(rawValueString);
if (path.isAbsolute())
{
s_projectPath = rawValueString.toUtf8().constData();
break;
}
}
}
}
// Override Game Name if a non-empty override string has been supplied
if (!gameNameOverride.isEmpty())
{
s_gameName = gameNameOverride.toUtf8().constData();
}
else
{
QDir engineRoot;
if (!AssetUtilities::ComputeEngineRoot(engineRoot))
{
return QString();
}
s_gameName = ReadGameNameFromSettingsRegistry(engineRoot.absolutePath()).toUtf8().constData();
}
}
return QString::fromUtf8(s_gameName.c_str(), aznumeric_cast<int>(s_gameName.size()));
if (s_projectPath.empty())
{
s_projectPath = AZ::Utils::GetProjectPath();
}
return QString::fromUtf8(s_projectPath.c_str(), aznumeric_cast<int>(s_projectPath.size()));
}
bool InServerMode()
@@ -479,7 +546,7 @@ namespace AssetUtilities
}
else
{
AZ_Warning(AssetProcessor::ConsoleChannel, false, "Invalid server address, please check the AssetProcessorPlatformConfig.ini file \
AZ_Warning(AssetProcessor::ConsoleChannel, false, "Invalid server address, please check the AssetProcessorPlatformConfig.setreg file \
to ensure that the address is correct. Asset Processor won't be running in server mode.");
}
@@ -517,19 +584,18 @@ to ensure that the address is correct. Asset Processor won't be running in serve
}
}
QDir engineRoot;
ComputeEngineRoot(engineRoot);
QString rootConfigFile = engineRoot.absoluteFilePath("AssetProcessorPlatformConfig.ini");
if (QFile::exists(rootConfigFile))
auto settingsRegistry = AZ::SettingsRegistry::Get();
if (settingsRegistry)
{
QString address;
QSettings loader(rootConfigFile, QSettings::IniFormat);
loader.beginGroup("Server");
address = loader.value("cacheServerAddress", QString()).toString();
loader.endGroup();
s_assetServerAddress = address.toUtf8().constData();
return address;
AZStd::string address;
if (settingsRegistry->Get(address, AZ::SettingsRegistryInterface::FixedValueString(AssetProcessor::AssetProcessorSettingsKey)
+ "/Server/cacheServerAddress"))
{
AZ_TracePrintf(AssetProcessor::DebugChannel, "Server Address: %s\n", address.c_str());
}
s_assetServerAddress = address;
return QString::fromUtf8(address.data(), aznumeric_cast<int>(address.size()));
}
return QString();
@@ -549,18 +615,12 @@ to ensure that the address is correct. Asset Processor won't be running in serve
return *s_fileHashSetting;
}
QDir engineRoot;
ComputeEngineRoot(engineRoot);
QString rootConfigFile = engineRoot.absoluteFilePath("AssetProcessorPlatformConfig.ini");
if (QFile::exists(rootConfigFile))
auto settingsRegistry = AZ::SettingsRegistry::Get();
if (settingsRegistry)
{
bool curValue;
QSettings loader(rootConfigFile, QSettings::IniFormat);
loader.beginGroup("Fingerprinting");
curValue = loader.value("UseFileHashing", true).toBool();
loader.endGroup();
bool curValue = true;
settingsRegistry->Get(curValue, AZ::SettingsRegistryInterface::FixedValueString(AssetProcessor::AssetProcessorSettingsKey)
+ "/Fingerprinting/UseFileHashing");
AZ_TracePrintf(AssetProcessor::DebugChannel, "UseFileHashing: %s\n", curValue ? "True" : "False");
s_fileHashSetting = curValue;
@@ -573,46 +633,8 @@ to ensure that the address is correct. Asset Processor won't be running in serve
return *s_fileHashSetting;
}
QString ReadGameNameFromSettingsRegistry(QString initialFolder /*= QString()*/)
QString ReadAllowedlistFromSettingsRegistry([[maybe_unused]] QString initialFolder)
{
if (initialFolder.isEmpty())
{
QDir engineRoot;
if (!AssetUtilities::ComputeEngineRoot(engineRoot))
{
return QString();
}
initialFolder = engineRoot.absolutePath();
}
constexpr size_t BufferSize = AZ_ARRAY_SIZE(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + AZStd::char_traits<char>::length("/sys_game_folder");
AZStd::fixed_string<BufferSize> projectKey{ AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey };
projectKey += "/sys_game_folder";
AZ::SettingsRegistryInterface::FixedValueString projectName;
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry && settingsRegistry->Get(projectName, projectKey))
{
return QString::fromUtf8(projectName.c_str(), aznumeric_cast<int>(projectName.size()));
}
AZ_Warning("AssetUtils", false, "Unable to find the Project Name(sys_game_folder) key in the settings registry");
return {};
}
QString ReadAllowedlistFromSettingsRegistry(QString initialFolder /*= QString()*/)
{
if (initialFolder.isEmpty())
{
QDir assetRoot;
if (!AssetUtilities::ComputeAssetRoot(assetRoot))
{
return QString();
}
initialFolder = assetRoot.absolutePath();
}
constexpr size_t BufferSize = AZ_ARRAY_SIZE(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + AZStd::char_traits<char>::length("/allowed_list");
AZStd::fixed_string<BufferSize> allowedListKey{ AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey };
allowedListKey += "/allowed_list";
@@ -626,18 +648,8 @@ to ensure that the address is correct. Asset Processor won't be running in serve
return {};
}
QString ReadRemoteIpFromSettingsRegistry(QString initialFolder /*= QString()*/)
QString ReadRemoteIpFromSettingsRegistry([[maybe_unused]] QString initialFolder)
{
if (initialFolder.isEmpty())
{
QDir engineRoot;
if (!AssetUtilities::ComputeEngineRoot(engineRoot))
{
return QString();
}
initialFolder = engineRoot.absolutePath();
}
constexpr size_t BufferSize = AZ_ARRAY_SIZE(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + AZStd::char_traits<char>::length("/remote_ip");
AZStd::fixed_string<BufferSize> remoteIpKey{ AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey };
remoteIpKey += "/remote_ip";
@@ -651,159 +663,44 @@ to ensure that the address is correct. Asset Processor won't be running in serve
return {};
}
bool WriteAllowedlistToBootstrap(QStringList newAllowedList)
bool WriteAllowedlistToSettingsRegistry(const QStringList& newAllowedList)
{
QDir assetRoot;
ComputeAssetRoot(assetRoot);
QString bootstrapFilename = assetRoot.filePath("bootstrap.cfg");
QFile bootstrapFile(bootstrapFilename);
AZ::IO::FixedMaxPath assetProcessorUserSetregPath = AZ::Utils::GetProjectPath();
assetProcessorUserSetregPath /= AssetProcessorUserSetregRelPath;
// do not alter the branch file unless we are able to obtain an exclusive lock. Other apps (such as NPP) may actually write 0 bytes first, then slowly spool out the remainder)
if (!CheckCanLock(bootstrapFilename))
auto settingsRegistry = AZ::SettingsRegistry::Get();
if (!settingsRegistry)
{
AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable access Settings Registry. Branch Token cannot be updated");
return false;
}
if (!bootstrapFile.open(QIODevice::ReadOnly))
auto allowedListKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey)
+ "/allowed_list";
AZStd::string currentAllowedList;
if (settingsRegistry->Get(currentAllowedList, allowedListKey))
{
return false;
}
// Split the current allowedList into an array and compare against the new allowed list
AZStd::vector<AZStd::string_view> allowedListArray;
auto AppendAllowedIpTokens = [&allowedListArray](AZStd::string_view token) { allowedListArray.emplace_back(token); };
AZ::StringFunc::TokenizeVisitor(currentAllowedList, AppendAllowedIpTokens, ',');
// regexp that matches either the beginning of the file, some whitespace, and allowed_list, or,
// matches a newline, then whitespace, then allowed_list it will not match comments.
QRegExp allowedListPattern("(^|\\n)\\s*allowed_list\\s*=\\s*(.+)", Qt::CaseInsensitive, QRegExp::RegExp);
//read the file line by line and try to find the allowed_list line
QString readAllowedList;
QString allowedListline;
while (!bootstrapFile.atEnd())
{
QString contents(bootstrapFile.readLine());
int matchIdx = allowedListPattern.indexIn(contents);
if (matchIdx != -1)
auto CompareQListToAzVector = [](AZStd::string_view currentAllowedIp, const QString& newAllowedIp)
{
allowedListline = contents;
readAllowedList = allowedListPattern.cap(2);
break;
return currentAllowedIp == newAllowedIp.toUtf8().constData();
};
if (AZStd::equal(allowedListArray.begin(), allowedListArray.end(), newAllowedList.begin(), newAllowedList.end(), CompareQListToAzVector))
{
// no need to update, remote_ip already matches
return true;
}
}
//read the entire file into so we can do a buffer replacement
bootstrapFile.seek(0);
QString fileContents;
fileContents = bootstrapFile.readAll();
bootstrapFile.close();
// Update Settings Registry with new token
AZStd::string azNewAllowedList{ newAllowedList.join(', ').toUtf8().constData() };
settingsRegistry->Set(allowedListKey, azNewAllowedList);
//format the new allowed list
QString formattedNewAllowedList = newAllowedList.join(", ");
//if we didn't find a allowed_list entry then append one
if (allowedListline.isEmpty())
{
fileContents.append("\nallowed_list = " + formattedNewAllowedList + "\n");
}
else if (QString::compare(formattedNewAllowedList, readAllowedList, Qt::CaseInsensitive) == 0)
{
// no need to update, they match
return true;
}
else
{
//Replace the found line with a new one
fileContents.replace(allowedListline, "allowed_list = " + formattedNewAllowedList + "\n");
}
// Make the bootstrap file writable
if (!MakeFileWritable(bootstrapFilename))
{
AZ_Warning(AssetProcessor::ConsoleChannel, false, "Failed to make the bootstrap file writable.")
return false;
}
if (!bootstrapFile.open(QIODevice::WriteOnly))
{
return false;
}
QTextStream output(&bootstrapFile);
output << fileContents;
bootstrapFile.close();
return true;
}
bool WriteRemoteIpToBootstrap(QString newRemoteIp)
{
QDir assetRoot;
ComputeAssetRoot(assetRoot);
QString bootstrapFilename = assetRoot.filePath("bootstrap.cfg");
QFile bootstrapFile(bootstrapFilename);
// do not alter the branch file unless we are able to obtain an exclusive lock. Other apps (such as NPP) may actually write 0 bytes first, then slowly spool out the remainder)
if (!CheckCanLock(bootstrapFilename))
{
return false;
}
if (!bootstrapFile.open(QIODevice::ReadOnly))
{
return false;
}
// regexp that matches either the beginning of the file, and remote_ip, or,
// matches a newline, then whitespace, then remote_ip it will not match comments.
QRegExp remoteIpPattern("(^|\\n)\\s*remote_ip\\s*=\\s*(.+)", Qt::CaseInsensitive, QRegExp::RegExp);
//read the file line by line and try to find the remote_ip line
QString readRemoteIp;
QString remoteIpline;
while (!bootstrapFile.atEnd())
{
QString contents(bootstrapFile.readLine());
int matchIdx = remoteIpPattern.indexIn(contents);
if (matchIdx != -1)
{
remoteIpline = contents;
readRemoteIp = remoteIpPattern.cap(2);
break;
}
}
//read the entire file into so we can do a buffer replacement
bootstrapFile.seek(0);
QString fileContents;
fileContents = bootstrapFile.readAll();
bootstrapFile.close();
//if we didn't find a remote_ip entry then append one
if (remoteIpline.isEmpty())
{
fileContents.append("\nremote_ip = " + newRemoteIp + "\n");
}
else if (QString::compare(newRemoteIp, readRemoteIp, Qt::CaseInsensitive) == 0)
{
// no need to update, they match
return true;
}
else
{
//Replace the found line with a new one
fileContents.replace(remoteIpline, "remote_ip = " + newRemoteIp + "\n");
}
// Make the bootstrap file writable
if (!MakeFileWritable(bootstrapFilename))
{
AZ_Warning(AssetProcessor::ConsoleChannel, false, "Failed to make the bootstrap file writable.")
return false;
}
if (!bootstrapFile.open(QIODevice::WriteOnly))
{
return false;
}
QTextStream output(&bootstrapFile);
output << fileContents;
bootstrapFile.close();
return true;
return AssetUtilsInternal::DumpAssetProcessorUserSettingsToFile(*settingsRegistry, assetProcessorUserSetregPath);
}
quint16 ReadListeningPortFromSettingsRegistry(QString initialFolder /*= QString()*/)
@@ -923,23 +820,20 @@ to ensure that the address is correct. Asset Processor won't be running in serve
bool ComputeProjectCacheRoot(QDir& projectCacheRoot)
{
QDir assetRoot;
if (!ComputeAssetRoot(assetRoot))
if (auto registry = AZ::SettingsRegistry::Get(); registry != nullptr)
{
return false; // failed to detect engine root
AZ::SettingsRegistryInterface::FixedValueString projectCacheRootValue;
if (registry->Get(projectCacheRootValue, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder);
!projectCacheRootValue.empty())
{
projectCacheRoot = QDir(QString::fromUtf8(projectCacheRootValue.c_str(), aznumeric_cast<int>(projectCacheRootValue.size())));
return true;
}
}
QString gameDir = ComputeGameName(assetRoot.absolutePath());
if (gameDir.isEmpty())
{
return false;
}
projectCacheRoot = QDir(assetRoot.filePath("Cache/" + gameDir));
return true;
return false;
}
bool ComputeFenceDirectory(QDir& fenceDir)
{
QDir cacheRoot;
@@ -951,6 +845,25 @@ to ensure that the address is correct. Asset Processor won't be running in serve
return true;
}
QString StripAssetPlatform(AZStd::string_view relativeProductPath)
{
// Skip over the assetPlatform path segment if it is matches one of the platform defaults
// Otherwise return the path unchanged
AZStd::string_view strippedProductPath{ relativeProductPath };
if (AZStd::optional pathSegment = AZ::StringFunc::TokenizeNext(strippedProductPath, AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR);
pathSegment.has_value())
{
AZ::IO::FixedMaxPathString assetPlatformSegmentLower{ *pathSegment };
AZStd::to_lower(assetPlatformSegmentLower.begin(), assetPlatformSegmentLower.end());
if (AzFramework::PlatformHelper::GetPlatformIdFromName(assetPlatformSegmentLower) != AzFramework::PlatformId::Invalid)
{
return QString::fromUtf8(strippedProductPath.data(), aznumeric_cast<int>(strippedProductPath.size()));
}
}
return QString::fromUtf8(relativeProductPath.data(), aznumeric_cast<int>(relativeProductPath.size()));
}
QString NormalizeFilePath(const QString& filePath)
{
// do NOT convert to absolute paths here, we just want to manipulate the string itself.
@@ -1035,92 +948,41 @@ to ensure that the address is correct. Asset Processor won't be running in serve
bool UpdateBranchToken()
{
QDir assetRoot;
ComputeAssetRoot(assetRoot);
QString bootstrapFilename = assetRoot.filePath("bootstrap.cfg");
QFile bootstrapFile(bootstrapFilename);
QString fileContents;
// do not alter the branch file unless we are able to obtain an exclusive lock. Other apps (such as NPP) may actually write 0 bytes first, then slowly spool out the remainder)
QElapsedTimer timer;
timer.start();
bool hasLock = false;
do
{
if (CheckCanLock(bootstrapFilename))
{
hasLock = true;
break;
}
QThread::msleep(AssetUtilsInternal::g_RetryWaitInterval);
} while (!timer.hasExpired(10 * AssetUtilsInternal::g_RetryWaitInterval));
if (!hasLock)
{
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Unable to lock bootstrap file at: %s\n", bootstrapFilename.toUtf8().constData());
return false;
}
if (!bootstrapFile.open(QIODevice::ReadOnly))
{
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Unable to open bootstrap file at: %s\n", bootstrapFilename.toUtf8().constData());
return false;
}
fileContents = bootstrapFile.readAll();
bootstrapFile.close();
AZ::IO::FixedMaxPath assetProcessorUserSetregPath = AZ::Utils::GetProjectPath();
assetProcessorUserSetregPath /= AssetProcessorUserSetregRelPath;
AZStd::string appBranchToken;
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForAppRoot, appBranchToken);
QString currentBranchToken(appBranchToken.c_str());
QString readBranchToken;
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForEngineRoot, appBranchToken);
// regexp that matches either the beginning of the file, some whitespace, and assetProcessor_branch_token, or,
// matches a newline, then whitespace, then assetProcessor_branch_token
// it will not match comments.
QRegExp branchTokenPattern("(^|\\n)\\s*assetProcessor_branch_token\\s*=\\s*(\\S+)\\b", Qt::CaseInsensitive, QRegExp::RegExp);
int matchIdx = branchTokenPattern.indexIn(fileContents);
if (matchIdx != -1)
auto settingsRegistry = AZ::SettingsRegistry::Get();
if (!settingsRegistry)
{
readBranchToken = branchTokenPattern.cap(2);
AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable access Settings Registry. Branch Token cannot be updated");
return false;
}
if (readBranchToken.isEmpty())
AZStd::string registryBranchToken;
auto branchTokenKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/assetProcessor_branch_token";
if (settingsRegistry->Get(registryBranchToken, branchTokenKey))
{
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "adding branch token (%s) in (%s)\n", currentBranchToken.toUtf8().constData(), bootstrapFilename.toUtf8().constData());
fileContents.append("\nassetProcessor_branch_token = " + currentBranchToken + "\n");
}
else if (QString::compare(currentBranchToken, readBranchToken, Qt::CaseInsensitive) == 0)
{
// no need to update, branch token match
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Branch token (%s) is already correct in (%s)\n", currentBranchToken.toUtf8().constData(), bootstrapFilename.toUtf8().constData());
return true;
if (appBranchToken == registryBranchToken)
{
// no need to update, branch token match
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Branch token (%s) is already correct in (%s)\n", appBranchToken.c_str(), assetProcessorUserSetregPath.c_str());
return true;
}
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Updating branch token (%s) in (%s)\n", appBranchToken.c_str(), assetProcessorUserSetregPath.c_str());
}
else
{
//Updating branch token
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Updating branch token (%s) in (%s)\n", currentBranchToken.toUtf8().constData(), bootstrapFilename.toUtf8().constData());
fileContents.replace(branchTokenPattern.cap(0), "\nassetProcessor_branch_token = " + currentBranchToken + "\n");
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Adding branch token (%s) in (%s)\n", appBranchToken.c_str(), assetProcessorUserSetregPath.c_str());
}
// Make the bootstrap file writable
if (!MakeFileWritable(bootstrapFilename))
{
AZ_Warning(AssetProcessor::ConsoleChannel, false, "Failed to make the bootstrap file writable.")
return false;
}
if (!bootstrapFile.open(QIODevice::WriteOnly))
{
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Unable to open bootstrap file (%s)\n", bootstrapFilename.toUtf8().constData());
return false;
}
// Update Settings Registry with new token
settingsRegistry->Set(branchTokenKey, appBranchToken);
QTextStream output(&bootstrapFile);
output << fileContents;
bootstrapFile.close();
return true;
return AssetUtilsInternal::DumpAssetProcessorUserSettingsToFile(*settingsRegistry, assetProcessorUserSetregPath);
}
QString ComputeJobDescription(const AssetProcessor::AssetRecognizer* recognizer)
@@ -1130,7 +992,7 @@ to ensure that the address is correct. Asset Processor won't be running in serve
AZStd::string ComputeJobLogFolder()
{
return AZStd::string::format("@log@/logs/JobLogs");
return AZStd::string::format("@log@/JobLogs");
}
AZStd::string ComputeJobLogFileName(const AzToolsFramework::AssetSystem::JobInfo& jobInfo)
@@ -1477,14 +1339,28 @@ to ensure that the address is correct. Asset Processor won't be running in serve
bool CreateTempWorkspace(QString& result)
{
// Use the engine root as a temp workspace folder
// this works better for numerous reasons
// * Its on the same drive as the /Cache/ so we will be moving files instead of copying from drive to drive
// Use the project user folder as a temp workspace folder
// The benefits are
// * It's on the same drive as the Cache/ so we will be moving files instead of copying from drive to drive
// * It is discoverable by the user and thus deletable and we can also tell people to send us that folder without them having to go digging for it
// * If you can't write to it you have much bigger problems
QDir rootDir;
if (ComputeAssetRoot(rootDir))
bool foundValidPath{};
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
if (AZ::IO::Path userPath; settingsRegistry->Get(userPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectUserPath))
{
rootDir.setPath(QString::fromUtf8(userPath.c_str(), aznumeric_cast<int>(userPath.Native().size())));
foundValidPath = true;
}
}
if (!foundValidPath)
{
foundValidPath = ComputeAssetRoot(rootDir);
}
if (foundValidPath)
{
QString tempPath = rootDir.absolutePath();
return CreateTempWorkspace(tempPath, result);
@@ -1499,7 +1375,6 @@ to ensure that the address is correct. Asset Processor won't be running in serve
QString inputName;
QString platformName;
QString jobDescription;
QString gameName = AssetUtilities::ComputeGameName();
AZ::Uuid guid = AZ::Uuid::CreateNull();
using namespace AzToolsFramework::AssetDatabase;
@@ -1513,16 +1388,16 @@ to ensure that the address is correct. Asset Processor won't be running in serve
platform = AzToolsFramework::AssetSystem::GetHostAssetPlatform();
}
QString platformPrepend = QString("%1/%2/").arg(platform, gameName);
QString productNameWithPlatformAndGameName = productName;
QString platformPrepend = QString("%1/").arg(platform);
QString productNameWithPlatform = productName;
if (!productName.startsWith(platformPrepend, Qt::CaseInsensitive))
{
productNameWithPlatformAndGameName = productName = QString("%1/%2/%3").arg(platform, gameName, productName);
productNameWithPlatform = productName = QString("%1/%2").arg(platform, productName);
}
ProductDatabaseEntryContainer products;
if (databaseConnection->GetProductsByProductName(productNameWithPlatformAndGameName, products))
if (databaseConnection->GetProductsByProductName(productNameWithPlatform, products))
{
// if we find stuff, then return immediately, productName is already a productName.
return productName;
@@ -1535,24 +1410,9 @@ to ensure that the address is correct. Asset Processor won't be running in serve
return productName;
}
if (!databaseConnection->GetProductsLikeProductName(productNameWithPlatformAndGameName, AssetDatabaseConnection::LikeType::StartsWith, products))
if (!databaseConnection->GetProductsLikeProductName(productNameWithPlatform, AssetDatabaseConnection::LikeType::StartsWith, products))
{
//if we are here it means that the asset database does not know about this product,
//we will now remove the gameName and try again ,so now the path will only have $PLATFORM/ in front of it
int gameNameIndex = productName.indexOf(gameName, 0, Qt::CaseInsensitive);
if (gameNameIndex != -1)
{
//we will now remove the gameName and the separator
productName.remove(gameNameIndex, gameName.length() + 1);// adding one for the native separator
}
//Search the database for this product
if (!databaseConnection->GetProductsLikeProductName(productName, AssetDatabaseConnection::LikeType::StartsWith, products))
{
//return empty string if the database still does not have any idea about the product
productName = QString();
}
return {};
}
return productName.toLower();
}
@@ -51,8 +51,7 @@ namespace AssetProcessor
namespace AssetUtilities
{
inline constexpr char GameFolderOverrideParameter[] = "gamefolder";
inline constexpr char ProjectPathOverrideParameter[] = "project-path";
//! Set precision fingerprint timestamps will be truncated to avoid mismatches across systems/packaging with different file timestamp precisions
//! Timestamps default to milliseconds. A value of 1 will keep the default millisecond precision. A value of 1000 will reduce the precision to seconds
@@ -79,10 +78,10 @@ namespace AssetUtilities
//! makes the file writable
//! return true if operation is successful, otherwise return false
bool MakeFileWritable(QString filename);
bool MakeFileWritable(const QString& filename);
//! Check to see if we can Lock the file
bool CheckCanLock(QString filename);
bool CheckCanLock(const QString& filename);
//! Updates the branch token in the bootstrap file
bool UpdateBranchToken();
@@ -98,11 +97,14 @@ namespace AssetUtilities
bool ShouldUseFileHashing();
//! Determine the name of the current game - for example, SamplesProject
//! Can be overridden by passing in a non-empty gameNameOverride
//! The override will persist if the GameName wasn't set previously or
//! Determine the name of the current project - for example, SamplesProject
//! Can be overridden by passing in a non-empty projectNameOverride
//! The override will persist if the project name wasn't set previously or
//! force=true is supplied
QString ComputeGameName(QString gameNameOverride = QString(), bool force = false);
QString ComputeProjectName(QString projectNameOverride = QString(), bool force = false);
//! Determine the absolute path of the current project
QString ComputeProjectPath();
//! Reads the allowed list directly from the bootstrap file
QString ReadAllowedlistFromSettingsRegistry(QString initialFolder = QString());
@@ -111,13 +113,7 @@ namespace AssetUtilities
QString ReadRemoteIpFromSettingsRegistry(QString initialFolder = QString());
//! Writes the allowed list directly to the bootstrap file
bool WriteAllowedlistToBootstrap(QStringList allowedList);
//! Writes the remote ip directly to the bootstrap file
bool WriteRemoteIpToBootstrap(QString remoteIp);
//! Reads the game name directly from the bootstrap file
QString ReadGameNameFromSettingsRegistry(QString initialFolder = QString());
bool WriteAllowedlistToSettingsRegistry(const QStringList& allowedList);
//! Reads the listening port from the bootstrap file
//! By default the listening port is 45643
@@ -143,13 +139,24 @@ namespace AssetUtilities
QString ComputeJobDescription(const AssetProcessor::AssetRecognizer* recognizer);
//! Compute the root of the cache for the current project.
//! This is generally the "cache" folder, subfolder gamedir.
//! This is generally the "<Project>/Cache" folder
bool ComputeProjectCacheRoot(QDir& projectCacheRoot);
//! Compute the folder that will be used for fence files.
bool ComputeFenceDirectory(QDir& fenceDir);
//! Converts all slashes to forward slashes, removes double slashes,
//! Strips the first "asset platform" from the first path segment of a relative product path
//! This is meant for removing the asset platform for paths such as "pc/MyAssetFolder/MyAsset.asset"
//! Therefore the result here becomes "MyAssetFolder/MyAsset"
//!
//! Similarly invoking this function on relative path that begins with the "server" platform
//! "server/AssetFolder/Server.asset2" -> "AssetFolder/Server.asset2"
//! This function does not strip an asset platform from anywhere, but the first path segment
//! Therefore invoking strip Asset on "MyProject/Cache/pc/MyAsset/MyAsset.asset"
//! would return a copy of the relative path
QString StripAssetPlatform(AZStd::string_view relativeProductPath);
//! Converts all slashes to forward slashes, removes double slashes,
//! replaces all indirections such as '.' or '..' as appropriate.
//! On windows, the drive letter (if present) is converted to uppercase.
//! Besides that, all case is preserved.