GUI for engine force registration and renaming (#6184)

Signed-off-by: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com>
monroegm-disable-blank-issue-2
Alex Peterson 4 years ago committed by GitHub
parent 72b5539259
commit 3405cee2ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -21,6 +21,7 @@
#include <QApplication>
#include <QDir>
#include <QMessageBox>
#include <QInputDialog>
namespace O3DE::ProjectManager
{
@ -111,6 +112,11 @@ namespace O3DE::ProjectManager
}
}
if (!RegisterEngine(interactive))
{
return false;
}
const AZ::CommandLine* commandLine = GetCommandLine();
AZ_Assert(commandLine, "Failed to get command line");
@ -165,6 +171,86 @@ namespace O3DE::ProjectManager
return m_entity != nullptr;
}
bool Application::RegisterEngine(bool interactive)
{
// get this engine's info
auto engineInfoOutcome = m_pythonBindings->GetEngineInfo();
if (!engineInfoOutcome)
{
if (interactive)
{
QMessageBox::critical(nullptr,
QObject::tr("Failed to get engine info"),
QObject::tr("A valid engine.json could not be found or loaded. "
"Please verify a valid engine.json file exists in %1")
.arg(GetEngineRoot()));
}
AZ_Error("Project Manager", false, "Failed to get engine info");
return false;
}
EngineInfo engineInfo = engineInfoOutcome.GetValue();
if (engineInfo.m_registered)
{
return true;
}
bool forceRegistration = false;
// check if an engine with this name is already registered
auto existingEngineResult = m_pythonBindings->GetEngineInfo(engineInfo.m_name);
if (existingEngineResult)
{
if (!interactive)
{
AZ_Error("Project Manager", false, "An engine with the name %s is already registered with the path %s",
engineInfo.m_name.toUtf8().constData(), engineInfo.m_path.toUtf8().constData());
return false;
}
// get the updated engine name unless the user wants to cancel
bool okPressed = false;
const EngineInfo& otherEngineInfo = existingEngineResult.GetValue();
engineInfo.m_name = QInputDialog::getText(nullptr,
QObject::tr("Engine '%1' already registered").arg(engineInfo.m_name),
QObject::tr("An engine named '%1' is already registered.<br /><br />"
"<b>Current path</b><br />%2<br/><br />"
"<b>New path</b><br />%3<br /><br />"
"Press 'OK' to force registration, or provide a new engine name below.<br />"
"Alternatively, press `Cancel` to close the Project Manager and resolve the issue manually.")
.arg(engineInfo.m_name, otherEngineInfo.m_path, engineInfo.m_path),
QLineEdit::Normal,
engineInfo.m_name,
&okPressed);
if (!okPressed)
{
// user elected not to change the name or force registration
return false;
}
forceRegistration = true;
}
auto registerOutcome = m_pythonBindings->SetEngineInfo(engineInfo, forceRegistration);
if (!registerOutcome)
{
if (interactive)
{
ProjectUtils::DisplayDetailedError(QObject::tr("Failed to register engine"), registerOutcome);
}
AZ_Error("Project Manager", false, "Failed to register engine %s : %s",
engineInfo.m_path.toUtf8().constData(), registerOutcome.GetError().first.c_str());
return false;
}
return true;
}
void Application::TearDown()
{
if (m_entity)

@ -34,6 +34,7 @@ namespace O3DE::ProjectManager
private:
bool InitLog(const char* logName);
bool RegisterEngine(bool interactive);
AZStd::unique_ptr<PythonBindings> m_pythonBindings;
QSharedPointer<QCoreApplication> m_app;

@ -25,13 +25,16 @@ namespace O3DE::ProjectManager
QString m_name;
QString m_thirdPartyPath;
// from o3de_manifest.json
QString m_path;
// from o3de_manifest.json
QString m_defaultProjectsFolder;
QString m_defaultGemsFolder;
QString m_defaultTemplatesFolder;
QString m_defaultRestrictedFolder;
bool m_registered = false;
bool IsValid() const;
};
} // namespace O3DE::ProjectManager

