Files
o3de/Code/Tools/AssetBundler/source/utils/GUIApplicationManager.cpp
T
lumberyard-employee-dm 5fc4551ac0 [LYN-8041] Enable relocation of the Project Game Release Layout (#5380)
* Enable relocation of the Project Game Release Layout

Relocating the Project Game Release Layout to another directory on the file system failed due to the querying of the engine root failing due to the ComponentApplication::m_engineRoot not using the project path stored in the SettingsRegisry if the engine root cannot be detected

Removed the ApplicationRequestBus GetEngineRoot function.
The ComponentApplicationRequestBus has a function of the same name that returns the same path.

Removed the deprecated GetAppRoot function.
The path it returns has no defined value. It was not the engine root or the project root.
Removed unused CFileUtil and CFileUtil_impl functions that were invoking the ApplicationREquestBus GetEngineRoot function.
On the way to update the functions it was discovered that they aren't called

Added a CalculateBranchToken overload that can populate a fixed_string to avoid heap allocations

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Protect against an empty list of artifacts to remove when generating the
engine.pak

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
2021-11-09 12:03:52 -06:00

396 lines
15 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <source/utils/GUIApplicationManager.h>
#include <source/ui/MainWindow.h>
#include <source/utils/utils.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/Asset/AssetCatalog.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/AssetCatalog/PlatformAddressedAssetCatalogBus.h>
#include <AzQtComponents/Components/ConfigHelpers.h>
#include <AzQtComponents/Components/StyleManager.h>
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
#include <QApplication>
#include <QLocale>
#include <QStringList>
// Forward declare platform-specific functions
namespace Platform
{
/// On Windows this will return the setting for our custom title bar
/// On other platforms this will return the setting for using platform default
/// This ensures that functions like Exit, Maximize, and Minimize appear in the right platform-specific style
AzQtComponents::WindowDecorationWrapper::Option GetWindowDecorationWrapperOption();
} // namespace Platform
const char AssetBundlingFolderName [] = "AssetBundling";
const char SeedListsFolderName [] = "SeedLists";
const char AssetListsFolderName [] = "AssetLists";
const char RulesFolderName[] = "Rules";
const char BundleSettingsFolderName [] = "BundleSettings";
const char BundlesFolderName [] = "Bundles";
namespace AssetBundler
{
GUIApplicationManager::Config GUIApplicationManager::loadConfig(QSettings& settings)
{
using namespace AzQtComponents;
Config config = defaultConfig();
// Error Log
{
ConfigHelpers::GroupGuard details(&settings, QStringLiteral("ErrorLogDetails"));
ConfigHelpers::read<int>(settings, QStringLiteral("LogTypeColumnWidth"), config.logTypeColumnWidth);
ConfigHelpers::read<int>(settings, QStringLiteral("LogSourceColumnWidth"), config.logSourceColumnWidth);
}
// General File Tables
{
ConfigHelpers::GroupGuard details(&settings, QStringLiteral("GeneralFileTableDetails"));
ConfigHelpers::read<int>(settings, QStringLiteral("FileTableWidth"), config.fileTableWidth);
ConfigHelpers::read<int>(settings, QStringLiteral("FileNameColumnWidth"), config.fileNameColumnWidth);
}
// Seeds Tab
{
ConfigHelpers::GroupGuard details(&settings, QStringLiteral("SeedsTabDetails"));
ConfigHelpers::read<int>(settings, QStringLiteral("CheckBoxColumnWidth"), config.checkBoxColumnWidth);
ConfigHelpers::read<int>(settings, QStringLiteral("SeedListFileNameColumnWidth"), config.seedListFileNameColumnWidth);
ConfigHelpers::read<int>(settings, QStringLiteral("ProjectNameColumnWidth"), config.projectNameColumnWidth);
ConfigHelpers::read<int>(settings, QStringLiteral("SeedListContentsNameColumnWidth"), config.seedListContentsNameColumnWidth);
}
// Asset Lists Tab
{
ConfigHelpers::GroupGuard details(&settings, QStringLiteral("AssetListsTabDetails"));
ConfigHelpers::read<int>(settings, QStringLiteral("AssetListFileNameColumnWidth"), config.assetListFileNameColumnWidth);
ConfigHelpers::read<int>(settings, QStringLiteral("AssetListPlatformColumnWidth"), config.assetListPlatformColumnWidth);
ConfigHelpers::read<int>(settings, QStringLiteral("ProductAssetNameColumnWidth"), config.productAssetNameColumnWidth);
ConfigHelpers::read<int>(
settings, QStringLiteral("ProductAssetRelativePathColumnWidth"), config.productAssetRelativePathColumnWidth);
}
return config;
}
GUIApplicationManager::Config GUIApplicationManager::defaultConfig()
{
// These are used if the values can't be read from AssetBundlerConfig.ini.
Config config;
config.logTypeColumnWidth = 150;
config.logSourceColumnWidth = 150;
config.fileTableWidth = 250;
config.fileNameColumnWidth = 150;
config.checkBoxColumnWidth = 150;
config.seedListFileNameColumnWidth = 150;
config.projectNameColumnWidth = 150;
config.seedListContentsNameColumnWidth = 150;
config.assetListFileNameColumnWidth = 150;
config.assetListPlatformColumnWidth = 150;
config.productAssetNameColumnWidth = 150;
config.productAssetRelativePathColumnWidth = 150;
return config;
}
GUIApplicationManager::GUIApplicationManager(int* argc, char*** argv, QObject* parent)
: ApplicationManager(argc, argv, parent)
{
}
GUIApplicationManager::~GUIApplicationManager()
{
// Reset this before DestroyApplication, BusDisconnect needs to happen before Application::Stop() destroys the allocators.
m_platformCatalogManager.reset();
}
bool GUIApplicationManager::Init()
{
m_isInitializing = true;
// Initialize Asset Bundler Batch
ApplicationManager::Init();
AZ::IO::FixedMaxPath engineRoot = GetEngineRoot();
if (engineRoot.empty())
{
// Error has already been thrown
return false;
}
// Determine the name of the current project
auto projectOutcome = AssetBundler::GetCurrentProjectName();
if (!projectOutcome.IsSuccess())
{
AZ_Error("AssetBundler", false, projectOutcome.GetError().c_str());
return false;
}
m_currentProjectName = projectOutcome.GetValue();
// Set up paths to the Project folder, Project Cache folder, and determine enabled platforms
auto pathOutcome = InitializePaths();
if (!pathOutcome.IsSuccess())
{
AZ_Error("AssetBundler", false, pathOutcome.GetError().c_str());
return false;
}
// Set up platform-specific Asset Catalogs
m_platformCatalogManager = AZStd::make_unique<AzToolsFramework::PlatformAddressedAssetCatalogManager>();
// Define some application-level settings
QApplication::setOrganizationName("O3DE");
QApplication::setApplicationName("Asset Bundler");
QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates));
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
m_isInitializing = false;
// Create the actual Qt Application
m_qApp.reset(new QApplication(*GetArgC(), *GetArgV()));
// Create the Main Window
m_mainWindow.reset(new MainWindow(this));
return true;
}
bool GUIApplicationManager::Run()
{
// Set up the Style Manager
AzQtComponents::StyleManager styleManager(qApp);
styleManager.initialize(qApp, GetEngineRoot());
AZ::IO::FixedMaxPath engineRoot(GetEngineRoot());
QDir engineRootDir(engineRoot.c_str());
AzQtComponents::StyleManager::addSearchPaths(
QStringLiteral("style"),
engineRootDir.filePath(QStringLiteral("Code/Tools/AssetBundler/source/ui/style")),
QStringLiteral(":/AssetBundler/style"),
engineRoot);
AzQtComponents::StyleManager::setStyleSheet(m_mainWindow.data(), QStringLiteral("style:AssetBundler.qss"));
AzQtComponents::ConfigHelpers::loadConfig<Config, GUIApplicationManager>(
&m_fileWatcher,
&m_config,
QStringLiteral("style:AssetBundlerConfig.ini"),
this,
std::bind(&GUIApplicationManager::ApplyConfig, this));
ApplyConfig();
qApp->setWindowIcon(QIcon("style:AssetBundler-Icon-256x256@x2.ico"));
// Set up the Main Window
auto wrapper = new AzQtComponents::WindowDecorationWrapper(Platform::GetWindowDecorationWrapperOption());
wrapper->setGuest(m_mainWindow.data());
m_mainWindow->Activate();
wrapper->show();
m_mainWindow->show();
qApp->setQuitOnLastWindowClosed(true);
// Run the application
return qApp->exec();
}
void GUIApplicationManager::AddWatchedPath(const QString& folderPath)
{
m_fileWatcher.addPath(folderPath);
}
void GUIApplicationManager::AddWatchedPaths(const QSet<QString>& folderPaths)
{
m_fileWatcher.addPaths(folderPaths.values());
}
void GUIApplicationManager::RemoveWatchedPath(const QString& path)
{
m_fileWatcher.removePath(path);
}
void GUIApplicationManager::RemoveWatchedPaths(const QSet<QString>& paths)
{
// Check whether the list is empty to get rid of the warning from Qt
if (paths.isEmpty())
{
return;
}
m_fileWatcher.removePaths(paths.values());
}
bool GUIApplicationManager::OnPreError(
const char* /*window*/,
const char* /*fileName*/,
int /*line*/,
const char* /*func*/,
const char* message)
{
// We want to display errors during initialization, then let the MainWindow handle errors during runtime
if (m_isInitializing)
{
// These are fatal initialization errors, and the application will shut down after the user closes the message box
m_qApp.reset(new QApplication(*GetArgC(), *GetArgV()));
QMessageBox errorMessageBox;
errorMessageBox.setWindowTitle("Asset Bundler");
errorMessageBox.setText(message);
errorMessageBox.setStandardButtons(QMessageBox::Ok);
errorMessageBox.setDefaultButton(QMessageBox::Ok);
errorMessageBox.exec();
return true;
}
return false;
}
bool GUIApplicationManager::OnPreWarning(
const char* /*window*/,
const char* /*fileName*/,
int /*line*/,
const char* /*func*/,
const char* /*message*/)
{
// Don't handle warnings, let the MainWindow print them
return false;
}
bool GUIApplicationManager::OnPrintf(const char* /*window*/, const char* /*message*/)
{
// This is disabled during initialization to prevent a lot of message spam printed to the CLI that gets generated on setup
return m_isInitializing;
}
AZ::Outcome<void, AZStd::string> GUIApplicationManager::InitializePaths()
{
// Calculate the path to the Cache for the current project
auto pathOutcome = AssetBundler::GetProjectCacheFolderPath();
if (!pathOutcome.IsSuccess())
{
return AZ::Failure(pathOutcome.GetError());
}
m_currentProjectCacheFolder = pathOutcome.GetValue().String();
// Calculate the path to the current project folder
pathOutcome = AssetBundler::GetProjectFolderPath();
if (!pathOutcome.IsSuccess())
{
return AZ::Failure(pathOutcome.GetError());
}
m_currentProjectFolder = pathOutcome.GetValue().String();
// Generate the AssetBundling folder inside the current project
AzFramework::StringFunc::Path::ConstructFull(m_currentProjectFolder.c_str(), AssetBundlingFolderName, m_assetBundlingFolder);
// Seed Lists folder
AzFramework::StringFunc::Path::ConstructFull(m_assetBundlingFolder.c_str(), SeedListsFolderName, m_seedListsFolder);
AZ::Outcome<void, AZStd::string> createPathOutcome = AssetBundler::MakePath(m_seedListsFolder);
if (!createPathOutcome.IsSuccess())
{
return createPathOutcome;
}
// Asset Lists folder
AzFramework::StringFunc::Path::ConstructFull(m_assetBundlingFolder.c_str(), AssetListsFolderName, m_assetListsFolder);
createPathOutcome = AssetBundler::MakePath(m_assetListsFolder);
if (!createPathOutcome.IsSuccess())
{
return createPathOutcome;
}
// Rules folder
AzFramework::StringFunc::Path::ConstructFull(m_assetBundlingFolder.c_str(), RulesFolderName, m_rulesFolder);
createPathOutcome = AssetBundler::MakePath(m_rulesFolder);
if (!createPathOutcome.IsSuccess())
{
return createPathOutcome;
}
// Bundle Settings Folder
AzFramework::StringFunc::Path::ConstructFull(m_assetBundlingFolder.c_str(), BundleSettingsFolderName, m_bundleSettingsFolder);
createPathOutcome = AssetBundler::MakePath(m_bundleSettingsFolder);
if (!createPathOutcome.IsSuccess())
{
return createPathOutcome;
}
// Bundles Folder
AzFramework::StringFunc::Path::ConstructFull(m_assetBundlingFolder.c_str(), BundlesFolderName, m_bundlesFolder);
createPathOutcome = AssetBundler::MakePath(m_bundlesFolder);
if (!createPathOutcome.IsSuccess())
{
return createPathOutcome;
}
// Determine the enabled platforms
m_enabledPlatforms = GetEnabledPlatformFlags(GetEngineRoot(), AZStd::string_view(AZ::Utils::GetProjectPath()));
// Determine which Gems are enabled for the current project
if (!AzFramework::GetGemsInfo(m_gemInfoList, *m_settingsRegistry))
{
return AZ::Failure(AZStd::string::format("Failed to read Gems for project: %s\n", m_currentProjectName.c_str()));
}
QObject::connect(&m_fileWatcher, &QFileSystemWatcher::directoryChanged, this, &GUIApplicationManager::DirectoryChanged);
QObject::connect(&m_fileWatcher, &QFileSystemWatcher::fileChanged, this, &GUIApplicationManager::FileChanged);
return AZ::Success();
}
void GUIApplicationManager::DirectoryChanged(const QString& directory)
{
UpdateTab(directory.toUtf8().data());
}
void GUIApplicationManager::FileChanged(const QString& path)
{
// FileChanged will only be called when engine or gem seed files are updated
// Otherwilse DirectoryChanged should be triggered
AZStd::string extension;
AzFramework::StringFunc::Path::GetExtension(path.toUtf8().data(), extension);
extension = extension.starts_with(".") ? extension.substr(1) : extension;
if (extension == AzToolsFramework::AssetSeedManager::GetSeedFileExtension())
{
UpdateTab(GetSeedListsFolder());
}
// Many applications save an open file by writing a new file and then deleting the old one
// Add the file path back if it has been removed from the watcher file list
if (!m_fileWatcher.files().contains(path))
{
m_fileWatcher.addPath(path);
}
}
void GUIApplicationManager::ApplyConfig()
{
m_mainWindow->ApplyConfig();
}
} // namespace AssetBundler
#include <source/utils/moc_GUIApplicationManager.cpp>