|
@@ -7,17 +7,18 @@
|
|
|
*/
|
|
|
|
|
|
#include <AzToolsFramework/API/PythonLoader.h>
|
|
|
-#include <AzToolsFramework_Traits_Platform.h>
|
|
|
#include <AzCore/Component/ComponentApplicationBus.h>
|
|
|
#include <AzCore/IO/FileIO.h>
|
|
|
#include <AzCore/IO/GenericStreams.h>
|
|
|
#include <AzCore/IO/Path/Path.h>
|
|
|
+#include <AzCore/IO/SystemFile.h>
|
|
|
#include <AzCore/Math/Sha1.h>
|
|
|
#include <AzCore/std/containers/vector.h>
|
|
|
#include <AzCore/std/string/string.h>
|
|
|
#include <AzCore/std/string/string_view.h>
|
|
|
#include <AzCore/std/string/conversions.h>
|
|
|
#include <AzCore/std/string/tokenize.h>
|
|
|
+#include <AzCore/Serialization/Json/JsonUtils.h>
|
|
|
#include <AzCore/Settings/ConfigParser.h>
|
|
|
#include <AzCore/Utils/Utils.h>
|
|
|
#include <AzFramework/IO/LocalFileIO.h>
|
|
@@ -26,82 +27,56 @@ namespace AzToolsFramework::EmbeddedPython
|
|
|
{
|
|
|
PythonLoader::PythonLoader()
|
|
|
{
|
|
|
- #if AZ_TRAIT_PYTHON_LOADER_ENABLE_EXPLICIT_LOADING
|
|
|
- // PYTHON_SHARED_LIBRARY_PATH must be defined in the build scripts and referencing the path to the python shared library
|
|
|
- #if !defined(PYTHON_SHARED_LIBRARY_PATH)
|
|
|
- #error "PYTHON_SHARED_LIBRARY_PATH is not defined"
|
|
|
- #endif
|
|
|
-
|
|
|
- // Construct the path to the shared python library within the venv folder
|
|
|
- AZ::IO::FixedMaxPath engineRoot = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath());
|
|
|
- AZ::IO::FixedMaxPath thirdPartyRoot = PythonLoader::GetDefault3rdPartyPath(false);
|
|
|
- AZ::IO::FixedMaxPath pythonVenvPath = PythonLoader::GetPythonVenvPath(thirdPartyRoot, engineRoot);
|
|
|
-
|
|
|
- AZ::IO::PathView libPythonName = AZ::IO::PathView(PYTHON_SHARED_LIBRARY_PATH).Filename();
|
|
|
- AZ::IO::FixedMaxPath pythonVenvLibPath = pythonVenvPath / "lib" / libPythonName;
|
|
|
-
|
|
|
- m_embeddedLibPythonModuleHandle = AZ::DynamicModuleHandle::Create(pythonVenvLibPath.StringAsPosix().c_str(), false);
|
|
|
- bool loadResult = m_embeddedLibPythonModuleHandle->Load(false, true);
|
|
|
- AZ_Error("PythonLoader", loadResult, "Failed to load %s.\n", libPythonName.StringAsPosix().c_str());
|
|
|
- #endif // AZ_TRAIT_PYTHON_LOADER_ENABLE_EXPLICIT_LOADING
|
|
|
- }
|
|
|
-
|
|
|
- PythonLoader::~PythonLoader()
|
|
|
- {
|
|
|
- #if AZ_TRAIT_PYTHON_LOADER_ENABLE_EXPLICIT_LOADING
|
|
|
- AZ_Assert(m_embeddedLibPythonModuleHandle, "DynamicModuleHandle for python was not created");
|
|
|
- m_embeddedLibPythonModuleHandle->Unload();
|
|
|
- #endif // AZ_TRAIT_PYTHON_LOADER_ENABLE_EXPLICIT_LOADING
|
|
|
- }
|
|
|
+ #if defined(IMPLICIT_LOAD_PYTHON_SHARED_LIBRARY)
|
|
|
|
|
|
- AZ::IO::FixedMaxPath PythonLoader::GetDefault3rdPartyPath(bool createOnDemand)
|
|
|
- {
|
|
|
- AZ::IO::FixedMaxPath thirdPartyEnvPathPath;
|
|
|
-
|
|
|
- // The highest priority for the 3rd party path is the environment variable 'LY_3RDPARTY_PATH'
|
|
|
- static constexpr const char* env3rdPartyKey = "LY_3RDPARTY_PATH";
|
|
|
- char env3rdPartyPath[AZ::IO::MaxPathLength] = { '\0' };
|
|
|
- auto envOutcome = AZ::Utils::GetEnv(AZStd::span(env3rdPartyPath), env3rdPartyKey);
|
|
|
- if (envOutcome && (strlen(env3rdPartyPath) > 0))
|
|
|
- {
|
|
|
- // If so, then use the path that is set as the third party path
|
|
|
- thirdPartyEnvPathPath = AZ::IO::FixedMaxPath(env3rdPartyPath).LexicallyNormal();
|
|
|
- }
|
|
|
- // The next priority is to read the 3rd party directory from the manifest file
|
|
|
- else if (auto manifest3rdPartyResult = AZ::Utils::Get3rdPartyDirectory(); manifest3rdPartyResult.IsSuccess())
|
|
|
+ // Determine if this is an sdk-engine build. For SDK engines, we want to prevent implicit python module loading.
|
|
|
+ [[maybe_unused]] bool isSdkEngine{ false };
|
|
|
+ auto engineSettingsPath = AZ::IO::FixedMaxPath{ AZ::Utils::GetEnginePath() } / "engine.json";
|
|
|
+ if (AZ::IO::SystemFile::Exists(engineSettingsPath.c_str()))
|
|
|
{
|
|
|
- thirdPartyEnvPathPath = manifest3rdPartyResult.GetValue();
|
|
|
+ auto loadOutcome = AZ::JsonSerializationUtils::ReadJsonFile(engineSettingsPath.c_str());
|
|
|
+ if (loadOutcome.IsSuccess())
|
|
|
+ {
|
|
|
+ auto& doc = loadOutcome.GetValue();
|
|
|
+ rapidjson::Value::MemberIterator sdkEngineFieldIter = doc.FindMember("sdk_engine");
|
|
|
+ if (sdkEngineFieldIter != doc.MemberEnd())
|
|
|
+ {
|
|
|
+ isSdkEngine = sdkEngineFieldIter->value.GetBool();
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- // Fallback to the default 3rd Party path based on the location of the manifest folder
|
|
|
- else
|
|
|
+ if (!isSdkEngine)
|
|
|
{
|
|
|
- auto manifestPath = AZ::Utils::GetO3deManifestDirectory();
|
|
|
- thirdPartyEnvPathPath = AZ::IO::FixedMaxPath(manifestPath) / "3rdParty";
|
|
|
+ m_embeddedLibPythonModuleHandle = AZ::DynamicModuleHandle::Create(IMPLICIT_LOAD_PYTHON_SHARED_LIBRARY, false);
|
|
|
+ bool loadResult = m_embeddedLibPythonModuleHandle->Load(false, true);
|
|
|
+ AZ_Error("PythonLoader", loadResult, "Failed to load " IMPLICIT_LOAD_PYTHON_SHARED_LIBRARY "\n");
|
|
|
}
|
|
|
+ #endif // IMPLICIT_LOAD_PYTHON_SHARED_LIBRARY
|
|
|
+ }
|
|
|
|
|
|
- if ((!AZ::IO::SystemFile::IsDirectory(thirdPartyEnvPathPath.c_str())) && createOnDemand)
|
|
|
+ PythonLoader::~PythonLoader()
|
|
|
+ {
|
|
|
+ #if defined(IMPLICIT_LOAD_PYTHON_SHARED_LIBRARY)
|
|
|
+ if (m_embeddedLibPythonModuleHandle)
|
|
|
{
|
|
|
- auto createPathResult = AZ::IO::SystemFile::CreateDir(thirdPartyEnvPathPath.c_str());
|
|
|
- AZ_Assert(createPathResult, "Unable to create missing 3rd Party Folder '%s'", thirdPartyEnvPathPath.c_str())
|
|
|
+ m_embeddedLibPythonModuleHandle->Unload();
|
|
|
}
|
|
|
- return thirdPartyEnvPathPath;
|
|
|
+ #endif // IMPLICIT_LOAD_PYTHON_SHARED_LIBRARY
|
|
|
}
|
|
|
|
|
|
- AZ::IO::FixedMaxPath PythonLoader::GetPythonHomePath(AZ::IO::PathView engineRoot)
|
|
|
+ AZ::IO::FixedMaxPath PythonLoader::GetPythonHomePath(AZ::IO::PathView engineRoot, const char* overridePythonBaseVenvPath /*= nullptr*/)
|
|
|
{
|
|
|
- AZ::IO::FixedMaxPath thirdPartyFolder = GetDefault3rdPartyPath(true);
|
|
|
-
|
|
|
// The python HOME path relative to the executable depends on the host platform the package is created for
|
|
|
#if AZ_TRAIT_PYTHON_LOADER_PYTHON_HOME_BIN_SUBPATH
|
|
|
- AZ::IO::FixedMaxPath pythonHomePath = PythonLoader::GetPythonExecutablePath(thirdPartyFolder, engineRoot).ParentPath();
|
|
|
+ AZ::IO::FixedMaxPath pythonHomePath = PythonLoader::GetPythonExecutablePath(engineRoot, overridePythonBaseVenvPath).ParentPath();
|
|
|
#else
|
|
|
- AZ::IO::FixedMaxPath pythonHomePath = PythonLoader::GetPythonExecutablePath(thirdPartyFolder, engineRoot);
|
|
|
+ AZ::IO::FixedMaxPath pythonHomePath = PythonLoader::GetPythonExecutablePath(engineRoot, overridePythonBaseVenvPath);
|
|
|
#endif // AZ_TRAIT_PYTHON_LOADER_PYTHON_HOME_BIN_SUBPATH
|
|
|
|
|
|
return pythonHomePath;
|
|
|
}
|
|
|
|
|
|
- AZ::IO::FixedMaxPath PythonLoader::GetPythonVenvPath(AZ::IO::PathView thirdPartyRoot, AZ::IO::PathView engineRoot)
|
|
|
+ AZ::IO::FixedMaxPath PythonLoader::GetPythonVenvPath(AZ::IO::PathView engineRoot, const char* overridePythonBaseVenvPath /*= nullptr*/)
|
|
|
{
|
|
|
// Perform the same hash calculation as cmake/CalculateEnginePathId.cmake
|
|
|
/////
|
|
@@ -118,16 +93,18 @@ namespace AzToolsFramework::EmbeddedPython
|
|
|
hasher.GetDigest(digest);
|
|
|
|
|
|
// Construct the path to where the python venv based on the engine path should be located
|
|
|
- AZ::IO::FixedMaxPath libPath = thirdPartyRoot;
|
|
|
+ AZ::IO::FixedMaxPath libPath = (overridePythonBaseVenvPath != nullptr) ? AZ::IO::FixedMaxPath(overridePythonBaseVenvPath) :
|
|
|
+ AZ::Utils::GetO3dePythonVenvRoot();
|
|
|
+
|
|
|
// The ID is based on the first 32 bits of the digest, and formatted to at least 8-character wide hexadecimal representation
|
|
|
- libPath /= AZ::IO::FixedMaxPathString::format("venv/%08x", digest[0]);
|
|
|
+ libPath /= AZ::IO::FixedMaxPathString::format("%08x", digest[0]);
|
|
|
libPath = libPath.LexicallyNormal();
|
|
|
return libPath;
|
|
|
}
|
|
|
|
|
|
- AZ::IO::FixedMaxPath PythonLoader::GetPythonExecutablePath(AZ::IO::PathView thirdPartyRoot, AZ::IO::PathView engineRoot)
|
|
|
+ AZ::IO::FixedMaxPath PythonLoader::GetPythonExecutablePath(AZ::IO::PathView engineRoot, const char* overridePythonBaseVenvPath /*= nullptr*/)
|
|
|
{
|
|
|
- AZ::IO::FixedMaxPath pythonVenvConfig = PythonLoader::GetPythonVenvPath(thirdPartyRoot, engineRoot) / "pyvenv.cfg";
|
|
|
+ AZ::IO::FixedMaxPath pythonVenvConfig = PythonLoader::GetPythonVenvPath(engineRoot, overridePythonBaseVenvPath) / "pyvenv.cfg";
|
|
|
AZ::IO::SystemFileStream systemFileStream;
|
|
|
if (!systemFileStream.Open(pythonVenvConfig.c_str(), AZ::IO::OpenMode::ModeRead))
|
|
|
{
|
|
@@ -151,11 +128,11 @@ namespace AzToolsFramework::EmbeddedPython
|
|
|
return AZ::IO::FixedMaxPath(pythonHome);
|
|
|
}
|
|
|
|
|
|
- void PythonLoader::ReadPythonEggLinkPaths(AZ::IO::PathView thirdPartyRoot, AZ::IO::PathView engineRoot, EggLinkPathVisitor resultPathCallback)
|
|
|
+ void PythonLoader::ReadPythonEggLinkPaths(AZ::IO::PathView engineRoot, EggLinkPathVisitor resultPathCallback, const char* overridePythonBaseVenvPath /*= nullptr*/)
|
|
|
{
|
|
|
// Get the python venv path
|
|
|
AZ::IO::FixedMaxPath pythonVenvSitePackages =
|
|
|
- AZ::IO::FixedMaxPath(PythonLoader::GetPythonVenvPath(thirdPartyRoot, engineRoot)) / O3DE_PYTHON_SITE_PACKAGE_SUBPATH;
|
|
|
+ AZ::IO::FixedMaxPath(PythonLoader::GetPythonVenvPath(engineRoot, overridePythonBaseVenvPath)) / O3DE_PYTHON_SITE_PACKAGE_SUBPATH;
|
|
|
|
|
|
// Always add the site-packages folder from the virtual environment into the path list
|
|
|
resultPathCallback(pythonVenvSitePackages.LexicallyNormal());
|