@ -11,6 +11,7 @@
#include <FormFolderBrowseEditWidget.h>
#include <PythonBindingsInterface.h>
#include <PathValidator.h>
#include <ProjectUtils.h>
#include <AzQtComponents/Utilities/DesktopUtilities.h>
#include <QVBoxLayout>
@ -114,10 +115,10 @@ namespace O3DE::ProjectManager
engineInfo.m_defaultGemsFolder = m_defaultGems->lineEdit()->text();
engineInfo.m_defaultTemplatesFolder = m_defaultProjectTemplates->lineEdit()->text();
bool result = PythonBindingsInterface::Get()->SetEngineInfo(engineInfo);
auto result = PythonBindingsInterface::Get()->SetEngineInfo(engineInfo);
if (!result)
{
QMessageBox::critical(this, tr("Engine Settings"), tr("Failed to save engine settings."));
ProjectUtils::DisplayDetailedError(tr("Failed to save engine settings"), result, this);
}
}
else

@ -14,6 +14,7 @@
#include <GemRepo/GemRepoInspector.h>
#include <PythonBindingsInterface.h>
#include <ProjectManagerDefs.h>
#include <ProjectUtils.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
@ -92,8 +93,7 @@ namespace O3DE::ProjectManager
return;
}
AZ::Outcome < void,
AZStd::pair<AZStd::string, AZStd::string>> addGemRepoResult = PythonBindingsInterface::Get()->AddGemRepo(repoUri);
auto addGemRepoResult = PythonBindingsInterface::Get()->AddGemRepo(repoUri);
if (addGemRepoResult.IsSuccess())
{
Reinit();
@ -102,20 +102,7 @@ namespace O3DE::ProjectManager
else
{
QString failureMessage = tr("Failed to add gem repo: %1.").arg(repoUri);
if (!addGemRepoResult.GetError().second.empty())
{
QMessageBox addRepoError;
addRepoError.setIcon(QMessageBox::Critical);
addRepoError.setWindowTitle(failureMessage);
addRepoError.setText(addGemRepoResult.GetError().first.c_str());
addRepoError.setDetailedText(addGemRepoResult.GetError().second.c_str());
addRepoError.exec();
}
else
{
QMessageBox::critical(this, failureMessage, addGemRepoResult.GetError().first.c_str());
}
ProjectUtils::DisplayDetailedError(failureMessage, addGemRepoResult, this);
AZ_Error("Project Manager", false, failureMessage.toUtf8());
}
}

@ -659,5 +659,24 @@ namespace O3DE::ProjectManager
return AZ::Success(QString(projectBuildPath.c_str()));
}
void DisplayDetailedError(const QString& title, const AZ::Outcome<void, AZStd::pair<AZStd::string, AZStd::string>>& outcome, QWidget* parent)
{
const AZStd::string& generalError = outcome.GetError().first;
const AZStd::string& detailedError = outcome.GetError().second;
if (!detailedError.empty())
{
QMessageBox errorDialog(parent);
errorDialog.setIcon(QMessageBox::Critical);
errorDialog.setWindowTitle(title);
errorDialog.setText(generalError.c_str());
errorDialog.setDetailedText(detailedError.c_str());
errorDialog.exec();
}
else
{
QMessageBox::critical(parent, title, generalError.c_str());
}
}
} // namespace ProjectUtils
} // namespace O3DE::ProjectManager

@ -98,5 +98,14 @@ namespace O3DE::ProjectManager
*/
AZ::IO::FixedMaxPath GetEditorExecutablePath(const AZ::IO::PathView& projectPath);
/**
* Display a dialog with general and detailed sections for the given AZ::Outcome
* @param title Dialog title
* @param outcome The AZ::Outcome with general and detailed error messages
* @param parent Optional QWidget parent
*/
void DisplayDetailedError(const QString& title, const AZ::Outcome<void, AZStd::pair<AZStd::string, AZStd::string>>& outcome, QWidget* parent = nullptr);
} // namespace ProjectUtils
} // namespace O3DE::ProjectManager

@ -312,6 +312,7 @@ namespace O3DE::ProjectManager
m_register = pybind11::module::import("o3de.register");
m_manifest = pybind11::module::import("o3de.manifest");
m_engineTemplate = pybind11::module::import("o3de.engine_template");
m_engineProperties = pybind11::module::import("o3de.engine_properties");
m_enableGemProject = pybind11::module::import("o3de.enable_gem");
m_disableGemProject = pybind11::module::import("o3de.disable_gem");
m_editProjectProperties = pybind11::module::import("o3de.project_properties");
@ -319,9 +320,6 @@ namespace O3DE::ProjectManager
m_repo = pybind11::module::import("o3de.repo");
m_pathlib = pybind11::module::import("pathlib");
// make sure the engine is registered
RegisterThisEngine();
m_pythonStarted = !PyErr_Occurred();
return m_pythonStarted;
}
@ -346,36 +344,6 @@ namespace O3DE::ProjectManager
return !PyErr_Occurred();
}
bool PythonBindings::RegisterThisEngine()
{
bool registrationResult = true; // already registered is considered successful
bool pythonResult = ExecuteWithLock(
[&]
{
// check current engine path against all other registered engines
// to see if we are already registered
auto allEngines = m_manifest.attr("get_engines")();
if (pybind11::isinstance<pybind11::list>(allEngines))
{
for (auto engine : allEngines)
{
AZ::IO::FixedMaxPath enginePath(Py_To_String(engine));
if (enginePath.Compare(m_enginePath) == 0)
{
return;
}
}
}
auto result = m_register.attr("register")(QString_To_Py_Path(QString(m_enginePath.c_str())));
registrationResult = (result.cast<int>() == 0);
});
bool finalResult = (registrationResult && pythonResult);
AZ_Assert(finalResult, "Registration of this engine failed!");
return finalResult;
}
AZ::Outcome<void, AZStd::string> PythonBindings::ExecuteWithLockErrorHandling(AZStd::function<void()> executionCallback)
{
if (!Py_IsInitialized())
@ -407,16 +375,22 @@ namespace O3DE::ProjectManager
return ExecuteWithLockErrorHandling(executionCallback).IsSuccess();
}
AZ::Outcome<EngineInfo> PythonBindings::GetEngineInfo()
EngineInfo PythonBindings::EngineInfoFromPath(pybind11::handle enginePath)
{
EngineInfo engineInfo;
bool result = ExecuteWithLock([&] {
auto enginePath = m_manifest.attr("get_this_engine_path")();
try
{
auto engineData = m_manifest.attr("get_engine_json_data")(pybind11::none(), enginePath);
if (pybind11::isinstance<pybind11::dict>(engineData))
{
engineInfo.m_version = Py_To_String_Optional(engineData, "O3DEVersion", "0.0.0.0");
engineInfo.m_name = Py_To_String_Optional(engineData, "engine_name", "O3DE");
engineInfo.m_path = Py_To_String(enginePath);
}
auto o3deData = m_manifest.attr("load_o3de_manifest")();
if (pybind11::isinstance<pybind11::dict>(o3deData))
{
engineInfo.m_path = Py_To_String(enginePath);
auto defaultGemsFolder = m_manifest.attr("get_o3de_gems_folder")();
engineInfo.m_defaultGemsFolder = Py_To_String_Optional(o3deData, "default_gems_folder", Py_To_String(defaultGemsFolder));
@ -433,19 +407,36 @@ namespace O3DE::ProjectManager
engineInfo.m_thirdPartyPath = Py_To_String_Optional(o3deData, "default_third_party_folder", Py_To_String(defaultThirdPartyFolder));
}
auto engineData = m_manifest.attr("get_engine_json_data")(pybind11::none(), enginePath);
if (pybind11::isinstance<pybind11::dict>(engineData))
// check if engine path is registered
auto allEngines = m_manifest.attr("get_engines")();
if (pybind11::isinstance<pybind11::list>(allEngines))
{
try
{
engineInfo.m_version = Py_To_String_Optional(engineData, "O3DEVersion", "0.0.0.0");
engineInfo.m_name = Py_To_String_Optional(engineData, "engine_name", "O3DE");
}
catch ([[maybe_unused]] const std::exception& e)
const AZ::IO::FixedMaxPath enginePathFixed(Py_To_String(enginePath));
for (auto engine : allEngines)
{
AZ_Warning("PythonBindings", false, "Failed to get EngineInfo from %s", Py_To_String(enginePath));
AZ::IO::FixedMaxPath otherEnginePath(Py_To_String(engine));
if (otherEnginePath.Compare(enginePathFixed) == 0)
{
engineInfo.m_registered = true;
break;
}
}
}
}
catch ([[maybe_unused]] const std::exception& e)
{
AZ_Warning("PythonBindings", false, "Failed to get EngineInfo from %s", Py_To_String(enginePath));
}
return engineInfo;
}
AZ::Outcome<EngineInfo> PythonBindings::GetEngineInfo()
{
EngineInfo engineInfo;
bool result = ExecuteWithLock([&] {
auto enginePath = m_manifest.attr("get_this_engine_path")();
engineInfo = EngineInfoFromPath(enginePath);
});
if (!result || !engineInfo.IsValid())
@ -458,10 +449,55 @@ namespace O3DE::ProjectManager
}
}
bool PythonBindings::SetEngineInfo(const EngineInfo& engineInfo)
AZ::Outcome<EngineInfo> PythonBindings::GetEngineInfo(const QString& engineName)
{
EngineInfo engineInfo;
bool result = ExecuteWithLock([&] {
auto registrationResult = m_register.attr("register")(
auto enginePathResult = m_manifest.attr("get_registered")(QString_To_Py_String(engineName));
// if a valid registered object is not found None is returned
if (!pybind11::isinstance<pybind11::none>(enginePathResult))
{
engineInfo = EngineInfoFromPath(enginePathResult);
}
});
if (!result || !engineInfo.IsValid())
{
return AZ::Failure();
}
else
{
return AZ::Success(AZStd::move(engineInfo));
}
}
IPythonBindings::DetailedOutcome PythonBindings::SetEngineInfo(const EngineInfo& engineInfo, bool force)
{
bool registrationSuccess = false;
bool pythonSuccess = ExecuteWithLock([&] {
EngineInfo currentEngine = EngineInfoFromPath(QString_To_Py_Path(engineInfo.m_path));
// be kind to source control and avoid needlessly updating engine.json
if (currentEngine.IsValid() &&
(currentEngine.m_name.compare(engineInfo.m_name) != 0 || currentEngine.m_version.compare(engineInfo.m_version) != 0))
{
auto enginePropsResult = m_engineProperties.attr("edit_engine_props")(
QString_To_Py_Path(engineInfo.m_path),
pybind11::none(), // existing engine_name
QString_To_Py_String(engineInfo.m_name),
QString_To_Py_String(engineInfo.m_version)
);
if (enginePropsResult.cast<int>() != 0)
{
// do not proceed with registration
return;
}
}
auto result = m_register.attr("register")(
QString_To_Py_Path(engineInfo.m_path),
pybind11::none(), // project_path
pybind11::none(), // gem_path
@ -474,16 +510,22 @@ namespace O3DE::ProjectManager
QString_To_Py_Path(engineInfo.m_defaultGemsFolder),
QString_To_Py_Path(engineInfo.m_defaultTemplatesFolder),
pybind11::none(), // default_restricted_folder
QString_To_Py_Path(engineInfo.m_thirdPartyPath)
);
QString_To_Py_Path(engineInfo.m_thirdPartyPath),
pybind11::none(), // external_subdir_engine_path
pybind11::none(), // external_subdir_project_path
false, // remove
force
);
if (registrationResult.cast<int>() != 0)
{
result = false;
}
registrationSuccess = result.cast<int>() == 0;
});
return result;
if (pythonSuccess && registrationSuccess)
{
return AZ::Success();
}
return AZ::Failure<ErrorPair>(GetErrorPair());
}
AZ::Outcome<GemInfo> PythonBindings::GetGemInfo(const QString& path, const QString& projectPath)
@ -1064,7 +1106,7 @@ namespace O3DE::ProjectManager
return result && refreshResult;
}
AZ::Outcome<void, AZStd::pair<AZStd::string, AZStd::string>> PythonBindings::AddGemRepo(const QString& repoUri)
IPythonBindings::DetailedOutcome PythonBindings::AddGemRepo(const QString& repoUri)
{
bool registrationResult = false;
bool result = ExecuteWithLock(
@ -1080,7 +1122,7 @@ namespace O3DE::ProjectManager
if (!result || !registrationResult)
{
return AZ::Failure<AZStd::pair<AZStd::string, AZStd::string>>(GetSimpleDetailedErrorPair());
return AZ::Failure<IPythonBindings::ErrorPair>(GetErrorPair());
}
return AZ::Success();
@ -1170,13 +1212,10 @@ namespace O3DE::ProjectManager
return gemRepoInfo;
}
//#define MOCK_GEM_REPO_INFO true
AZ::Outcome<QVector<GemRepoInfo>, AZStd::string> PythonBindings::GetAllGemRepoInfos()
{
QVector<GemRepoInfo> gemRepos;
#ifndef MOCK_GEM_REPO_INFO
auto result = ExecuteWithLockErrorHandling(
[&]
{
@ -1189,18 +1228,6 @@ namespace O3DE::ProjectManager
{
return AZ::Failure<AZStd::string>(result.GetError().c_str());
}
#else
GemRepoInfo mockJohnRepo("JohnCreates", "John Smith", QDateTime(QDate(2021, 8, 31), QTime(11, 57)), true);
mockJohnRepo.m_summary = "John's Summary. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sollicitudin dapibus urna";
mockJohnRepo.m_repoUri = "https://github.com/o3de/o3de";
mockJohnRepo.m_additionalInfo = "John's additional info. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sollicitu.";
gemRepos.push_back(mockJohnRepo);
GemRepoInfo mockJaneRepo("JanesGems", "Jane Doe", QDateTime(QDate(2021, 9, 10), QTime(18, 23)), false);
mockJaneRepo.m_summary = "Jane's Summary.";
mockJaneRepo.m_repoUri = "https://github.com/o3de/o3de.org";
gemRepos.push_back(mockJaneRepo);
#endif // MOCK_GEM_REPO_INFO
std::sort(gemRepos.begin(), gemRepos.end());
return AZ::Success(AZStd::move(gemRepos));
@ -1261,7 +1288,7 @@ namespace O3DE::ProjectManager
return AZ::Success(AZStd::move(gemInfos));
}
AZ::Outcome<void, AZStd::pair<AZStd::string, AZStd::string>> PythonBindings::DownloadGem(
IPythonBindings::DetailedOutcome PythonBindings::DownloadGem(
const QString& gemName, std::function<void(int, int)> gemProgressCallback, bool force)
{
// This process is currently limited to download a single gem at a time.
@ -1290,12 +1317,12 @@ namespace O3DE::ProjectManager
if (!result.IsSuccess())
{
AZStd::pair<AZStd::string, AZStd::string> pythonRunError(result.GetError(), result.GetError());
return AZ::Failure<AZStd::pair<AZStd::string, AZStd::string>>(AZStd::move(pythonRunError));
IPythonBindings::ErrorPair pythonRunError(result.GetError(), result.GetError());
return AZ::Failure<IPythonBindings::ErrorPair>(AZStd::move(pythonRunError));
}
else if (!downloadSucceeded)
{
return AZ::Failure<AZStd::pair<AZStd::string, AZStd::string>>(GetSimpleDetailedErrorPair());
return AZ::Failure<IPythonBindings::ErrorPair>(GetErrorPair());
}
return AZ::Success();
@ -1322,13 +1349,13 @@ namespace O3DE::ProjectManager
return result && updateAvaliableResult;
}
AZStd::pair<AZStd::string, AZStd::string> PythonBindings::GetSimpleDetailedErrorPair()
IPythonBindings::ErrorPair PythonBindings::GetErrorPair()
{
AZStd::string detailedString = m_pythonErrorStrings.size() == 1
? ""
: AZStd::accumulate(m_pythonErrorStrings.begin(), m_pythonErrorStrings.end(), AZStd::string(""));
return AZStd::pair<AZStd::string, AZStd::string>(m_pythonErrorStrings.front(), detailedString);
return IPythonBindings::ErrorPair(m_pythonErrorStrings.front(), detailedString);
}
void PythonBindings::AddErrorString(AZStd::string errorString)

@ -35,7 +35,8 @@ namespace O3DE::ProjectManager
// Engine
AZ::Outcome<EngineInfo> GetEngineInfo() override;
bool SetEngineInfo(const EngineInfo& engineInfo) override;
AZ::Outcome<EngineInfo> GetEngineInfo(const QString& engineName) override;
DetailedOutcome SetEngineInfo(const EngineInfo& engineInfo, bool force = false) override;
// Gem
AZ::Outcome<GemInfo> GetGemInfo(const QString& path, const QString& projectPath = {}) override;
@ -62,12 +63,12 @@ namespace O3DE::ProjectManager
// Gem Repos
AZ::Outcome<void, AZStd::string> RefreshGemRepo(const QString& repoUri) override;
bool RefreshAllGemRepos() override;
AZ::Outcome<void, AZStd::pair<AZStd::string, AZStd::string>> AddGemRepo(const QString& repoUri) override;
DetailedOutcome AddGemRepo(const QString& repoUri) override;
bool RemoveGemRepo(const QString& repoUri) override;
AZ::Outcome<QVector<GemRepoInfo>, AZStd::string> GetAllGemRepoInfos() override;
AZ::Outcome<QVector<GemInfo>, AZStd::string> GetGemInfosForRepo(const QString& repoUri) override;
AZ::Outcome<QVector<GemInfo>, AZStd::string> GetGemInfosForAllRepos() override;
AZ::Outcome<void, AZStd::pair<AZStd::string, AZStd::string>> DownloadGem(
DetailedOutcome DownloadGem(
const QString& gemName, std::function<void(int, int)> gemProgressCallback, bool force = false) override;
void CancelDownload() override;
bool IsGemUpdateAvaliable(const QString& gemName, const QString& lastUpdated) override;
@ -80,14 +81,14 @@ namespace O3DE::ProjectManager
AZ::Outcome<void, AZStd::string> ExecuteWithLockErrorHandling(AZStd::function<void()> executionCallback);
bool ExecuteWithLock(AZStd::function<void()> executionCallback);
EngineInfo EngineInfoFromPath(pybind11::handle enginePath);
GemInfo GemInfoFromPath(pybind11::handle path, pybind11::handle pyProjectPath);
GemRepoInfo GetGemRepoInfo(pybind11::handle repoUri);
ProjectInfo ProjectInfoFromPath(pybind11::handle path);
ProjectTemplateInfo ProjectTemplateInfoFromPath(pybind11::handle path, pybind11::handle pyProjectPath);
AZ::Outcome<void, AZStd::string> GemRegistration(const QString& gemPath, const QString& projectPath, bool remove = false);
bool RegisterThisEngine();
bool StopPython();
AZStd::pair<AZStd::string, AZStd::string> GetSimpleDetailedErrorPair();
IPythonBindings::ErrorPair GetErrorPair();
bool m_pythonStarted = false;
@ -96,6 +97,7 @@ namespace O3DE::ProjectManager
AZStd::recursive_mutex m_lock;
pybind11::handle m_engineTemplate;
pybind11::handle m_engineProperties;
pybind11::handle m_cmake;
pybind11::handle m_register;
pybind11::handle m_manifest;

@ -31,6 +31,10 @@ namespace O3DE::ProjectManager
IPythonBindings() = default;
virtual ~IPythonBindings() = default;
//! First string in pair is general error, second is detailed
using ErrorPair = AZStd::pair<AZStd::string, AZStd::string>;
using DetailedOutcome = AZ::Outcome<void, ErrorPair>;
/**
* Get whether Python was started or not. All Python functionality will fail if Python
* failed to start.
@ -49,17 +53,25 @@ namespace O3DE::ProjectManager
// Engine
/**
* Get info about the engine
* Get info about the current engine
* @return an outcome with EngineInfo on success
*/
virtual AZ::Outcome<EngineInfo> GetEngineInfo() = 0;
/**
* Get info about an engine by name
* @param engineName The name of the engine to get info about
* @return an outcome with EngineInfo on success
*/
virtual AZ::Outcome<EngineInfo> GetEngineInfo(const QString& engineName) = 0;
/**
* Set info about the engine
* @param force True to force registration even if an engine with the same name is already registered
* @param engineInfo an EngineInfo object
* @return a detailed error outcome on failure.
*/
virtual bool SetEngineInfo(const EngineInfo& engineInfo) = 0;
virtual DetailedOutcome SetEngineInfo(const EngineInfo& engineInfo, bool force = false) = 0;
// Gems
@ -202,7 +214,7 @@ namespace O3DE::ProjectManager
* @param repoUri the absolute filesystem path or url to the gem repo.
* @return an outcome with a pair of string error and detailed messages on failure.
*/
virtual AZ::Outcome<void, AZStd::pair<AZStd::string, AZStd::string>> AddGemRepo(const QString& repoUri) = 0;
virtual DetailedOutcome AddGemRepo(const QString& repoUri) = 0;
/**
* Unregisters this gem repo with the current engine.
@ -237,7 +249,7 @@ namespace O3DE::ProjectManager
* @param force should we forcibly overwrite the old version of the gem.
* @return an outcome with a pair of string error and detailed messages on failure.
*/
virtual AZ::Outcome<void, AZStd::pair<AZStd::string, AZStd::string>> DownloadGem(
virtual DetailedOutcome DownloadGem(
const QString& gemName, std::function<void(int, int)> gemProgressCallback, bool force = false) = 0;
/**

@ -26,6 +26,11 @@ def edit_engine_props(engine_path: pathlib.Path = None,
if not engine_path and not engine_name:
logger.error(f'Either a engine path or a engine name must be supplied to lookup engine.json')
return 1
if not new_name and not new_version:
logger.error('A new engine name or new version, or both must be supplied.')
return 1
if not engine_path:
engine_path = manifest.get_registered(engine_name=engine_name)

@ -607,75 +607,90 @@ def get_registered(engine_name: str = None,
engine_path = pathlib.Path(engine).resolve()
engine_json = engine_path / 'engine.json'
with engine_json.open('r') as f:
try:
engine_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warning(f'{engine_json} failed to load: {str(e)}')
else:
this_engines_name = engine_json_data['engine_name']
if this_engines_name == engine_name:
return engine_path
if not pathlib.Path(engine_json).is_file():
logger.warning(f'{engine_json} does not exist')
else:
with engine_json.open('r') as f:
try:
engine_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warning(f'{engine_json} failed to load: {str(e)}')
else:
this_engines_name = engine_json_data['engine_name']
if this_engines_name == engine_name:
return engine_path
elif isinstance(project_name, str):
projects = get_all_projects()
for project_path in projects:
project_path = pathlib.Path(project_path).resolve()
project_json = project_path / 'project.json'
with project_json.open('r') as f:
try:
project_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warning(f'{project_json} failed to load: {str(e)}')
else:
this_projects_name = project_json_data['project_name']
if this_projects_name == project_name:
return project_path
if not pathlib.Path(project_json).is_file():
logger.warning(f'{project_json} does not exist')
else:
with project_json.open('r') as f:
try:
project_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warning(f'{project_json} failed to load: {str(e)}')
else:
this_projects_name = project_json_data['project_name']
if this_projects_name == project_name:
return project_path
elif isinstance(gem_name, str):
gems = get_all_gems(project_path)
for gem_path in gems:
gem_path = pathlib.Path(gem_path).resolve()
gem_json = gem_path / 'gem.json'
with gem_json.open('r') as f:
try:
gem_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warning(f'{gem_json} failed to load: {str(e)}')
else:
this_gems_name = gem_json_data['gem_name']
if this_gems_name == gem_name:
return gem_path
if not pathlib.Path(gem_json).is_file():
logger.warning(f'{gem_json} does not exist')
else:
with gem_json.open('r') as f:
try:
gem_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warning(f'{gem_json} failed to load: {str(e)}')
else:
this_gems_name = gem_json_data['gem_name']
if this_gems_name == gem_name:
return gem_path
elif isinstance(template_name, str):
templates = get_all_templates(project_path)
for template_path in templates:
template_path = pathlib.Path(template_path).resolve()
template_json = template_path / 'template.json'
with template_json.open('r') as f:
try:
template_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warning(f'{template_path} failed to load: {str(e)}')
else:
this_templates_name = template_json_data['template_name']
if this_templates_name == template_name:
return template_path
if not pathlib.Path(template_json).is_file():
logger.warning(f'{template_json} does not exist')
else:
with template_json.open('r') as f:
try:
template_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warning(f'{template_path} failed to load: {str(e)}')
else:
this_templates_name = template_json_data['template_name']
if this_templates_name == template_name:
return template_path
elif isinstance(restricted_name, str):
restricted = get_all_restricted(project_path)
for restricted_path in restricted:
restricted_path = pathlib.Path(restricted_path).resolve()
restricted_json = restricted_path / 'restricted.json'
with restricted_json.open('r') as f:
try:
restricted_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warning(f'{restricted_json} failed to load: {str(e)}')
else:
this_restricted_name = restricted_json_data['restricted_name']
if this_restricted_name == restricted_name:
return restricted_path
if not pathlib.Path(restricted_json).is_file():
logger.warning(f'{restricted_json} does not exist')
else:
with restricted_json.open('r') as f:
try:
restricted_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warning(f'{restricted_json} failed to load: {str(e)}')
else:
this_restricted_name = restricted_json_data['restricted_name']
if this_restricted_name == restricted_name:
return restricted_path
elif isinstance(default_folder, str):
if default_folder == 'engines':

Loading…
Cancel
Save