Merge remote-tracking branch 'upstream/stabilization/2106' into cgalvan/gitflow_210811
Signed-off-by: Chris Galvan <chgalvan@amazon.com>
This commit is contained in:
-55
@@ -1,55 +0,0 @@
|
|||||||
"""
|
|
||||||
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
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import azlmbr.legacy.general as general
|
|
||||||
import azlmbr.paths
|
|
||||||
|
|
||||||
sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
|
|
||||||
import editor_python_test_tools.hydra_editor_utils as hydra
|
|
||||||
from editor_python_test_tools.editor_test_helper import EditorTestHelper
|
|
||||||
|
|
||||||
|
|
||||||
class TestDebuggerDebugCVarsWorks(EditorTestHelper):
|
|
||||||
def __init__(self):
|
|
||||||
EditorTestHelper.__init__(self, log_prefix="Debugger_DebugCVarsWorks", args=["level"])
|
|
||||||
|
|
||||||
def run_test(self):
|
|
||||||
"""
|
|
||||||
Summary:
|
|
||||||
C2789148 Vegetation Debug CVars are enabled when the Debugger component is present
|
|
||||||
|
|
||||||
Expected Result:
|
|
||||||
The following commands are available in the Editor only when the Vegetation Debugger Level component is present:
|
|
||||||
veg_debugDumpReport (Command)
|
|
||||||
veg_debugRefreshAllAreas (Command)
|
|
||||||
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Create empty level
|
|
||||||
self.test_success = self.create_level(
|
|
||||||
self.args["level"],
|
|
||||||
heightmap_resolution=1024,
|
|
||||||
heightmap_meters_per_pixel=1,
|
|
||||||
terrain_texture_resolution=4096,
|
|
||||||
use_terrain=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Initially run the command in console without Debugger component
|
|
||||||
general.run_console("veg_debugDumpReport")
|
|
||||||
|
|
||||||
# Add the Vegetation Debugger component to the Level Inspector
|
|
||||||
hydra.add_level_component("Vegetation Debugger")
|
|
||||||
|
|
||||||
# Run a command again after adding the Vegetation debugger
|
|
||||||
general.run_console("veg_debugRefreshAllAreas")
|
|
||||||
|
|
||||||
|
|
||||||
test = TestDebuggerDebugCVarsWorks()
|
|
||||||
test.run()
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
"""
|
|
||||||
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
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import pytest
|
|
||||||
import logging
|
|
||||||
|
|
||||||
# Bail on the test if ly_test_tools doesn't exist.
|
|
||||||
pytest.importorskip("ly_test_tools")
|
|
||||||
import ly_test_tools.environment.file_system as file_system
|
|
||||||
import editor_python_test_tools.hydra_test_utils as hydra
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
|
||||||
@pytest.mark.parametrize("level", ["tmp_level"])
|
|
||||||
@pytest.mark.usefixtures("automatic_process_killer")
|
|
||||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
|
||||||
class TestDebugger(object):
|
|
||||||
@pytest.fixture(autouse=True)
|
|
||||||
def setup_teardown(self, request, workspace, project, level):
|
|
||||||
# Cleanup our temp level
|
|
||||||
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
|
|
||||||
|
|
||||||
def teardown():
|
|
||||||
# Cleanup our temp level
|
|
||||||
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
|
|
||||||
|
|
||||||
request.addfinalizer(teardown)
|
|
||||||
|
|
||||||
@pytest.mark.test_case_id("C2789148")
|
|
||||||
@pytest.mark.SUITE_periodic
|
|
||||||
@pytest.mark.dynveg_misc
|
|
||||||
def test_Debugger_DebugCVarsWork(self, request, editor, level, workspace, launcher_platform):
|
|
||||||
cfg_args = [level]
|
|
||||||
|
|
||||||
expected_lines = [
|
|
||||||
"Debugger_DebugCVarsWorks: test started",
|
|
||||||
"[Warning] Unknown command: veg_debugDumpReport",
|
|
||||||
"[CONSOLE] Executing console command 'veg_debugRefreshAllAreas'",
|
|
||||||
"Debugger_DebugCVarsWorks: result=SUCCESS",
|
|
||||||
]
|
|
||||||
|
|
||||||
hydra.launch_and_validate_results(
|
|
||||||
request,
|
|
||||||
test_directory,
|
|
||||||
editor,
|
|
||||||
"Debugger_DebugCVarsWorks.py",
|
|
||||||
expected_lines=expected_lines,
|
|
||||||
cfg_args=cfg_args
|
|
||||||
)
|
|
||||||
+1
@@ -104,6 +104,7 @@ class TestGeneralGraphFunctionality(object):
|
|||||||
|
|
||||||
@pytest.mark.test_case_id("C17488412")
|
@pytest.mark.test_case_id("C17488412")
|
||||||
@pytest.mark.SUITE_periodic
|
@pytest.mark.SUITE_periodic
|
||||||
|
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2201")
|
||||||
def test_LandscapeCanvas_GraphClosed_OnEntityDelete(self, request, editor, level, launcher_platform):
|
def test_LandscapeCanvas_GraphClosed_OnEntityDelete(self, request, editor, level, launcher_platform):
|
||||||
cfg_args = [level]
|
cfg_args = [level]
|
||||||
|
|
||||||
|
|||||||
@@ -163,6 +163,8 @@ ly_add_target(
|
|||||||
editor_files.cmake
|
editor_files.cmake
|
||||||
PLATFORM_INCLUDE_FILES
|
PLATFORM_INCLUDE_FILES
|
||||||
Platform/${PAL_PLATFORM_NAME}/editor_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
|
Platform/${PAL_PLATFORM_NAME}/editor_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
|
||||||
|
TARGET_PROPERTIES
|
||||||
|
LY_INSTALL_GENERATE_RUN_TARGET TRUE
|
||||||
BUILD_DEPENDENCIES
|
BUILD_DEPENDENCIES
|
||||||
PRIVATE
|
PRIVATE
|
||||||
3rdParty::Qt::Core
|
3rdParty::Qt::Core
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ namespace AZ::IO
|
|||||||
//! then their hash values are also equal
|
//! then their hash values are also equal
|
||||||
//! For example : path "a//b" equals "a/b", the
|
//! For example : path "a//b" equals "a/b", the
|
||||||
//! hash value of "a//b" would also equal the hash value of "a/b"
|
//! hash value of "a//b" would also equal the hash value of "a/b"
|
||||||
constexpr size_t hash_value(const PathView& pathToHash) noexcept;
|
size_t hash_value(const PathView& pathToHash) noexcept;
|
||||||
|
|
||||||
// path.comparison
|
// path.comparison
|
||||||
constexpr bool operator==(const PathView& lhs, const PathView& rhs) noexcept;
|
constexpr bool operator==(const PathView& lhs, const PathView& rhs) noexcept;
|
||||||
@@ -622,7 +622,7 @@ namespace AZ::IO
|
|||||||
//! For example : path "a//b" equals "a/b", the
|
//! For example : path "a//b" equals "a/b", the
|
||||||
//! hash value of "a//b" would also equal the hash value of "a/b"
|
//! hash value of "a//b" would also equal the hash value of "a/b"
|
||||||
template <typename StringType>
|
template <typename StringType>
|
||||||
constexpr size_t hash_value(const BasicPath<StringType>& pathToHash);
|
size_t hash_value(const BasicPath<StringType>& pathToHash);
|
||||||
|
|
||||||
// path.append
|
// path.append
|
||||||
template <typename StringType>
|
template <typename StringType>
|
||||||
|
|||||||
@@ -1951,7 +1951,7 @@ namespace AZ::IO
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename StringType>
|
template <typename StringType>
|
||||||
constexpr size_t hash_value(const BasicPath<StringType>& pathToHash)
|
inline size_t hash_value(const BasicPath<StringType>& pathToHash)
|
||||||
{
|
{
|
||||||
return AZStd::hash<BasicPath<StringType>>{}(pathToHash);
|
return AZStd::hash<BasicPath<StringType>>{}(pathToHash);
|
||||||
}
|
}
|
||||||
@@ -2082,13 +2082,28 @@ namespace AZStd
|
|||||||
template <>
|
template <>
|
||||||
struct hash<AZ::IO::PathView>
|
struct hash<AZ::IO::PathView>
|
||||||
{
|
{
|
||||||
constexpr size_t operator()(const AZ::IO::PathView& pathToHash) noexcept
|
/// Path is using FNV-1a algorithm 64 bit version.
|
||||||
|
static size_t hash_path(AZStd::string_view pathSegment, const char pathSeparator)
|
||||||
|
{
|
||||||
|
size_t hash = 14695981039346656037ULL;
|
||||||
|
constexpr size_t fnvPrime = 1099511628211ULL;
|
||||||
|
|
||||||
|
for (const char first : pathSegment)
|
||||||
|
{
|
||||||
|
hash ^= static_cast<size_t>((pathSeparator == AZ::IO::PosixPathSeparator)
|
||||||
|
? first : tolower(first));
|
||||||
|
hash *= fnvPrime;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t operator()(const AZ::IO::PathView& pathToHash) noexcept
|
||||||
{
|
{
|
||||||
auto pathParser = AZ::IO::parser::PathParser::CreateBegin(pathToHash.Native(), pathToHash.m_preferred_separator);
|
auto pathParser = AZ::IO::parser::PathParser::CreateBegin(pathToHash.Native(), pathToHash.m_preferred_separator);
|
||||||
size_t hash_value = 0;
|
size_t hash_value = 0;
|
||||||
while (pathParser)
|
while (pathParser)
|
||||||
{
|
{
|
||||||
AZStd::hash_combine(hash_value, AZStd::hash<AZStd::string_view>{}(*pathParser));
|
AZStd::hash_combine(hash_value, hash_path(*pathParser, pathToHash.m_preferred_separator));
|
||||||
++pathParser;
|
++pathParser;
|
||||||
}
|
}
|
||||||
return hash_value;
|
return hash_value;
|
||||||
@@ -2097,7 +2112,7 @@ namespace AZStd
|
|||||||
template <typename StringType>
|
template <typename StringType>
|
||||||
struct hash<AZ::IO::BasicPath<StringType>>
|
struct hash<AZ::IO::BasicPath<StringType>>
|
||||||
{
|
{
|
||||||
constexpr size_t operator()(const AZ::IO::BasicPath<StringType>& pathToHash) noexcept
|
const size_t operator()(const AZ::IO::BasicPath<StringType>& pathToHash) noexcept
|
||||||
{
|
{
|
||||||
return AZStd::hash<AZ::IO::PathView>{}(pathToHash);
|
return AZStd::hash<AZ::IO::PathView>{}(pathToHash);
|
||||||
}
|
}
|
||||||
@@ -2108,11 +2123,11 @@ namespace AZStd
|
|||||||
template struct hash<AZ::IO::FixedMaxPath>;
|
template struct hash<AZ::IO::FixedMaxPath>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicit instantations of our support Path classes
|
// Explicit instantiations of our support Path classes
|
||||||
namespace AZ::IO
|
namespace AZ::IO
|
||||||
{
|
{
|
||||||
// PathView hash
|
// PathView hash
|
||||||
constexpr size_t hash_value(const PathView& pathToHash) noexcept
|
inline size_t hash_value(const PathView& pathToHash) noexcept
|
||||||
{
|
{
|
||||||
return AZStd::hash<PathView>{}(pathToHash);
|
return AZStd::hash<PathView>{}(pathToHash);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,6 +182,36 @@ namespace UnitTest
|
|||||||
AZStd::tuple<AZStd::string_view, AZStd::string_view>(R"(foO/Bar)", "foo/bar")
|
AZStd::tuple<AZStd::string_view, AZStd::string_view>(R"(foO/Bar)", "foo/bar")
|
||||||
));
|
));
|
||||||
|
|
||||||
|
using PathHashParamFixture = PathParamFixture;
|
||||||
|
TEST_P(PathHashParamFixture, HashOperator_HashesCaseInsensitiveForWindowsPaths)
|
||||||
|
{
|
||||||
|
AZ::IO::Path path1{ AZStd::get<0>(GetParam()), AZ::IO::WindowsPathSeparator };
|
||||||
|
AZ::IO::Path path2{ AZStd::get<1>(GetParam()), AZ::IO::WindowsPathSeparator };
|
||||||
|
size_t path1Hash = AZStd::hash<AZ::IO::PathView>{}(path1);
|
||||||
|
size_t path2Hash = AZStd::hash<AZ::IO::PathView>{}(path2);
|
||||||
|
EXPECT_EQ(path1Hash, path2Hash) << AZStd::string::format(R"(path1 "%s" should hash to path2 "%s"\n)",
|
||||||
|
path1.c_str(), path2.c_str()).c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(PathHashParamFixture, HashOperator_HashesCaseSensitiveForPosixPaths)
|
||||||
|
{
|
||||||
|
AZ::IO::Path path1{ AZStd::get<0>(GetParam()), AZ::IO::PosixPathSeparator };
|
||||||
|
AZ::IO::Path path2{ AZStd::get<1>(GetParam()), AZ::IO::PosixPathSeparator };
|
||||||
|
size_t path1Hash = AZStd::hash<AZ::IO::PathView>{}(path1);
|
||||||
|
size_t path2Hash = AZStd::hash<AZ::IO::PathView>{}(path2);
|
||||||
|
EXPECT_NE(path1Hash, path2Hash) << AZStd::string::format(R"(path1 "%s" should NOT hash to path2 "%s"\n)",
|
||||||
|
path1.c_str(), path2.c_str()).c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(
|
||||||
|
HashPaths,
|
||||||
|
PathHashParamFixture,
|
||||||
|
::testing::Values(
|
||||||
|
AZStd::tuple<AZStd::string_view, AZStd::string_view>("C:/test/foo", R"(c:\test/foo)"),
|
||||||
|
AZStd::tuple<AZStd::string_view, AZStd::string_view>(R"(D:\test/bar/baz//foo)", "d:/test/bar/baz\\\\\\foo"),
|
||||||
|
AZStd::tuple<AZStd::string_view, AZStd::string_view>(R"(foO/Bar)", "foo/bar")
|
||||||
|
));
|
||||||
|
|
||||||
class PathSingleParamFixture
|
class PathSingleParamFixture
|
||||||
: public ScopedAllocatorSetupFixture
|
: public ScopedAllocatorSetupFixture
|
||||||
, public ::testing::WithParamInterface<AZStd::tuple<AZStd::string_view>>
|
, public ::testing::WithParamInterface<AZStd::tuple<AZStd::string_view>>
|
||||||
|
|||||||
+2
-2
@@ -206,12 +206,12 @@ namespace AzToolsFramework
|
|||||||
|
|
||||||
const AZStd::string& AssetBrowserEntry::GetRelativePath() const
|
const AZStd::string& AssetBrowserEntry::GetRelativePath() const
|
||||||
{
|
{
|
||||||
return m_relativePath;
|
return m_relativePath.Native();
|
||||||
}
|
}
|
||||||
|
|
||||||
const AZStd::string& AssetBrowserEntry::GetFullPath() const
|
const AZStd::string& AssetBrowserEntry::GetFullPath() const
|
||||||
{
|
{
|
||||||
return m_fullPath;
|
return m_fullPath.Native();
|
||||||
}
|
}
|
||||||
|
|
||||||
const AssetBrowserEntry* AssetBrowserEntry::GetChild(int index) const
|
const AssetBrowserEntry* AssetBrowserEntry::GetChild(int index) const
|
||||||
|
|||||||
+3
-2
@@ -9,6 +9,7 @@
|
|||||||
#if !defined(Q_MOC_RUN)
|
#if !defined(Q_MOC_RUN)
|
||||||
#include <AzCore/std/string/string.h>
|
#include <AzCore/std/string/string.h>
|
||||||
#include <AzCore/Asset/AssetCommon.h>
|
#include <AzCore/Asset/AssetCommon.h>
|
||||||
|
#include <AzCore/IO/Path/Path.h>
|
||||||
#include <AzCore/Math/Uuid.h>
|
#include <AzCore/Math/Uuid.h>
|
||||||
|
|
||||||
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
|
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
|
||||||
@@ -130,8 +131,8 @@ namespace AzToolsFramework
|
|||||||
protected:
|
protected:
|
||||||
AZStd::string m_name;
|
AZStd::string m_name;
|
||||||
QString m_displayName;
|
QString m_displayName;
|
||||||
AZStd::string m_relativePath;
|
AZ::IO::Path m_relativePath;
|
||||||
AZStd::string m_fullPath;
|
AZ::IO::Path m_fullPath;
|
||||||
AZStd::vector<AssetBrowserEntry*> m_children;
|
AZStd::vector<AssetBrowserEntry*> m_children;
|
||||||
AssetBrowserEntry* m_parentAssetEntry = nullptr;
|
AssetBrowserEntry* m_parentAssetEntry = nullptr;
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -37,8 +37,8 @@ namespace AzToolsFramework
|
|||||||
|
|
||||||
void FolderAssetBrowserEntry::UpdateChildPaths(AssetBrowserEntry* child) const
|
void FolderAssetBrowserEntry::UpdateChildPaths(AssetBrowserEntry* child) const
|
||||||
{
|
{
|
||||||
child->m_relativePath = m_relativePath + AZ_CORRECT_DATABASE_SEPARATOR + child->m_name;
|
child->m_relativePath = m_relativePath / child->m_name;
|
||||||
child->m_fullPath = m_fullPath + AZ_CORRECT_DATABASE_SEPARATOR + child->m_name;
|
child->m_fullPath = m_fullPath / child->m_name;
|
||||||
AssetBrowserEntry::UpdateChildPaths(child);
|
AssetBrowserEntry::UpdateChildPaths(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+90
-113
@@ -10,6 +10,7 @@
|
|||||||
#include <AzFramework/StringFunc/StringFunc.h>
|
#include <AzFramework/StringFunc/StringFunc.h>
|
||||||
|
|
||||||
#include <AzCore/IO/FileIO.h>
|
#include <AzCore/IO/FileIO.h>
|
||||||
|
#include <AzCore/IO/Path/Path.h>
|
||||||
|
|
||||||
#include <AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.h>
|
#include <AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.h>
|
||||||
#include <AzToolsFramework/AssetBrowser/Entries/FolderAssetBrowserEntry.h>
|
#include <AzToolsFramework/AssetBrowser/Entries/FolderAssetBrowserEntry.h>
|
||||||
@@ -24,8 +25,6 @@ namespace AzToolsFramework
|
|||||||
{
|
{
|
||||||
namespace AssetBrowser
|
namespace AssetBrowser
|
||||||
{
|
{
|
||||||
const char* GEMS_FOLDER_NAME = "Gems";
|
|
||||||
|
|
||||||
RootAssetBrowserEntry::RootAssetBrowserEntry()
|
RootAssetBrowserEntry::RootAssetBrowserEntry()
|
||||||
: AssetBrowserEntry()
|
: AssetBrowserEntry()
|
||||||
{
|
{
|
||||||
@@ -53,13 +52,7 @@ namespace AzToolsFramework
|
|||||||
EntryCache::GetInstance()->Clear();
|
EntryCache::GetInstance()->Clear();
|
||||||
|
|
||||||
m_enginePath = enginePath;
|
m_enginePath = enginePath;
|
||||||
|
m_fullPath = enginePath;
|
||||||
// there is no "Gems" scan folder registered in db, create one manually
|
|
||||||
auto gemFolder = aznew FolderAssetBrowserEntry();
|
|
||||||
gemFolder->m_name = m_enginePath + AZ_CORRECT_DATABASE_SEPARATOR + GEMS_FOLDER_NAME;
|
|
||||||
gemFolder->m_displayName = GEMS_FOLDER_NAME;
|
|
||||||
gemFolder->m_isGemsFolder = true;
|
|
||||||
AddChild(gemFolder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RootAssetBrowserEntry::IsInitialUpdate() const
|
bool RootAssetBrowserEntry::IsInitialUpdate() const
|
||||||
@@ -80,8 +73,17 @@ namespace AzToolsFramework
|
|||||||
|
|
||||||
if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(scanFolderDatabaseEntry.m_scanFolder.c_str()))
|
if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(scanFolderDatabaseEntry.m_scanFolder.c_str()))
|
||||||
{
|
{
|
||||||
const auto scanFolder = CreateFolders(scanFolderDatabaseEntry.m_scanFolder.c_str(), this);
|
const auto scanFolder = CreateFolders(scanFolderDatabaseEntry.m_scanFolder, this);
|
||||||
scanFolder->m_displayName = QString::fromUtf8(scanFolderDatabaseEntry.m_displayName.c_str());
|
// Append an "[External]" to the display if the Scan Folder is NOT relative to the Engine Root path
|
||||||
|
if (!AZ::IO::PathView(scanFolderDatabaseEntry.m_scanFolder).IsRelativeTo(m_enginePath))
|
||||||
|
{
|
||||||
|
scanFolder->m_displayName += " [External]";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scanFolder->m_displayName = QString::fromUtf8(scanFolderDatabaseEntry.m_displayName.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
EntryCache::GetInstance()->m_scanFolderIdMap[scanFolderDatabaseEntry.m_scanFolderID] = scanFolder;
|
EntryCache::GetInstance()->m_scanFolderIdMap[scanFolderDatabaseEntry.m_scanFolderID] = scanFolder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,38 +123,34 @@ namespace AzToolsFramework
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* filePath = fileDatabaseEntry.m_fileName.c_str();
|
AZ::IO::FixedMaxPath absoluteFilePath = AZ::IO::FixedMaxPath(AZStd::string_view{ scanFolder->GetFullPath() })
|
||||||
|
/ fileDatabaseEntry.m_fileName.c_str();
|
||||||
|
|
||||||
AssetBrowserEntry* file;
|
AssetBrowserEntry* file;
|
||||||
// file can be either folder or actual file
|
// file can be either folder or actual file
|
||||||
if (fileDatabaseEntry.m_isFolder)
|
if (fileDatabaseEntry.m_isFolder)
|
||||||
{
|
{
|
||||||
file = CreateFolders(filePath, scanFolder);
|
file = CreateFolders(absoluteFilePath.Native(), scanFolder);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AZStd::string sourcePath;
|
|
||||||
AZStd::string sourceName;
|
|
||||||
AZStd::string sourceExtension;
|
|
||||||
StringFunc::Path::Split(filePath, nullptr, &sourcePath, &sourceName, &sourceExtension);
|
|
||||||
// if missing create folders leading to file's location and get immediate parent
|
// if missing create folders leading to file's location and get immediate parent
|
||||||
// (we don't need to have fileIds for any folders created yet, they will be added later)
|
// (we don't need to have fileIds for any folders created yet, they will be added later)
|
||||||
auto parent = CreateFolders(sourcePath.c_str(), scanFolder);
|
auto parent = CreateFolders(absoluteFilePath.ParentPath().Native(), scanFolder);
|
||||||
// for simplicity in AB, files are represented as sources, but they are missing SourceDatabaseEntry-specific information such as SourceUuid
|
// for simplicity in AB, files are represented as sources, but they are missing SourceDatabaseEntry-specific information such as SourceUuid
|
||||||
auto source = aznew SourceAssetBrowserEntry();
|
auto source = aznew SourceAssetBrowserEntry();
|
||||||
source->m_name = (sourceName + sourceExtension).c_str();
|
source->m_name = absoluteFilePath.Filename().Native();
|
||||||
source->m_fileId = fileDatabaseEntry.m_fileID;
|
source->m_fileId = fileDatabaseEntry.m_fileID;
|
||||||
source->m_displayName = QString::fromUtf8(source->m_name.c_str());
|
source->m_displayName = QString::fromUtf8(source->m_name.c_str());
|
||||||
source->m_scanFolderId = fileDatabaseEntry.m_scanFolderPK;
|
source->m_scanFolderId = fileDatabaseEntry.m_scanFolderPK;
|
||||||
source->m_extension = sourceExtension.c_str();
|
source->m_extension = absoluteFilePath.Extension().Native();
|
||||||
parent->AddChild(source);
|
parent->AddChild(source);
|
||||||
file = source;
|
file = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
EntryCache::GetInstance()->m_fileIdMap[fileDatabaseEntry.m_fileID] = file;
|
EntryCache::GetInstance()->m_fileIdMap[fileDatabaseEntry.m_fileID] = file;
|
||||||
AZStd::string fullPath = file->m_fullPath;
|
AZStd::string filePath = AZ::IO::PathView(file->m_fullPath).LexicallyNormal().String();
|
||||||
AzFramework::StringFunc::Path::Normalize(fullPath);
|
EntryCache::GetInstance()->m_absolutePathToFileId[filePath] = fileDatabaseEntry.m_fileID;
|
||||||
EntryCache::GetInstance()->m_absolutePathToFileId[fullPath] = fileDatabaseEntry.m_fileID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RootAssetBrowserEntry::RemoveFile(const AZ::s64& fileId) const
|
bool RootAssetBrowserEntry::RemoveFile(const AZ::s64& fileId) const
|
||||||
@@ -305,116 +303,95 @@ namespace AzToolsFramework
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FolderAssetBrowserEntry* RootAssetBrowserEntry::CreateFolder(const char* folderName, AssetBrowserEntry* parent)
|
AssetBrowserEntry* RootAssetBrowserEntry::GetNearestAncestor(AZ::IO::PathView absolutePathView, AssetBrowserEntry* parent,
|
||||||
|
AZStd::unordered_set<AssetBrowserEntry*>& visitedSet)
|
||||||
|
{
|
||||||
|
auto IsPathRelativeToEntry = [absolutePathView](AssetBrowserEntry* assetBrowserEntry)
|
||||||
|
{
|
||||||
|
auto& childPath = assetBrowserEntry->m_fullPath;
|
||||||
|
return absolutePathView.IsRelativeTo(AZ::IO::PathView(childPath));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (visitedSet.contains(parent))
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
visitedSet.insert(parent);
|
||||||
|
|
||||||
|
AssetBrowserEntry* nearestAncestor{};
|
||||||
|
for (AssetBrowserEntry* childBrowserEntry : parent->m_children)
|
||||||
|
{
|
||||||
|
if (IsPathRelativeToEntry(childBrowserEntry))
|
||||||
|
{
|
||||||
|
// Walk the AssetBrowserEntry Tree looking for a nearer ancestor to the absolute path
|
||||||
|
// If one is not found in the recursive call to GetNearestAncestor, then the childBrowserEntry
|
||||||
|
// is the current best candidate
|
||||||
|
AssetBrowserEntry* candidateAncestor = GetNearestAncestor(absolutePathView, childBrowserEntry, visitedSet);
|
||||||
|
candidateAncestor = candidateAncestor != nullptr ? candidateAncestor : childBrowserEntry;
|
||||||
|
AZ::IO::PathView candidatePathView(candidateAncestor->m_fullPath);
|
||||||
|
// If the candidate is relative to the current nearest ancestor, then it is even nearer to the path
|
||||||
|
if (!nearestAncestor || candidatePathView.IsRelativeTo(nearestAncestor->m_fullPath))
|
||||||
|
{
|
||||||
|
nearestAncestor = candidateAncestor;
|
||||||
|
// If the full path compares equal to the AssetBrowserEntry path, then no need to proceed any further
|
||||||
|
if (AZ::IO::PathView(nearestAncestor->m_fullPath) == absolutePathView)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nearestAncestor;
|
||||||
|
}
|
||||||
|
|
||||||
|
FolderAssetBrowserEntry* RootAssetBrowserEntry::CreateFolder(AZStd::string_view folderName, AssetBrowserEntry* parent)
|
||||||
{
|
{
|
||||||
auto it = AZStd::find_if(parent->m_children.begin(), parent->m_children.end(), [folderName](AssetBrowserEntry* entry)
|
auto it = AZStd::find_if(parent->m_children.begin(), parent->m_children.end(), [folderName](AssetBrowserEntry* entry)
|
||||||
{
|
{
|
||||||
if (!azrtti_istypeof<FolderAssetBrowserEntry*>(entry))
|
if (!azrtti_istypeof<FolderAssetBrowserEntry*>(entry))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return AzFramework::StringFunc::Equal(entry->m_name.c_str(), folderName);
|
return AZ::IO::PathView(entry->m_name) == AZ::IO::PathView(folderName);
|
||||||
});
|
});
|
||||||
if (it != parent->m_children.end())
|
if (it != parent->m_children.end())
|
||||||
{
|
{
|
||||||
return azrtti_cast<FolderAssetBrowserEntry*>(*it);
|
return azrtti_cast<FolderAssetBrowserEntry*>(*it);
|
||||||
}
|
}
|
||||||
const auto folder = aznew FolderAssetBrowserEntry();
|
const auto folder = aznew FolderAssetBrowserEntry();
|
||||||
folder->m_name = folderName;
|
folder->m_name = folderName;
|
||||||
folder->m_displayName = folderName;
|
folder->m_displayName = QString::fromUtf8(folderName.data(), aznumeric_caster(folderName.size()));
|
||||||
parent->AddChild(folder);
|
parent->AddChild(folder);
|
||||||
return folder;
|
return folder;
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetBrowserEntry* RootAssetBrowserEntry::CreateFolders(const char* relativePath, AssetBrowserEntry* parent)
|
AssetBrowserEntry* RootAssetBrowserEntry::CreateFolders(AZStd::string_view absolutePath, AssetBrowserEntry* parent)
|
||||||
{
|
{
|
||||||
auto children(parent->m_children);
|
AZ::IO::PathView absolutePathView(absolutePath);
|
||||||
int n = 0;
|
// Find the nearest ancestor path to the absolutePath
|
||||||
|
AZStd::unordered_set<AssetBrowserEntry*> visitedSet;
|
||||||
|
|
||||||
// check if folder with the same name already exists
|
if (AssetBrowserEntry* nearestAncestor = GetNearestAncestor(absolutePathView, parent, visitedSet);
|
||||||
// step through every character in relativePath and compare to each child's relative path of suggested parent
|
nearestAncestor != nullptr)
|
||||||
// if a character @n in child's rel path mismatches character at n in relativePath, remove that child from further search
|
|
||||||
while (!children.empty() && relativePath[n])
|
|
||||||
{
|
{
|
||||||
AZStd::vector<AssetBrowserEntry*> toRemove;
|
parent = nearestAncestor;
|
||||||
for (auto child : children)
|
|
||||||
{
|
|
||||||
auto& childPath = azrtti_istypeof<RootAssetBrowserEntry*>(parent) ? child->m_fullPath : child->m_relativePath;
|
|
||||||
|
|
||||||
// child's path mismatched, remove it from search candidates
|
|
||||||
if (childPath.length() == n || childPath[n] != relativePath[n])
|
|
||||||
{
|
|
||||||
toRemove.push_back(child);
|
|
||||||
|
|
||||||
// it is possible that child may be a closer parent, substitute it as new potential parent
|
|
||||||
// e.g. child->m_relativePath = 'Gems', relativePath = 'Gems/Assets', old parent = root, new parent = Gems
|
|
||||||
if (childPath.length() == n && relativePath[n] == AZ_CORRECT_DATABASE_SEPARATOR)
|
|
||||||
{
|
|
||||||
parent = child;
|
|
||||||
relativePath += n; // advance relative path n characters since the parent has changed
|
|
||||||
n = 0; // Once the relative path pointer is advanced, reset n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto entry : toRemove)
|
|
||||||
{
|
|
||||||
children.erase(AZStd::remove(children.begin(), children.end(), entry), children.end());
|
|
||||||
}
|
|
||||||
n++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter out the remaining children that don't end with '/' or '\0'
|
// If the nearest ancestor is the absolutePath, then it is already crated
|
||||||
// for example if folderName = "foo", while children may still remain with names like "foo123",
|
if (absolutePathView == AZ::IO::PathView(parent->GetFullPath()))
|
||||||
// which is not the same folder
|
|
||||||
AZStd::vector<AssetBrowserEntry*> toRemove;
|
|
||||||
for (auto child : children)
|
|
||||||
{
|
{
|
||||||
auto& childPath = azrtti_istypeof<RootAssetBrowserEntry*>(parent) ? child->m_fullPath : child->m_relativePath;
|
return parent;
|
||||||
// check if there are non-null characters remaining @n
|
|
||||||
if (childPath.length() > n)
|
|
||||||
{
|
|
||||||
toRemove.push_back(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto entry : toRemove)
|
|
||||||
{
|
|
||||||
children.erase(AZStd::remove(children.begin(), children.end(), entry), children.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// at least one child remains, this means the folder with this name already exists, return it
|
// create all missing folders
|
||||||
if (!children.empty())
|
auto proximateToPath = absolutePathView.IsRelativeTo(parent->m_fullPath)
|
||||||
|
? absolutePathView.LexicallyProximate(parent->m_fullPath)
|
||||||
|
: AZ::IO::FixedMaxPath(absolutePathView);
|
||||||
|
for (AZ::IO::FixedMaxPath scanFolderSegment : proximateToPath)
|
||||||
{
|
{
|
||||||
parent = children.front();
|
parent = CreateFolder(scanFolderSegment.c_str(), parent);
|
||||||
}
|
|
||||||
// if it's a scanfolder, then do not create folders leading to it
|
|
||||||
// e.g. instead of 'C:\dev\SampleProject' just create 'SampleProject'
|
|
||||||
else if (parent->GetEntryType() == AssetEntryType::Root)
|
|
||||||
{
|
|
||||||
AZStd::string folderName;
|
|
||||||
AzFramework::StringFunc::Path::Split(relativePath, nullptr, nullptr, &folderName);
|
|
||||||
parent = CreateFolder(folderName.c_str(), parent);
|
|
||||||
parent->m_fullPath = relativePath;
|
|
||||||
}
|
|
||||||
// otherwise create all missing folders
|
|
||||||
else
|
|
||||||
{
|
|
||||||
n = 0;
|
|
||||||
AZStd::string folderName(strlen(relativePath) + 1, '\0');
|
|
||||||
// iterate through relativePath until the first '/'
|
|
||||||
while (relativePath[n] && relativePath[n] != AZ_CORRECT_DATABASE_SEPARATOR)
|
|
||||||
{
|
|
||||||
folderName[n] = relativePath[n];
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
if (n > 0)
|
|
||||||
{
|
|
||||||
parent = CreateFolder(folderName.c_str(), parent);
|
|
||||||
}
|
|
||||||
// n+1 also skips the '/' character
|
|
||||||
if (relativePath[n] && relativePath[n + 1])
|
|
||||||
{
|
|
||||||
parent = CreateFolders(relativePath + n + 1, parent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
@@ -422,7 +399,7 @@ namespace AzToolsFramework
|
|||||||
void RootAssetBrowserEntry::UpdateChildPaths(AssetBrowserEntry* child) const
|
void RootAssetBrowserEntry::UpdateChildPaths(AssetBrowserEntry* child) const
|
||||||
{
|
{
|
||||||
child->m_relativePath = child->m_name;
|
child->m_relativePath = child->m_name;
|
||||||
child->m_fullPath = child->m_name;
|
child->m_fullPath = m_fullPath / child->m_name;
|
||||||
AssetBrowserEntry::UpdateChildPaths(child);
|
AssetBrowserEntry::UpdateChildPaths(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-4
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <AzCore/std/string/string.h>
|
#include <AzCore/std/string/string.h>
|
||||||
#include <AzCore/Asset/AssetCommon.h>
|
#include <AzCore/Asset/AssetCommon.h>
|
||||||
|
#include <AzCore/IO/Path/Path.h>
|
||||||
#include <AzCore/Math/Uuid.h>
|
#include <AzCore/Math/Uuid.h>
|
||||||
|
|
||||||
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
|
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
|
||||||
@@ -77,12 +78,15 @@ namespace AzToolsFramework
|
|||||||
private:
|
private:
|
||||||
AZ_DISABLE_COPY_MOVE(RootAssetBrowserEntry);
|
AZ_DISABLE_COPY_MOVE(RootAssetBrowserEntry);
|
||||||
|
|
||||||
AZStd::string m_enginePath;
|
AZ::IO::Path m_enginePath;
|
||||||
|
|
||||||
//! Create folder entry child
|
//! Create folder entry child
|
||||||
FolderAssetBrowserEntry* CreateFolder(const char* folderName, AssetBrowserEntry* parent);
|
FolderAssetBrowserEntry* CreateFolder(AZStd::string_view folderName, AssetBrowserEntry* parent);
|
||||||
//! Recursively create folder structure leading to relative path from parent
|
//! Recursively create folder structure leading to path from parent
|
||||||
AssetBrowserEntry* CreateFolders(const char* relativePath, AssetBrowserEntry* parent);
|
AssetBrowserEntry* CreateFolders(AZStd::string_view absolutePath, AssetBrowserEntry* parent);
|
||||||
|
// Retrieves the nearest ancestor AssetBrowserEntry from the absolutePath
|
||||||
|
static AssetBrowserEntry* GetNearestAncestor(AZ::IO::PathView absolutePath, AssetBrowserEntry* parent,
|
||||||
|
AZStd::unordered_set<AssetBrowserEntry*>& visitedSet);
|
||||||
|
|
||||||
bool m_isInitialUpdate = false;
|
bool m_isInitialUpdate = false;
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-3
@@ -26,9 +26,7 @@ namespace AzToolsFramework
|
|||||||
if (EntryCache* cache = EntryCache::GetInstance())
|
if (EntryCache* cache = EntryCache::GetInstance())
|
||||||
{
|
{
|
||||||
cache->m_fileIdMap.erase(m_fileId);
|
cache->m_fileIdMap.erase(m_fileId);
|
||||||
AZStd::string fullPath = m_fullPath;
|
cache->m_absolutePathToFileId.erase(m_fullPath.LexicallyNormal().Native());
|
||||||
AzFramework::StringFunc::Path::Normalize(fullPath);
|
|
||||||
cache->m_absolutePathToFileId.erase(fullPath);
|
|
||||||
|
|
||||||
if (m_sourceId != -1)
|
if (m_sourceId != -1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ foreach(project_name project_path IN ZIP_LISTS LY_PROJECTS_TARGET_NAME LY_PROJEC
|
|||||||
add_custom_target(${project_name}.Assets
|
add_custom_target(${project_name}.Assets
|
||||||
COMMENT "Processing ${project_name} assets..."
|
COMMENT "Processing ${project_name} assets..."
|
||||||
COMMAND "${CMAKE_COMMAND}"
|
COMMAND "${CMAKE_COMMAND}"
|
||||||
-DLY_LOCK_FILE=$<TARGET_FILE_DIR:AZ::AssetProcessorBatch>/project_assets.lock
|
-DLY_LOCK_FILE=$<GENEX_EVAL:$<TARGET_FILE_DIR:AZ::AssetProcessorBatch>>/project_assets.lock
|
||||||
-P ${LY_ROOT_FOLDER}/cmake/CommandExecution.cmake
|
-P ${LY_ROOT_FOLDER}/cmake/CommandExecution.cmake
|
||||||
EXEC_COMMAND $<TARGET_FILE:AZ::AssetProcessorBatch>
|
EXEC_COMMAND $<GENEX_EVAL:$<TARGET_FILE:AZ::AssetProcessorBatch>>
|
||||||
--zeroAnalysisMode
|
--zeroAnalysisMode
|
||||||
--project-path=${project_real_path}
|
--project-path=${project_real_path}
|
||||||
--platforms=${LY_ASSET_DEPLOY_ASSET_TYPE}
|
--platforms=${LY_ASSET_DEPLOY_ASSET_TYPE}
|
||||||
|
|||||||
@@ -38,8 +38,19 @@ ly_add_target(
|
|||||||
string(REPLACE "." ";" version_list "${LY_VERSION_STRING}")
|
string(REPLACE "." ";" version_list "${LY_VERSION_STRING}")
|
||||||
list(GET version_list 0 EXE_VERSION_INFO_0)
|
list(GET version_list 0 EXE_VERSION_INFO_0)
|
||||||
list(GET version_list 1 EXE_VERSION_INFO_1)
|
list(GET version_list 1 EXE_VERSION_INFO_1)
|
||||||
list(GET version_list 2 EXE_VERSION_INFO_2)
|
|
||||||
list(GET version_list 3 EXE_VERSION_INFO_3)
|
list(LENGTH version_list version_component_count)
|
||||||
|
if(${version_component_count} GREATER_EQUAL 3)
|
||||||
|
list(GET version_list 2 EXE_VERSION_INFO_2)
|
||||||
|
else()
|
||||||
|
set(EXE_VERSION_INFO_2 0)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(${version_component_count} GREATER_EQUAL 4)
|
||||||
|
list(GET version_list 3 EXE_VERSION_INFO_3)
|
||||||
|
else()
|
||||||
|
set(EXE_VERSION_INFO_3 0)
|
||||||
|
endif()
|
||||||
|
|
||||||
ly_add_source_properties(
|
ly_add_source_properties(
|
||||||
SOURCES Shared/CrashHandler.cpp
|
SOURCES Shared/CrashHandler.cpp
|
||||||
|
|||||||
@@ -8,4 +8,6 @@
|
|||||||
set(FILES
|
set(FILES
|
||||||
Python_linux.cpp
|
Python_linux.cpp
|
||||||
ProjectBuilderWorker_linux.cpp
|
ProjectBuilderWorker_linux.cpp
|
||||||
|
ProjectUtils_linux.cpp
|
||||||
|
ProjectManagerDefs_linux.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* 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 <ProjectManagerDefs.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace O3DE::ProjectManager
|
||||||
|
{
|
||||||
|
const QString ProjectBuildPathPostfix = ProjectBuildDirectoryName + "/linux";
|
||||||
|
|
||||||
|
} // namespace O3DE::ProjectManager
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* 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 <ProjectUtils.h>
|
||||||
|
|
||||||
|
namespace O3DE::ProjectManager
|
||||||
|
{
|
||||||
|
namespace ProjectUtils
|
||||||
|
{
|
||||||
|
AZ::Outcome<void, QString> FindSupportedCompilerForPlatform()
|
||||||
|
{
|
||||||
|
// Compiler detection not supported on platform
|
||||||
|
return AZ::Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ProjectUtils
|
||||||
|
} // namespace O3DE::ProjectManager
|
||||||
@@ -8,4 +8,6 @@
|
|||||||
set(FILES
|
set(FILES
|
||||||
Python_mac.cpp
|
Python_mac.cpp
|
||||||
ProjectBuilderWorker_mac.cpp
|
ProjectBuilderWorker_mac.cpp
|
||||||
|
ProjectUtils_mac.cpp
|
||||||
|
ProjectManagerDefs_mac.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* 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 <ProjectManagerDefs.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace O3DE::ProjectManager
|
||||||
|
{
|
||||||
|
const QString ProjectBuildPathPostfix = ProjectBuildDirectoryName + "/mac_xcode";
|
||||||
|
|
||||||
|
} // namespace O3DE::ProjectManager
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* 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 <ProjectUtils.h>
|
||||||
|
|
||||||
|
namespace O3DE::ProjectManager
|
||||||
|
{
|
||||||
|
namespace ProjectUtils
|
||||||
|
{
|
||||||
|
AZ::Outcome<void, QString> FindSupportedCompilerForPlatform()
|
||||||
|
{
|
||||||
|
// Compiler detection not supported on platform
|
||||||
|
return AZ::Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ProjectUtils
|
||||||
|
} // namespace O3DE::ProjectManager
|
||||||
@@ -8,4 +8,6 @@
|
|||||||
set(FILES
|
set(FILES
|
||||||
Python_windows.cpp
|
Python_windows.cpp
|
||||||
ProjectBuilderWorker_windows.cpp
|
ProjectBuilderWorker_windows.cpp
|
||||||
|
ProjectUtils_windows.cpp
|
||||||
|
ProjectManagerDefs_windows.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -75,8 +75,17 @@ namespace O3DE::ProjectManager
|
|||||||
|
|
||||||
m_configProjectProcess->start(
|
m_configProjectProcess->start(
|
||||||
"cmake",
|
"cmake",
|
||||||
QStringList{ "-B", QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix), "-S", m_projectInfo.m_path, "-G",
|
QStringList
|
||||||
"Visual Studio 16", "-DLY_3RDPARTY_PATH=" + engineInfo.m_thirdPartyPath });
|
{
|
||||||
|
"-B",
|
||||||
|
QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix),
|
||||||
|
"-S",
|
||||||
|
m_projectInfo.m_path,
|
||||||
|
"-G",
|
||||||
|
"Visual Studio 16",
|
||||||
|
"-DLY_3RDPARTY_PATH=" + engineInfo.m_thirdPartyPath,
|
||||||
|
"-DLY_UNITY_BUILD=1"
|
||||||
|
});
|
||||||
|
|
||||||
if (!m_configProjectProcess->waitForStarted())
|
if (!m_configProjectProcess->waitForStarted())
|
||||||
{
|
{
|
||||||
@@ -124,8 +133,16 @@ namespace O3DE::ProjectManager
|
|||||||
|
|
||||||
m_buildProjectProcess->start(
|
m_buildProjectProcess->start(
|
||||||
"cmake",
|
"cmake",
|
||||||
QStringList{ "--build", QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix), "--target",
|
QStringList
|
||||||
m_projectInfo.m_projectName + ".GameLauncher", "Editor", "--config", "profile" });
|
{
|
||||||
|
"--build",
|
||||||
|
QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix),
|
||||||
|
"--target",
|
||||||
|
m_projectInfo.m_projectName + ".GameLauncher",
|
||||||
|
"Editor",
|
||||||
|
"--config",
|
||||||
|
"profile"
|
||||||
|
});
|
||||||
|
|
||||||
if (!m_buildProjectProcess->waitForStarted())
|
if (!m_buildProjectProcess->waitForStarted())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* 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 <ProjectManagerDefs.h>
|
||||||
|
|
||||||
|
namespace O3DE::ProjectManager
|
||||||
|
{
|
||||||
|
const QString ProjectBuildPathPostfix = ProjectBuildDirectoryName + "/windows_vs2019";
|
||||||
|
|
||||||
|
} // namespace O3DE::ProjectManager
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* 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 <ProjectUtils.h>
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QProcessEnvironment>
|
||||||
|
|
||||||
|
namespace O3DE::ProjectManager
|
||||||
|
{
|
||||||
|
namespace ProjectUtils
|
||||||
|
{
|
||||||
|
AZ::Outcome<void, QString> FindSupportedCompilerForPlatform()
|
||||||
|
{
|
||||||
|
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
|
||||||
|
QString programFilesPath = environment.value("ProgramFiles(x86)");
|
||||||
|
QString vsWherePath = QDir(programFilesPath).filePath("Microsoft Visual Studio/Installer/vswhere.exe");
|
||||||
|
|
||||||
|
QFileInfo vsWhereFile(vsWherePath);
|
||||||
|
if (vsWhereFile.exists() && vsWhereFile.isFile())
|
||||||
|
{
|
||||||
|
QProcess vsWhereProcess;
|
||||||
|
vsWhereProcess.setProcessChannelMode(QProcess::MergedChannels);
|
||||||
|
|
||||||
|
vsWhereProcess.start(
|
||||||
|
vsWherePath,
|
||||||
|
QStringList{
|
||||||
|
"-version",
|
||||||
|
"16.0",
|
||||||
|
"-latest",
|
||||||
|
"-requires",
|
||||||
|
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
|
||||||
|
"-property",
|
||||||
|
"isComplete"
|
||||||
|
});
|
||||||
|
|
||||||
|
if (vsWhereProcess.waitForStarted() && vsWhereProcess.waitForFinished())
|
||||||
|
{
|
||||||
|
QString vsWhereOutput(vsWhereProcess.readAllStandardOutput());
|
||||||
|
if (vsWhereOutput.startsWith("1"))
|
||||||
|
{
|
||||||
|
return AZ::Success();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AZ::Failure(QObject::tr("Visual Studio 2019 not found.\n\n"
|
||||||
|
"Visual Studio 2019 is required to build this project."
|
||||||
|
" Install any edition of <a href='https://visualstudio.microsoft.com/downloads/'>Visual Studio 2019</a>"
|
||||||
|
" before proceeding to the next step."));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ProjectUtils
|
||||||
|
} // namespace O3DE::ProjectManager
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <ScreenHeaderWidget.h>
|
#include <ScreenHeaderWidget.h>
|
||||||
#include <GemCatalog/GemModel.h>
|
#include <GemCatalog/GemModel.h>
|
||||||
#include <GemCatalog/GemCatalogScreen.h>
|
#include <GemCatalog/GemCatalogScreen.h>
|
||||||
|
#include <ProjectUtils.h>
|
||||||
|
|
||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
@@ -222,38 +223,42 @@ namespace O3DE::ProjectManager
|
|||||||
|
|
||||||
void CreateProjectCtrl::CreateProject()
|
void CreateProjectCtrl::CreateProject()
|
||||||
{
|
{
|
||||||
if (m_newProjectSettingsScreen->Validate())
|
if (ProjectUtils::FindSupportedCompiler(this))
|
||||||
{
|
{
|
||||||
ProjectInfo projectInfo = m_newProjectSettingsScreen->GetProjectInfo();
|
if (m_newProjectSettingsScreen->Validate())
|
||||||
QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath();
|
|
||||||
|
|
||||||
auto result = PythonBindingsInterface::Get()->CreateProject(projectTemplatePath, projectInfo);
|
|
||||||
if (result.IsSuccess())
|
|
||||||
{
|
{
|
||||||
// automatically register the project
|
ProjectInfo projectInfo = m_newProjectSettingsScreen->GetProjectInfo();
|
||||||
PythonBindingsInterface::Get()->AddProject(projectInfo.m_path);
|
QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath();
|
||||||
|
|
||||||
|
auto result = PythonBindingsInterface::Get()->CreateProject(projectTemplatePath, projectInfo);
|
||||||
|
if (result.IsSuccess())
|
||||||
|
{
|
||||||
|
// automatically register the project
|
||||||
|
PythonBindingsInterface::Get()->AddProject(projectInfo.m_path);
|
||||||
|
|
||||||
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
|
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||||
if (!m_gemCatalogScreen->EnableDisableGemsForProject(projectInfo.m_path))
|
if (!m_gemCatalogScreen->EnableDisableGemsForProject(projectInfo.m_path))
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this, tr("Failed to configure gems"), tr("Failed to configure gems for template."));
|
QMessageBox::critical(this, tr("Failed to configure gems"), tr("Failed to configure gems for template."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
|
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||||
|
|
||||||
projectInfo.m_needsBuild = true;
|
projectInfo.m_needsBuild = true;
|
||||||
emit NotifyBuildProject(projectInfo);
|
emit NotifyBuildProject(projectInfo);
|
||||||
emit ChangeScreenRequest(ProjectManagerScreen::Projects);
|
emit ChangeScreenRequest(ProjectManagerScreen::Projects);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, tr("Project creation failed"), tr("Failed to create project."));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this, tr("Project creation failed"), tr("Failed to create project."));
|
QMessageBox::warning(
|
||||||
|
this, tr("Invalid project settings"), tr("Please correct the indicated project settings and try again."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
QMessageBox::warning(this, tr("Invalid project settings"), tr("Please correct the indicated project settings and try again."));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateProjectCtrl::ReinitGemCatalogForSelectedTemplate()
|
void CreateProjectCtrl::ReinitGemCatalogForSelectedTemplate()
|
||||||
|
|||||||
@@ -70,8 +70,9 @@ namespace O3DE::ProjectManager
|
|||||||
m_lastProgress = progress;
|
m_lastProgress = progress;
|
||||||
if (m_projectButton)
|
if (m_projectButton)
|
||||||
{
|
{
|
||||||
m_projectButton->SetButtonOverlayText(QString("%1 (%2%)\n\n").arg(tr("Building Project..."), QString::number(progress)));
|
m_projectButton->SetButtonOverlayText(QString("%1 (%2%)<br>%3<br>").arg(tr("Building Project..."), QString::number(progress), tr("Click to <a href=\"logs\">view logs</a>.")));
|
||||||
m_projectButton->SetProgressBarValue(progress);
|
m_projectButton->SetProgressBarValue(progress);
|
||||||
|
m_projectButton->SetBuildLogsLink(m_worker->GetLogFilePath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,6 @@
|
|||||||
|
|
||||||
namespace O3DE::ProjectManager
|
namespace O3DE::ProjectManager
|
||||||
{
|
{
|
||||||
const QString ProjectBuilderWorker::BuildCancelled = QObject::tr("Build Cancelled.");
|
|
||||||
|
|
||||||
ProjectBuilderWorker::ProjectBuilderWorker(const ProjectInfo& projectInfo)
|
ProjectBuilderWorker::ProjectBuilderWorker(const ProjectInfo& projectInfo)
|
||||||
: QObject()
|
: QObject()
|
||||||
, m_projectInfo(projectInfo)
|
, m_projectInfo(projectInfo)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace O3DE::ProjectManager
|
|||||||
// QProcess::waitForFinished uses -1 to indicate that the process should not timeout
|
// QProcess::waitForFinished uses -1 to indicate that the process should not timeout
|
||||||
static constexpr int MaxBuildTimeMSecs = -1;
|
static constexpr int MaxBuildTimeMSecs = -1;
|
||||||
// Build was cancelled
|
// Build was cancelled
|
||||||
static const QString BuildCancelled;
|
inline static const QString BuildCancelled = QObject::tr("Build Cancelled.");
|
||||||
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ namespace O3DE::ProjectManager
|
|||||||
m_overlayLabel->setObjectName("labelButtonOverlay");
|
m_overlayLabel->setObjectName("labelButtonOverlay");
|
||||||
m_overlayLabel->setWordWrap(true);
|
m_overlayLabel->setWordWrap(true);
|
||||||
m_overlayLabel->setAlignment(Qt::AlignCenter);
|
m_overlayLabel->setAlignment(Qt::AlignCenter);
|
||||||
|
m_overlayLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
|
||||||
m_overlayLabel->setVisible(false);
|
m_overlayLabel->setVisible(false);
|
||||||
|
connect(m_overlayLabel, &QLabel::linkActivated, this, &LabelButton::OnLinkActivated);
|
||||||
vLayout->addWidget(m_overlayLabel);
|
vLayout->addWidget(m_overlayLabel);
|
||||||
|
|
||||||
m_buildOverlayLayout = new QVBoxLayout();
|
m_buildOverlayLayout = new QVBoxLayout();
|
||||||
@@ -231,7 +233,7 @@ namespace O3DE::ProjectManager
|
|||||||
AzQtComponents::ShowFileOnDesktop(m_projectInfo.m_path);
|
AzQtComponents::ShowFileOnDesktop(m_projectInfo.m_path);
|
||||||
});
|
});
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
menu->addAction(tr("Duplicate"), this, [this]() { emit CopyProject(m_projectInfo.m_path); });
|
menu->addAction(tr("Duplicate"), this, [this]() { emit CopyProject(m_projectInfo); });
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
menu->addAction(tr("Remove from O3DE"), this, [this]() { emit RemoveProject(m_projectInfo.m_path); });
|
menu->addAction(tr("Remove from O3DE"), this, [this]() { emit RemoveProject(m_projectInfo.m_path); });
|
||||||
menu->addAction(tr("Delete this Project"), this, [this]() { emit DeleteProject(m_projectInfo.m_path); });
|
menu->addAction(tr("Delete this Project"), this, [this]() { emit DeleteProject(m_projectInfo.m_path); });
|
||||||
@@ -266,6 +268,11 @@ namespace O3DE::ProjectManager
|
|||||||
SetProjectButtonAction(tr("Build Project"), [this]() { emit BuildProject(m_projectInfo); });
|
SetProjectButtonAction(tr("Build Project"), [this]() { emit BuildProject(m_projectInfo); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProjectButton::SetBuildLogsLink(const QUrl& logUrl)
|
||||||
|
{
|
||||||
|
m_projectImageLabel->SetLogUrl(logUrl);
|
||||||
|
}
|
||||||
|
|
||||||
void ProjectButton::ShowBuildFailed(bool show, const QUrl& logUrl)
|
void ProjectButton::ShowBuildFailed(bool show, const QUrl& logUrl)
|
||||||
{
|
{
|
||||||
if (!logUrl.isEmpty())
|
if (!logUrl.isEmpty())
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ namespace O3DE::ProjectManager
|
|||||||
|
|
||||||
void SetProjectButtonAction(const QString& text, AZStd::function<void()> lambda);
|
void SetProjectButtonAction(const QString& text, AZStd::function<void()> lambda);
|
||||||
void SetProjectBuildButtonAction();
|
void SetProjectBuildButtonAction();
|
||||||
|
void SetBuildLogsLink(const QUrl& logUrl);
|
||||||
void ShowBuildFailed(bool show, const QUrl& logUrl);
|
void ShowBuildFailed(bool show, const QUrl& logUrl);
|
||||||
|
|
||||||
void SetLaunchButtonEnabled(bool enabled);
|
void SetLaunchButtonEnabled(bool enabled);
|
||||||
@@ -87,7 +88,7 @@ namespace O3DE::ProjectManager
|
|||||||
signals:
|
signals:
|
||||||
void OpenProject(const QString& projectName);
|
void OpenProject(const QString& projectName);
|
||||||
void EditProject(const QString& projectName);
|
void EditProject(const QString& projectName);
|
||||||
void CopyProject(const QString& projectName);
|
void CopyProject(const ProjectInfo& projectInfo);
|
||||||
void RemoveProject(const QString& projectName);
|
void RemoveProject(const QString& projectName);
|
||||||
void DeleteProject(const QString& projectName);
|
void DeleteProject(const QString& projectName);
|
||||||
void BuildProject(const ProjectInfo& projectInfo);
|
void BuildProject(const ProjectInfo& projectInfo);
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ namespace O3DE::ProjectManager
|
|||||||
inline constexpr static int ProjectPreviewImageHeight = 280;
|
inline constexpr static int ProjectPreviewImageHeight = 280;
|
||||||
inline constexpr static int ProjectTemplateImageWidth = 92;
|
inline constexpr static int ProjectTemplateImageWidth = 92;
|
||||||
|
|
||||||
static const QString ProjectBuildPathPostfix = "build/windows_vs2019";
|
static const QString ProjectBuildDirectoryName = "build";
|
||||||
|
extern const QString ProjectBuildPathPostfix;
|
||||||
static const QString ProjectBuildPathCmakeFiles = "CMakeFiles";
|
static const QString ProjectBuildPathCmakeFiles = "CMakeFiles";
|
||||||
static const QString ProjectBuildErrorLogName = "CMakeProjectBuildError.log";
|
static const QString ProjectBuildErrorLogName = "CMakeProjectBuildError.log";
|
||||||
|
static const QString ProjectCacheDirectoryName = "Cache";
|
||||||
static const QString ProjectPreviewImagePath = "preview.png";
|
static const QString ProjectPreviewImagePath = "preview.png";
|
||||||
} // namespace O3DE::ProjectManager
|
} // namespace O3DE::ProjectManager
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <ProjectUtils.h>
|
#include <ProjectUtils.h>
|
||||||
|
#include <ProjectManagerDefs.h>
|
||||||
#include <PythonBindingsInterface.h>
|
#include <PythonBindingsInterface.h>
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
@@ -17,6 +18,8 @@
|
|||||||
#include <QProcessEnvironment>
|
#include <QProcessEnvironment>
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QProgressDialog>
|
#include <QProgressDialog>
|
||||||
|
#include <QSpacerItem>
|
||||||
|
#include <QGridLayout>
|
||||||
|
|
||||||
namespace O3DE::ProjectManager
|
namespace O3DE::ProjectManager
|
||||||
{
|
{
|
||||||
@@ -57,29 +60,63 @@ namespace O3DE::ProjectManager
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool SkipFilePaths(const QString& curPath, QStringList& skippedPaths, QStringList& deeperSkippedPaths)
|
||||||
|
{
|
||||||
|
bool skip = false;
|
||||||
|
for (const QString& skippedPath : skippedPaths)
|
||||||
|
{
|
||||||
|
QString nativeSkippedPath = QDir::toNativeSeparators(skippedPath);
|
||||||
|
QString firstSectionSkippedPath = nativeSkippedPath.section(QDir::separator(), 0, 0);
|
||||||
|
if (curPath == firstSectionSkippedPath)
|
||||||
|
{
|
||||||
|
// We are at the end of the path to skip, so skip it
|
||||||
|
if (nativeSkippedPath == firstSectionSkippedPath)
|
||||||
|
{
|
||||||
|
skippedPaths.removeAll(skippedPath);
|
||||||
|
skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Append the next section of the skipped path
|
||||||
|
else
|
||||||
|
{
|
||||||
|
deeperSkippedPaths.append(nativeSkippedPath.section(QDir::separator(), 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return skip;
|
||||||
|
}
|
||||||
|
|
||||||
typedef AZStd::function<void(/*fileCount=*/int, /*totalSizeInBytes=*/int)> StatusFunction;
|
typedef AZStd::function<void(/*fileCount=*/int, /*totalSizeInBytes=*/int)> StatusFunction;
|
||||||
static void RecursiveGetAllFiles(const QDir& directory, QStringList& outFileList, qint64& outTotalSizeInBytes, StatusFunction statusCallback)
|
static void RecursiveGetAllFiles(const QDir& directory, QStringList& skippedPaths, int& outFileCount, qint64& outTotalSizeInBytes, StatusFunction statusCallback)
|
||||||
{
|
{
|
||||||
const QStringList entries = directory.entryList(QDir::Dirs | QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot);
|
const QStringList entries = directory.entryList(QDir::Dirs | QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot);
|
||||||
for (const QString& entryPath : entries)
|
for (const QString& entryPath : entries)
|
||||||
{
|
{
|
||||||
const QString filePath = QDir::toNativeSeparators(QString("%1/%2").arg(directory.path()).arg(entryPath));
|
const QString filePath = QDir::toNativeSeparators(QString("%1/%2").arg(directory.path()).arg(entryPath));
|
||||||
|
|
||||||
|
QStringList deeperSkippedPaths;
|
||||||
|
if (SkipFilePaths(entryPath, skippedPaths, deeperSkippedPaths))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
QFileInfo fileInfo(filePath);
|
QFileInfo fileInfo(filePath);
|
||||||
|
|
||||||
if (fileInfo.isDir())
|
if (fileInfo.isDir())
|
||||||
{
|
{
|
||||||
QDir subDirectory(filePath);
|
QDir subDirectory(filePath);
|
||||||
RecursiveGetAllFiles(subDirectory, outFileList, outTotalSizeInBytes, statusCallback);
|
RecursiveGetAllFiles(subDirectory, deeperSkippedPaths, outFileCount, outTotalSizeInBytes, statusCallback);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
outFileList.push_back(filePath);
|
++outFileCount;
|
||||||
outTotalSizeInBytes += fileInfo.size();
|
outTotalSizeInBytes += fileInfo.size();
|
||||||
|
|
||||||
const int updateStatusEvery = 64;
|
const int updateStatusEvery = 64;
|
||||||
if (outFileList.size() % updateStatusEvery == 0)
|
if (outFileCount % updateStatusEvery == 0)
|
||||||
{
|
{
|
||||||
statusCallback(outFileList.size(), outTotalSizeInBytes);
|
statusCallback(outFileCount, outTotalSizeInBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,7 +125,8 @@ namespace O3DE::ProjectManager
|
|||||||
static bool CopyDirectory(QProgressDialog* progressDialog,
|
static bool CopyDirectory(QProgressDialog* progressDialog,
|
||||||
const QString& origPath,
|
const QString& origPath,
|
||||||
const QString& newPath,
|
const QString& newPath,
|
||||||
QStringList& filesToCopy,
|
QStringList& skippedPaths,
|
||||||
|
int filesToCopyCount,
|
||||||
int& outNumCopiedFiles,
|
int& outNumCopiedFiles,
|
||||||
qint64 totalSizeToCopy,
|
qint64 totalSizeToCopy,
|
||||||
qint64& outCopiedFileSize,
|
qint64& outCopiedFileSize,
|
||||||
@@ -100,18 +138,24 @@ namespace O3DE::ProjectManager
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (QString directory : original.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
|
for (const QString& directory : original.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
|
||||||
{
|
{
|
||||||
if (progressDialog->wasCanceled())
|
if (progressDialog->wasCanceled())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList deeperSkippedPaths;
|
||||||
|
if (SkipFilePaths(directory, skippedPaths, deeperSkippedPaths))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
QString newDirectoryPath = newPath + QDir::separator() + directory;
|
QString newDirectoryPath = newPath + QDir::separator() + directory;
|
||||||
original.mkpath(newDirectoryPath);
|
original.mkpath(newDirectoryPath);
|
||||||
|
|
||||||
if (!CopyDirectory(progressDialog, origPath + QDir::separator() + directory,
|
if (!CopyDirectory(progressDialog, origPath + QDir::separator() + directory, newDirectoryPath, deeperSkippedPaths,
|
||||||
newDirectoryPath, filesToCopy, outNumCopiedFiles, totalSizeToCopy, outCopiedFileSize, showIgnoreFileDialog))
|
filesToCopyCount, outNumCopiedFiles, totalSizeToCopy, outCopiedFileSize, showIgnoreFileDialog))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -119,18 +163,25 @@ namespace O3DE::ProjectManager
|
|||||||
|
|
||||||
QLocale locale;
|
QLocale locale;
|
||||||
const float progressDialogRangeHalf = qFabs(progressDialog->maximum() - progressDialog->minimum()) * 0.5f;
|
const float progressDialogRangeHalf = qFabs(progressDialog->maximum() - progressDialog->minimum()) * 0.5f;
|
||||||
for (QString file : original.entryList(QDir::Files))
|
for (const QString& file : original.entryList(QDir::Files))
|
||||||
{
|
{
|
||||||
if (progressDialog->wasCanceled())
|
if (progressDialog->wasCanceled())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unused by this function but neccesary to pass in to SkipFilePaths
|
||||||
|
QStringList deeperSkippedPaths;
|
||||||
|
if (SkipFilePaths(file, skippedPaths, deeperSkippedPaths))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Progress window update
|
// Progress window update
|
||||||
{
|
{
|
||||||
// Weight in the number of already copied files as well as the copied bytes to get a better progress indication
|
// Weight in the number of already copied files as well as the copied bytes to get a better progress indication
|
||||||
// for cases combining many small files and some really large files.
|
// for cases combining many small files and some really large files.
|
||||||
const float normalizedNumFiles = static_cast<float>(outNumCopiedFiles) / filesToCopy.count();
|
const float normalizedNumFiles = static_cast<float>(outNumCopiedFiles) / filesToCopyCount;
|
||||||
const float normalizedFileSize = static_cast<float>(outCopiedFileSize) / totalSizeToCopy;
|
const float normalizedFileSize = static_cast<float>(outCopiedFileSize) / totalSizeToCopy;
|
||||||
const int progress = normalizedNumFiles * progressDialogRangeHalf + normalizedFileSize * progressDialogRangeHalf;
|
const int progress = normalizedNumFiles * progressDialogRangeHalf + normalizedFileSize * progressDialogRangeHalf;
|
||||||
progressDialog->setValue(progress);
|
progressDialog->setValue(progress);
|
||||||
@@ -138,7 +189,7 @@ namespace O3DE::ProjectManager
|
|||||||
const QString copiedFileSizeString = locale.formattedDataSize(outCopiedFileSize);
|
const QString copiedFileSizeString = locale.formattedDataSize(outCopiedFileSize);
|
||||||
const QString totalFileSizeString = locale.formattedDataSize(totalSizeToCopy);
|
const QString totalFileSizeString = locale.formattedDataSize(totalSizeToCopy);
|
||||||
progressDialog->setLabelText(QString("Coping file %1 of %2 (%3 of %4) ...").arg(QString::number(outNumCopiedFiles),
|
progressDialog->setLabelText(QString("Coping file %1 of %2 (%3 of %4) ...").arg(QString::number(outNumCopiedFiles),
|
||||||
QString::number(filesToCopy.count()),
|
QString::number(filesToCopyCount),
|
||||||
copiedFileSizeString,
|
copiedFileSizeString,
|
||||||
totalFileSizeString));
|
totalFileSizeString));
|
||||||
qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
|
qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||||
@@ -192,6 +243,39 @@ namespace O3DE::ProjectManager
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool ClearProjectBuildArtifactsAndCache(const QString& origPath, const QString& newPath, QWidget* parent)
|
||||||
|
{
|
||||||
|
QDir buildDirectory = QDir(newPath);
|
||||||
|
if ((!buildDirectory.cd(ProjectBuildDirectoryName) || !DeleteProjectFiles(buildDirectory.path(), true))
|
||||||
|
&& QDir(origPath).cd(ProjectBuildDirectoryName))
|
||||||
|
{
|
||||||
|
QMessageBox::warning(
|
||||||
|
parent,
|
||||||
|
QObject::tr("Clear Build Artifacts"),
|
||||||
|
QObject::tr("Build artifacts failed to delete for moved project. Please manually delete build directory at \"%1\"")
|
||||||
|
.arg(buildDirectory.path()),
|
||||||
|
QMessageBox::Close);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir cacheDirectory = QDir(newPath);
|
||||||
|
if ((!cacheDirectory.cd(ProjectCacheDirectoryName) || !DeleteProjectFiles(cacheDirectory.path(), true))
|
||||||
|
&& QDir(origPath).cd(ProjectCacheDirectoryName))
|
||||||
|
{
|
||||||
|
QMessageBox::warning(
|
||||||
|
parent,
|
||||||
|
QObject::tr("Clear Asset Cache"),
|
||||||
|
QObject::tr("Asset cache failed to delete for moved project. Please manually delete cache directory at \"%1\"")
|
||||||
|
.arg(cacheDirectory.path()),
|
||||||
|
QMessageBox::Close);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool AddProjectDialog(QWidget* parent)
|
bool AddProjectDialog(QWidget* parent)
|
||||||
{
|
{
|
||||||
QString path = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(parent, QObject::tr("Select Project Directory")));
|
QString path = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(parent, QObject::tr("Select Project Directory")));
|
||||||
@@ -213,7 +297,7 @@ namespace O3DE::ProjectManager
|
|||||||
return PythonBindingsInterface::Get()->RemoveProject(path);
|
return PythonBindingsInterface::Get()->RemoveProject(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CopyProjectDialog(const QString& origPath, QWidget* parent)
|
bool CopyProjectDialog(const QString& origPath, ProjectInfo& newProjectInfo, QWidget* parent)
|
||||||
{
|
{
|
||||||
bool copyResult = false;
|
bool copyResult = false;
|
||||||
|
|
||||||
@@ -223,6 +307,8 @@ namespace O3DE::ProjectManager
|
|||||||
QFileDialog::getExistingDirectory(parent, QObject::tr("Select New Project Directory"), parentOrigDir.path()));
|
QFileDialog::getExistingDirectory(parent, QObject::tr("Select New Project Directory"), parentOrigDir.path()));
|
||||||
if (!newPath.isEmpty())
|
if (!newPath.isEmpty())
|
||||||
{
|
{
|
||||||
|
newProjectInfo.m_path = newPath;
|
||||||
|
|
||||||
if (!WarnDirectoryOverwrite(newPath, parent))
|
if (!WarnDirectoryOverwrite(newPath, parent))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -234,7 +320,7 @@ namespace O3DE::ProjectManager
|
|||||||
return copyResult;
|
return copyResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CopyProject(const QString& origPath, const QString& newPath, QWidget* parent)
|
bool CopyProject(const QString& origPath, const QString& newPath, QWidget* parent, bool skipRegister)
|
||||||
{
|
{
|
||||||
// Disallow copying from or into subdirectory
|
// Disallow copying from or into subdirectory
|
||||||
if (IsDirectoryDescedent(origPath, newPath) || IsDirectoryDescedent(newPath, origPath))
|
if (IsDirectoryDescedent(origPath, newPath) || IsDirectoryDescedent(newPath, origPath))
|
||||||
@@ -242,8 +328,13 @@ namespace O3DE::ProjectManager
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList filesToCopy;
|
int filesToCopyCount = 0;
|
||||||
qint64 totalSizeInBytes = 0;
|
qint64 totalSizeInBytes = 0;
|
||||||
|
QStringList skippedPaths
|
||||||
|
{
|
||||||
|
ProjectBuildDirectoryName,
|
||||||
|
ProjectCacheDirectoryName
|
||||||
|
};
|
||||||
|
|
||||||
QProgressDialog* progressDialog = new QProgressDialog(parent);
|
QProgressDialog* progressDialog = new QProgressDialog(parent);
|
||||||
progressDialog->setAutoClose(true);
|
progressDialog->setAutoClose(true);
|
||||||
@@ -254,7 +345,8 @@ namespace O3DE::ProjectManager
|
|||||||
progressDialog->show();
|
progressDialog->show();
|
||||||
|
|
||||||
QLocale locale;
|
QLocale locale;
|
||||||
RecursiveGetAllFiles(origPath, filesToCopy, totalSizeInBytes, [=](int fileCount, int sizeInBytes)
|
QStringList getFilesSkippedPaths(skippedPaths);
|
||||||
|
RecursiveGetAllFiles(origPath, getFilesSkippedPaths, filesToCopyCount, totalSizeInBytes, [=](int fileCount, int sizeInBytes)
|
||||||
{
|
{
|
||||||
// Create a human-readable version of the file size.
|
// Create a human-readable version of the file size.
|
||||||
const QString fileSizeString = locale.formattedDataSize(sizeInBytes);
|
const QString fileSizeString = locale.formattedDataSize(sizeInBytes);
|
||||||
@@ -273,8 +365,10 @@ namespace O3DE::ProjectManager
|
|||||||
|
|
||||||
// Phase 1: Copy files
|
// Phase 1: Copy files
|
||||||
bool showIgnoreFileDialog = true;
|
bool showIgnoreFileDialog = true;
|
||||||
bool success = CopyDirectory(progressDialog, origPath, newPath, filesToCopy, numFilesCopied, totalSizeInBytes, copiedFileSize, showIgnoreFileDialog);
|
QStringList copyFilesSkippedPaths(skippedPaths);
|
||||||
if (success)
|
bool success = CopyDirectory(progressDialog, origPath, newPath, copyFilesSkippedPaths, filesToCopyCount, numFilesCopied,
|
||||||
|
totalSizeInBytes, copiedFileSize, showIgnoreFileDialog);
|
||||||
|
if (success && !skipRegister)
|
||||||
{
|
{
|
||||||
// Phase 2: Register project
|
// Phase 2: Register project
|
||||||
success = RegisterProject(newPath);
|
success = RegisterProject(newPath);
|
||||||
@@ -297,7 +391,7 @@ namespace O3DE::ProjectManager
|
|||||||
QDir projectDirectory(path);
|
QDir projectDirectory(path);
|
||||||
if (projectDirectory.exists())
|
if (projectDirectory.exists())
|
||||||
{
|
{
|
||||||
// Check if there is an actual project hereor just force it
|
// Check if there is an actual project here or just force it
|
||||||
if (force || PythonBindingsInterface::Get()->GetProject(path).IsSuccess())
|
if (force || PythonBindingsInterface::Get()->GetProject(path).IsSuccess())
|
||||||
{
|
{
|
||||||
return projectDirectory.removeRecursively();
|
return projectDirectory.removeRecursively();
|
||||||
@@ -307,12 +401,12 @@ namespace O3DE::ProjectManager
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MoveProject(QString origPath, QString newPath, QWidget* parent, bool ignoreRegister)
|
bool MoveProject(QString origPath, QString newPath, QWidget* parent, bool skipRegister)
|
||||||
{
|
{
|
||||||
origPath = QDir::toNativeSeparators(origPath);
|
origPath = QDir::toNativeSeparators(origPath);
|
||||||
newPath = QDir::toNativeSeparators(newPath);
|
newPath = QDir::toNativeSeparators(newPath);
|
||||||
|
|
||||||
if (!WarnDirectoryOverwrite(newPath, parent) || (!ignoreRegister && !UnregisterProject(origPath)))
|
if (!WarnDirectoryOverwrite(newPath, parent) || (!skipRegister && !UnregisterProject(origPath)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -332,8 +426,13 @@ namespace O3DE::ProjectManager
|
|||||||
|
|
||||||
DeleteProjectFiles(origPath, true);
|
DeleteProjectFiles(origPath, true);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If directoy rename succeeded then build and cache directories need to be deleted seperately
|
||||||
|
ClearProjectBuildArtifactsAndCache(origPath, newPath, parent);
|
||||||
|
}
|
||||||
|
|
||||||
if (!ignoreRegister && !RegisterProject(newPath))
|
if (!skipRegister && !RegisterProject(newPath))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -374,46 +473,27 @@ namespace O3DE::ProjectManager
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsVS2019Installed_internal()
|
bool FindSupportedCompiler(QWidget* parent)
|
||||||
{
|
{
|
||||||
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
|
auto findCompilerResult = FindSupportedCompilerForPlatform();
|
||||||
QString programFilesPath = environment.value("ProgramFiles(x86)");
|
|
||||||
QString vsWherePath = programFilesPath + "\\Microsoft Visual Studio\\Installer\\vswhere.exe";
|
|
||||||
|
|
||||||
QFileInfo vsWhereFile(vsWherePath);
|
if (!findCompilerResult.IsSuccess())
|
||||||
if (vsWhereFile.exists() && vsWhereFile.isFile())
|
|
||||||
{
|
{
|
||||||
QProcess vsWhereProcess;
|
QMessageBox vsWarningMessage(parent);
|
||||||
vsWhereProcess.setProcessChannelMode(QProcess::MergedChannels);
|
vsWarningMessage.setIcon(QMessageBox::Warning);
|
||||||
|
vsWarningMessage.setWindowTitle(QObject::tr("Create Project"));
|
||||||
|
// Makes link clickable
|
||||||
|
vsWarningMessage.setTextFormat(Qt::RichText);
|
||||||
|
vsWarningMessage.setText(findCompilerResult.GetError());
|
||||||
|
vsWarningMessage.setStandardButtons(QMessageBox::Close);
|
||||||
|
|
||||||
vsWhereProcess.start(
|
QSpacerItem* horizontalSpacer = new QSpacerItem(600, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||||
vsWherePath,
|
QGridLayout* layout = reinterpret_cast<QGridLayout*>(vsWarningMessage.layout());
|
||||||
QStringList{ "-version", "16.0", "-latest", "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
|
layout->addItem(horizontalSpacer, layout->rowCount(), 0, 1, layout->columnCount());
|
||||||
"-property", "isComplete" });
|
vsWarningMessage.exec();
|
||||||
|
|
||||||
if (!vsWhereProcess.waitForStarted())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (vsWhereProcess.waitForReadyRead())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString vsWhereOutput(vsWhereProcess.readAllStandardOutput());
|
|
||||||
if (vsWhereOutput.startsWith("1"))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return findCompilerResult.IsSuccess();
|
||||||
}
|
|
||||||
|
|
||||||
bool IsVS2019Installed()
|
|
||||||
{
|
|
||||||
static bool vs2019Installed = IsVS2019Installed_internal();
|
|
||||||
return vs2019Installed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectManagerScreen GetProjectManagerScreen(const QString& screen)
|
ProjectManagerScreen GetProjectManagerScreen(const QString& screen)
|
||||||
|
|||||||
@@ -7,7 +7,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ScreenDefs.h>
|
#include <ScreenDefs.h>
|
||||||
|
#include <ProjectInfo.h>
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
#include <AzCore/Outcome/Outcome.h>
|
||||||
|
|
||||||
namespace O3DE::ProjectManager
|
namespace O3DE::ProjectManager
|
||||||
{
|
{
|
||||||
@@ -16,14 +19,15 @@ namespace O3DE::ProjectManager
|
|||||||
bool AddProjectDialog(QWidget* parent = nullptr);
|
bool AddProjectDialog(QWidget* parent = nullptr);
|
||||||
bool RegisterProject(const QString& path);
|
bool RegisterProject(const QString& path);
|
||||||
bool UnregisterProject(const QString& path);
|
bool UnregisterProject(const QString& path);
|
||||||
bool CopyProjectDialog(const QString& origPath, QWidget* parent = nullptr);
|
bool CopyProjectDialog(const QString& origPath, ProjectInfo& newProjectInfo, QWidget* parent = nullptr);
|
||||||
bool CopyProject(const QString& origPath, const QString& newPath, QWidget* parent);
|
bool CopyProject(const QString& origPath, const QString& newPath, QWidget* parent, bool skipRegister = false);
|
||||||
bool DeleteProjectFiles(const QString& path, bool force = false);
|
bool DeleteProjectFiles(const QString& path, bool force = false);
|
||||||
bool MoveProject(QString origPath, QString newPath, QWidget* parent = nullptr, bool ignoreRegister = false);
|
bool MoveProject(QString origPath, QString newPath, QWidget* parent, bool skipRegister = false);
|
||||||
|
|
||||||
bool ReplaceFile(const QString& origFile, const QString& newFile, QWidget* parent = nullptr, bool interactive = true);
|
bool ReplaceFile(const QString& origFile, const QString& newFile, QWidget* parent = nullptr, bool interactive = true);
|
||||||
|
|
||||||
bool IsVS2019Installed();
|
bool FindSupportedCompiler(QWidget* parent = nullptr);
|
||||||
|
AZ::Outcome<void, QString> FindSupportedCompilerForPlatform();
|
||||||
|
|
||||||
ProjectManagerScreen GetProjectManagerScreen(const QString& screen);
|
ProjectManagerScreen GetProjectManagerScreen(const QString& screen);
|
||||||
} // namespace ProjectUtils
|
} // namespace ProjectUtils
|
||||||
|
|||||||
@@ -384,14 +384,17 @@ namespace O3DE::ProjectManager
|
|||||||
emit ChangeScreenRequest(ProjectManagerScreen::UpdateProject);
|
emit ChangeScreenRequest(ProjectManagerScreen::UpdateProject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void ProjectsScreen::HandleCopyProject(const QString& projectPath)
|
void ProjectsScreen::HandleCopyProject(const ProjectInfo& projectInfo)
|
||||||
{
|
{
|
||||||
if (!WarnIfInBuildQueue(projectPath))
|
if (!WarnIfInBuildQueue(projectInfo.m_path))
|
||||||
{
|
{
|
||||||
|
ProjectInfo newProjectInfo(projectInfo);
|
||||||
|
|
||||||
// Open file dialog and choose location for copied project then register copy with O3DE
|
// Open file dialog and choose location for copied project then register copy with O3DE
|
||||||
if (ProjectUtils::CopyProjectDialog(projectPath, this))
|
if (ProjectUtils::CopyProjectDialog(projectInfo.m_path, newProjectInfo, this))
|
||||||
{
|
{
|
||||||
ResetProjectsContent();
|
ResetProjectsContent();
|
||||||
|
emit NotifyBuildProject(newProjectInfo);
|
||||||
emit ChangeScreenRequest(ProjectManagerScreen::Projects);
|
emit ChangeScreenRequest(ProjectManagerScreen::Projects);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -516,7 +519,7 @@ namespace O3DE::ProjectManager
|
|||||||
|
|
||||||
bool ProjectsScreen::StartProjectBuild(const ProjectInfo& projectInfo)
|
bool ProjectsScreen::StartProjectBuild(const ProjectInfo& projectInfo)
|
||||||
{
|
{
|
||||||
if (ProjectUtils::IsVS2019Installed())
|
if (ProjectUtils::FindSupportedCompiler(this))
|
||||||
{
|
{
|
||||||
QMessageBox::StandardButton buildProject = QMessageBox::information(
|
QMessageBox::StandardButton buildProject = QMessageBox::information(
|
||||||
this,
|
this,
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace O3DE::ProjectManager
|
|||||||
void HandleAddProjectButton();
|
void HandleAddProjectButton();
|
||||||
void HandleOpenProject(const QString& projectPath);
|
void HandleOpenProject(const QString& projectPath);
|
||||||
void HandleEditProject(const QString& projectPath);
|
void HandleEditProject(const QString& projectPath);
|
||||||
void HandleCopyProject(const QString& projectPath);
|
void HandleCopyProject(const ProjectInfo& projectInfo);
|
||||||
void HandleRemoveProject(const QString& projectPath);
|
void HandleRemoveProject(const QString& projectPath);
|
||||||
void HandleDeleteProject(const QString& projectPath);
|
void HandleDeleteProject(const QString& projectPath);
|
||||||
|
|
||||||
|
|||||||
@@ -219,11 +219,13 @@ namespace O3DE::ProjectManager
|
|||||||
// Move project first to avoid trying to update settings at the new location before it has been moved there
|
// Move project first to avoid trying to update settings at the new location before it has been moved there
|
||||||
if (newProjectSettings.m_path != m_projectInfo.m_path)
|
if (newProjectSettings.m_path != m_projectInfo.m_path)
|
||||||
{
|
{
|
||||||
if (!ProjectUtils::MoveProject(m_projectInfo.m_path, newProjectSettings.m_path))
|
if (!ProjectUtils::MoveProject(m_projectInfo.m_path, newProjectSettings.m_path, this))
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this, tr("Project move failed"), tr("Failed to move project."));
|
QMessageBox::critical(this, tr("Project move failed"), tr("Failed to move project."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit NotifyBuildProject(newProjectSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update project if settings changed
|
// Update project if settings changed
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <AzCore/UnitTest/TestTypes.h>
|
#include <AzCore/UnitTest/TestTypes.h>
|
||||||
#include <Application.h>
|
#include <Application.h>
|
||||||
#include <ProjectUtils.h>
|
#include <ProjectUtils.h>
|
||||||
|
#include <ProjectManagerDefs.h>
|
||||||
#include <ProjectManager_Test_Traits_Platform.h>
|
#include <ProjectManager_Test_Traits_Platform.h>
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
@@ -25,16 +26,31 @@ namespace O3DE::ProjectManager
|
|||||||
: public ::UnitTest::ScopedAllocatorSetupFixture
|
: public ::UnitTest::ScopedAllocatorSetupFixture
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static inline QString ReplaceFirstAWithB(const QString& originalString)
|
||||||
|
{
|
||||||
|
QString bString(originalString);
|
||||||
|
return bString.replace(bString.indexOf('A'), 1, 'B');
|
||||||
|
}
|
||||||
|
|
||||||
ProjectManagerUtilsTests()
|
ProjectManagerUtilsTests()
|
||||||
{
|
{
|
||||||
m_application = AZStd::make_unique<ProjectManager::Application>();
|
m_application = AZStd::make_unique<ProjectManager::Application>();
|
||||||
m_application->Init(false);
|
m_application->Init(false);
|
||||||
|
|
||||||
QDir dir;
|
m_projectAPath = "ProjectA";
|
||||||
dir.mkdir("ProjectA");
|
|
||||||
dir.mkdir("ProjectB");
|
|
||||||
|
|
||||||
QFile origFile("ProjectA/origFile.txt");
|
// Replaces first 'A' with 'B'
|
||||||
|
m_projectBPath = ReplaceFirstAWithB(m_projectAPath);
|
||||||
|
m_projectABuildPath = QString("%1%2%3").arg(m_projectAPath, QDir::separator(), ProjectBuildDirectoryName);
|
||||||
|
m_projectBBuildPath = ReplaceFirstAWithB(m_projectABuildPath);
|
||||||
|
|
||||||
|
QDir dir;
|
||||||
|
dir.mkpath(m_projectABuildPath);
|
||||||
|
dir.mkdir(m_projectBPath);
|
||||||
|
|
||||||
|
m_projectAOrigFilePath = QString("%1%2%3").arg(m_projectAPath, QDir::separator(), "origFile.txt");
|
||||||
|
m_projectBOrigFilePath = ReplaceFirstAWithB(m_projectAOrigFilePath);
|
||||||
|
QFile origFile(m_projectAOrigFilePath);
|
||||||
if (origFile.open(QIODevice::ReadWrite))
|
if (origFile.open(QIODevice::ReadWrite))
|
||||||
{
|
{
|
||||||
QTextStream stream(&origFile);
|
QTextStream stream(&origFile);
|
||||||
@@ -42,63 +58,153 @@ namespace O3DE::ProjectManager
|
|||||||
origFile.close();
|
origFile.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
QFile replaceFile("ProjectA/replaceFile.txt");
|
m_projectAReplaceFilePath = QString("%1%2%3").arg(m_projectAPath, QDir::separator(), "replaceFile.txt");
|
||||||
|
m_projectBReplaceFilePath = ReplaceFirstAWithB(m_projectAReplaceFilePath);
|
||||||
|
QFile replaceFile(m_projectAReplaceFilePath);
|
||||||
if (replaceFile.open(QIODevice::ReadWrite))
|
if (replaceFile.open(QIODevice::ReadWrite))
|
||||||
{
|
{
|
||||||
QTextStream stream(&replaceFile);
|
QTextStream stream(&replaceFile);
|
||||||
stream << "replace" << Qt::endl;
|
stream << "replace" << Qt::endl;
|
||||||
replaceFile.close();
|
replaceFile.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_projectABuildFilePath = QString("%1%2%3").arg(m_projectABuildPath, QDir::separator(), "build.obj");
|
||||||
|
m_projectBBuildFilePath = ReplaceFirstAWithB(m_projectABuildFilePath);
|
||||||
|
QFile buildFile(m_projectABuildFilePath);
|
||||||
|
if (buildFile.open(QIODevice::ReadWrite))
|
||||||
|
{
|
||||||
|
QTextStream stream(&buildFile);
|
||||||
|
stream << "x0FFFFFFFF" << Qt::endl;
|
||||||
|
buildFile.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~ProjectManagerUtilsTests()
|
~ProjectManagerUtilsTests()
|
||||||
{
|
{
|
||||||
QDir dirA("ProjectA");
|
QDir dirA(m_projectAPath);
|
||||||
dirA.removeRecursively();
|
dirA.removeRecursively();
|
||||||
|
|
||||||
QDir dirB("ProjectB");
|
QDir dirB(m_projectBPath);
|
||||||
dirB.removeRecursively();
|
dirB.removeRecursively();
|
||||||
|
|
||||||
m_application.reset();
|
m_application.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
AZStd::unique_ptr<ProjectManager::Application> m_application;
|
AZStd::unique_ptr<ProjectManager::Application> m_application;
|
||||||
|
|
||||||
|
QString m_projectAPath;
|
||||||
|
QString m_projectAOrigFilePath;
|
||||||
|
QString m_projectAReplaceFilePath;
|
||||||
|
QString m_projectABuildPath;
|
||||||
|
QString m_projectABuildFilePath;
|
||||||
|
QString m_projectBPath;
|
||||||
|
QString m_projectBOrigFilePath;
|
||||||
|
QString m_projectBReplaceFilePath;
|
||||||
|
QString m_projectBBuildPath;
|
||||||
|
QString m_projectBBuildFilePath;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||||
TEST_F(ProjectManagerUtilsTests, DISABLED_MoveProject_Succeeds)
|
TEST_F(ProjectManagerUtilsTests, DISABLED_MoveProject_MovesExpectedFiles)
|
||||||
#else
|
#else
|
||||||
TEST_F(ProjectManagerUtilsTests, MoveProject_Succeeds)
|
TEST_F(ProjectManagerUtilsTests, MoveProject_MovesExpectedFiles)
|
||||||
#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||||
{
|
{
|
||||||
EXPECT_TRUE(MoveProject(
|
EXPECT_TRUE(MoveProject(
|
||||||
QDir::currentPath() + QDir::separator() + "ProjectA",
|
QDir::currentPath() + QDir::separator() + m_projectAPath,
|
||||||
QDir::currentPath() + QDir::separator() + "ProjectB",
|
QDir::currentPath() + QDir::separator() + m_projectBPath,
|
||||||
nullptr, true));
|
nullptr, true));
|
||||||
|
|
||||||
QFileInfo origFile("ProjectA/origFile.txt");
|
QFileInfo origFile(m_projectAOrigFilePath);
|
||||||
EXPECT_TRUE(!origFile.exists());
|
EXPECT_FALSE(origFile.exists());
|
||||||
|
|
||||||
QFileInfo replaceFile("ProjectA/replaceFile.txt");
|
QFileInfo replaceFile(m_projectAReplaceFilePath);
|
||||||
EXPECT_TRUE(!replaceFile.exists());
|
EXPECT_FALSE(replaceFile.exists());
|
||||||
|
|
||||||
QFileInfo origFileMoved("ProjectB/origFile.txt");
|
QFileInfo origFileMoved(m_projectBOrigFilePath);
|
||||||
EXPECT_TRUE(origFileMoved.exists() && origFileMoved.isFile());
|
EXPECT_TRUE(origFileMoved.exists() && origFileMoved.isFile());
|
||||||
|
|
||||||
QFileInfo replaceFileMoved("ProjectB/replaceFile.txt");
|
QFileInfo replaceFileMoved(m_projectBReplaceFilePath);
|
||||||
EXPECT_TRUE(replaceFileMoved.exists() && replaceFileMoved.isFile());
|
EXPECT_TRUE(replaceFileMoved.exists() && replaceFileMoved.isFile());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||||
|
TEST_F(ProjectManagerUtilsTests, DISABLED_MoveProject_DoesntMoveBuild)
|
||||||
|
#else
|
||||||
|
TEST_F(ProjectManagerUtilsTests, MoveProject_DoesntMoveBuild)
|
||||||
|
#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||||
|
{
|
||||||
|
EXPECT_TRUE(MoveProject(
|
||||||
|
QDir::currentPath() + QDir::separator() + m_projectAPath,
|
||||||
|
QDir::currentPath() + QDir::separator() + m_projectBPath,
|
||||||
|
nullptr, true));
|
||||||
|
|
||||||
|
QFileInfo origFile(m_projectAOrigFilePath);
|
||||||
|
EXPECT_FALSE(origFile.exists());
|
||||||
|
|
||||||
|
QFileInfo origFileMoved(m_projectBOrigFilePath);
|
||||||
|
EXPECT_TRUE(origFileMoved.exists() && origFileMoved.isFile());
|
||||||
|
|
||||||
|
QDir buildDir(m_projectBBuildPath);
|
||||||
|
EXPECT_FALSE(buildDir.exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||||
|
TEST_F(ProjectManagerUtilsTests, DISABLED_CopyProject_CopiesExpectedFiles)
|
||||||
|
#else
|
||||||
|
TEST_F(ProjectManagerUtilsTests, CopyProject_CopiesExpectedFiles)
|
||||||
|
#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||||
|
{
|
||||||
|
EXPECT_TRUE(CopyProject(
|
||||||
|
QDir::currentPath() + QDir::separator() + m_projectAPath,
|
||||||
|
QDir::currentPath() + QDir::separator() + m_projectBPath,
|
||||||
|
nullptr, true));
|
||||||
|
|
||||||
|
QFileInfo origFile(m_projectAOrigFilePath);
|
||||||
|
EXPECT_TRUE(origFile.exists());
|
||||||
|
|
||||||
|
QFileInfo replaceFile(m_projectAReplaceFilePath);
|
||||||
|
EXPECT_TRUE(replaceFile.exists());
|
||||||
|
|
||||||
|
QFileInfo origFileMoved(m_projectBOrigFilePath);
|
||||||
|
EXPECT_TRUE(origFileMoved.exists() && origFileMoved.isFile());
|
||||||
|
|
||||||
|
QFileInfo replaceFileMoved(m_projectBReplaceFilePath);
|
||||||
|
EXPECT_TRUE(replaceFileMoved.exists() && replaceFileMoved.isFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||||
|
TEST_F(ProjectManagerUtilsTests, DISABLED_CopyProject_DoesntCopyBuild)
|
||||||
|
#else
|
||||||
|
TEST_F(ProjectManagerUtilsTests, CopyProject_DoesntCopyBuild)
|
||||||
|
#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||||
|
{
|
||||||
|
EXPECT_TRUE(CopyProject(
|
||||||
|
QDir::currentPath() + QDir::separator() + m_projectAPath,
|
||||||
|
QDir::currentPath() + QDir::separator() + m_projectBPath,
|
||||||
|
nullptr, true));
|
||||||
|
|
||||||
|
QFileInfo origFile(m_projectAOrigFilePath);
|
||||||
|
EXPECT_TRUE(origFile.exists());
|
||||||
|
|
||||||
|
QFileInfo origFileMoved(m_projectBOrigFilePath);
|
||||||
|
EXPECT_TRUE(origFileMoved.exists() && origFileMoved.isFile());
|
||||||
|
|
||||||
|
QDir buildDir(m_projectBBuildPath);
|
||||||
|
EXPECT_FALSE(buildDir.exists());
|
||||||
|
}
|
||||||
|
|
||||||
#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||||
TEST_F(ProjectManagerUtilsTests, DISABLED_ReplaceFile_Succeeds)
|
TEST_F(ProjectManagerUtilsTests, DISABLED_ReplaceFile_Succeeds)
|
||||||
#else
|
#else
|
||||||
TEST_F(ProjectManagerUtilsTests, ReplaceFile_Succeeds)
|
TEST_F(ProjectManagerUtilsTests, ReplaceFile_Succeeds)
|
||||||
#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||||
{
|
{
|
||||||
EXPECT_TRUE(ReplaceFile("ProjectA/origFile.txt", "ProjectA/replaceFile.txt", nullptr, false));
|
EXPECT_TRUE(ReplaceFile(m_projectAOrigFilePath, m_projectAReplaceFilePath, nullptr, false));
|
||||||
|
|
||||||
QFile origFile("ProjectA/origFile.txt");
|
QFile origFile(m_projectAOrigFilePath);
|
||||||
if (origFile.open(QIODevice::ReadOnly))
|
EXPECT_TRUE(origFile.open(QIODevice::ReadOnly));
|
||||||
{
|
{
|
||||||
QTextStream stream(&origFile);
|
QTextStream stream(&origFile);
|
||||||
QString line = stream.readLine();
|
QString line = stream.readLine();
|
||||||
@@ -106,10 +212,6 @@ namespace O3DE::ProjectManager
|
|||||||
|
|
||||||
origFile.close();
|
origFile.close();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
FAIL();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} // namespace ProjectUtils
|
} // namespace ProjectUtils
|
||||||
} // namespace O3DE::ProjectManager
|
} // namespace O3DE::ProjectManager
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"type": "Code",
|
"type": "Code",
|
||||||
"summary": "The Asset Memory Analyzer Gem provides tools to profile asset memory usage in Open 3D Engine through ImGUI (Immediate Mode Graphical User Interface).",
|
"summary": "The Asset Memory Analyzer Gem provides tools to profile asset memory usage in Open 3D Engine through ImGUI (Immediate Mode Graphical User Interface).",
|
||||||
"canonical_tags": ["Gem"],
|
"canonical_tags": ["Gem"],
|
||||||
"user_tags": ["Debug", "Utillity", "Tools"],
|
"user_tags": ["Debug", "Utility", "Tools"],
|
||||||
"icon_path": "preview.png",
|
"icon_path": "preview.png",
|
||||||
"requirements": ""
|
"requirements": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,3 @@ add_subdirectory(RPI)
|
|||||||
add_subdirectory(Tools)
|
add_subdirectory(Tools)
|
||||||
add_subdirectory(Utils)
|
add_subdirectory(Utils)
|
||||||
|
|
||||||
# The "Atom" Gem will alias the real Atom_AtomBridge target variants
|
|
||||||
# allows the enabling and disabling the "Atom" Gem to build the pre-requisite dependencies
|
|
||||||
ly_create_alias(NAME Atom.Clients NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Clients)
|
|
||||||
ly_create_alias(NAME Atom.Servers NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Servers)
|
|
||||||
if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
|
||||||
ly_create_alias(NAME Atom.Builders NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Builders)
|
|
||||||
ly_create_alias(NAME Atom.Tools NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Tools)
|
|
||||||
endif()
|
|
||||||
|
|||||||
@@ -6,3 +6,7 @@
|
|||||||
#
|
#
|
||||||
add_subdirectory(ReferenceMaterials)
|
add_subdirectory(ReferenceMaterials)
|
||||||
add_subdirectory(Sponza)
|
add_subdirectory(Sponza)
|
||||||
|
|
||||||
|
if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||||
|
ly_create_alias(NAME AtomContent.Builders NAMESPACE Gem TARGETS Gem::AtomContent_ReferenceMaterials.Builders Gem::AtomContent_Sponza.Builders)
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -115,3 +115,18 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
|||||||
ly_create_alias(NAME Atom_AtomBridge.Builders NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Editor)
|
ly_create_alias(NAME Atom_AtomBridge.Builders NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Editor)
|
||||||
ly_create_alias(NAME Atom_AtomBridge.Tools NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Editor)
|
ly_create_alias(NAME Atom_AtomBridge.Tools NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Editor)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# The "Atom" Gem will alias the real Atom_AtomBridge target variants
|
||||||
|
# allows the enabling and disabling the "Atom" Gem to build the pre-requisite dependencies
|
||||||
|
# The "AtomLyIntegration" Gem will also alias the real Atom_AtomBridge target variants
|
||||||
|
# The Atom Gem does the same at the moment.
|
||||||
|
ly_create_alias(NAME Atom.Clients NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Clients)
|
||||||
|
ly_create_alias(NAME Atom.Servers NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Servers)
|
||||||
|
ly_create_alias(NAME AtomLyIntegration.Clients NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Clients)
|
||||||
|
ly_create_alias(NAME AtomLyIntegration.Servers NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Servers)
|
||||||
|
if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||||
|
ly_create_alias(NAME Atom.Builders NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Builders)
|
||||||
|
ly_create_alias(NAME Atom.Tools NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Tools)
|
||||||
|
ly_create_alias(NAME AtomLyIntegration.Builders NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Builders)
|
||||||
|
ly_create_alias(NAME AtomLyIntegration.Tools NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Tools)
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -15,11 +15,3 @@ add_subdirectory(AtomBridge)
|
|||||||
add_subdirectory(AtomViewportDisplayInfo)
|
add_subdirectory(AtomViewportDisplayInfo)
|
||||||
add_subdirectory(AtomViewportDisplayIcons)
|
add_subdirectory(AtomViewportDisplayIcons)
|
||||||
|
|
||||||
# The "AtomLyIntegration" Gem will also alias the real Atom_AtomBridge target variants
|
|
||||||
# The Atom Gem does the same at the moment.
|
|
||||||
ly_create_alias(NAME AtomLyIntegration.Clients NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Clients)
|
|
||||||
ly_create_alias(NAME AtomLyIntegration.Servers NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Servers)
|
|
||||||
if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
|
||||||
ly_create_alias(NAME AtomLyIntegration.Builders NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Builders)
|
|
||||||
ly_create_alias(NAME AtomLyIntegration.Tools NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Tools)
|
|
||||||
endif()
|
|
||||||
|
|||||||
@@ -57,5 +57,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
|||||||
PRIVATE
|
PRIVATE
|
||||||
AZ::AzCore
|
AZ::AzCore
|
||||||
Gem::EMotionFX_Atom.Static
|
Gem::EMotionFX_Atom.Static
|
||||||
|
RUNTIME_DEPENDENCIES
|
||||||
|
Gem::EMotionFX.Editor
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"type": "Code",
|
"type": "Code",
|
||||||
"summary": "The Wwise Audio Engine Gem provides support for Audiokinetic Wave Works Interactive Sound Engine (Wwise).",
|
"summary": "The Wwise Audio Engine Gem provides support for Audiokinetic Wave Works Interactive Sound Engine (Wwise).",
|
||||||
"canonical_tags": ["Gem"],
|
"canonical_tags": ["Gem"],
|
||||||
"user_tags": ["Audio", "Utiltity", "Tools"],
|
"user_tags": ["Audio", "Utility", "Tools"],
|
||||||
"icon_path": "preview.png",
|
"icon_path": "preview.png",
|
||||||
"requirements": "Users will need to download WWise from the AudioKinetic web site: https://www.audiokinetic.com/download/"
|
"requirements": "Users will need to download WWise from the AudioKinetic web site: https://www.audiokinetic.com/download/"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"type": "Code",
|
"type": "Code",
|
||||||
"summary": "The Audio System Gem provides the Audio Translation Layer (ATL) and Audio Controls Editor, which add support for audio in Open 3D Engine.",
|
"summary": "The Audio System Gem provides the Audio Translation Layer (ATL) and Audio Controls Editor, which add support for audio in Open 3D Engine.",
|
||||||
"canonical_tags": ["Gem"],
|
"canonical_tags": ["Gem"],
|
||||||
"user_tags": ["Audio", "Utiltity", "Tools"],
|
"user_tags": ["Audio", "Utility", "Tools"],
|
||||||
"icon_path": "preview.png",
|
"icon_path": "preview.png",
|
||||||
"requirements": ""
|
"requirements": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ namespace EMotionFX
|
|||||||
|
|
||||||
Group::ActorGroup* group = azrtti_cast<Group::ActorGroup*>(&target);
|
Group::ActorGroup* group = azrtti_cast<Group::ActorGroup*>(&target);
|
||||||
group->SetName(AZ::SceneAPI::DataTypes::Utilities::CreateUniqueName<Group::IActorGroup>(scene.GetName(), scene.GetManifest()));
|
group->SetName(AZ::SceneAPI::DataTypes::Utilities::CreateUniqueName<Group::IActorGroup>(scene.GetName(), scene.GetManifest()));
|
||||||
|
group->SetBestMatchingRootBone(scene.GetGraph());
|
||||||
|
|
||||||
// LOD Rule need to be built first in the actor, so we know which mesh and bone belongs to LOD.
|
// LOD Rule need to be built first in the actor, so we know which mesh and bone belongs to LOD.
|
||||||
// After this call, LOD rule will be populated with all the LOD bones
|
// After this call, LOD rule will be populated with all the LOD bones
|
||||||
|
|||||||
@@ -9,10 +9,14 @@
|
|||||||
#include <AzCore/Memory/SystemAllocator.h>
|
#include <AzCore/Memory/SystemAllocator.h>
|
||||||
#include <AzCore/Serialization/SerializeContext.h>
|
#include <AzCore/Serialization/SerializeContext.h>
|
||||||
#include <AzCore/Serialization/EditContext.h>
|
#include <AzCore/Serialization/EditContext.h>
|
||||||
|
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
|
||||||
|
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
|
||||||
|
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphDownwardsIterator.h>
|
||||||
#include <SceneAPI/SceneCore/DataTypes/Rules/IRule.h>
|
#include <SceneAPI/SceneCore/DataTypes/Rules/IRule.h>
|
||||||
#include <SceneAPI/SceneCore/DataTypes/GraphData/IBoneData.h>
|
#include <SceneAPI/SceneCore/DataTypes/GraphData/IBoneData.h>
|
||||||
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
|
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
|
||||||
#include <SceneAPI/SceneCore/DataTypes/Groups/ISceneNodeGroup.h>
|
#include <SceneAPI/SceneCore/DataTypes/Groups/ISceneNodeGroup.h>
|
||||||
|
#include <SceneAPI/SceneData/GraphData/RootBoneData.h>
|
||||||
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
|
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
|
||||||
#include <SceneAPI/SceneData/Rules/CoordinateSystemRule.h>
|
#include <SceneAPI/SceneData/Rules/CoordinateSystemRule.h>
|
||||||
#include <SceneAPIExt/Groups/ActorGroup.h>
|
#include <SceneAPIExt/Groups/ActorGroup.h>
|
||||||
@@ -76,6 +80,22 @@ namespace EMotionFX
|
|||||||
m_selectedRootBone = selectedRootBone;
|
m_selectedRootBone = selectedRootBone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ActorGroup::SetBestMatchingRootBone(const AZ::SceneAPI::Containers::SceneGraph& sceneGraph)
|
||||||
|
{
|
||||||
|
auto nameContentView = AZ::SceneAPI::Containers::Views::MakePairView(sceneGraph.GetNameStorage(), sceneGraph.GetContentStorage());
|
||||||
|
auto graphDownwardsView = AZ::SceneAPI::Containers::Views::MakeSceneGraphDownwardsView<AZ::SceneAPI::Containers::Views::BreadthFirst>(
|
||||||
|
sceneGraph, sceneGraph.GetRoot(), nameContentView.begin(), true);
|
||||||
|
|
||||||
|
for (auto it = graphDownwardsView.begin(); it != graphDownwardsView.end(); ++it)
|
||||||
|
{
|
||||||
|
if (it->second && it->second->RTTI_IsTypeOf(AZ::SceneData::GraphData::RootBoneData::TYPEINFO_Uuid()))
|
||||||
|
{
|
||||||
|
SetSelectedRootBone(it->first.GetPath());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ActorGroup::Reflect(AZ::ReflectContext* context)
|
void ActorGroup::Reflect(AZ::ReflectContext* context)
|
||||||
{
|
{
|
||||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ namespace EMotionFX
|
|||||||
|
|
||||||
// IActorGroup overrides
|
// IActorGroup overrides
|
||||||
const AZStd::string& GetSelectedRootBone() const override;
|
const AZStd::string& GetSelectedRootBone() const override;
|
||||||
|
|
||||||
void SetSelectedRootBone(const AZStd::string& selectedRootBone) override;
|
void SetSelectedRootBone(const AZStd::string& selectedRootBone) override;
|
||||||
|
void SetBestMatchingRootBone(const AZ::SceneAPI::Containers::SceneGraph& sceneGraph) override;
|
||||||
|
|
||||||
static void Reflect(AZ::ReflectContext* context);
|
static void Reflect(AZ::ReflectContext* context);
|
||||||
static bool IActorGroupVersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement);
|
static bool IActorGroupVersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement);
|
||||||
|
|||||||
@@ -10,6 +10,11 @@
|
|||||||
#include <AzCore/RTTI/RTTI.h>
|
#include <AzCore/RTTI/RTTI.h>
|
||||||
#include <SceneAPI/SceneCore/DataTypes/Groups/IGroup.h>
|
#include <SceneAPI/SceneCore/DataTypes/Groups/IGroup.h>
|
||||||
|
|
||||||
|
namespace AZ::SceneAPI::Containers
|
||||||
|
{
|
||||||
|
class SceneGraph;
|
||||||
|
}
|
||||||
|
|
||||||
namespace EMotionFX
|
namespace EMotionFX
|
||||||
{
|
{
|
||||||
namespace Pipeline
|
namespace Pipeline
|
||||||
@@ -26,6 +31,7 @@ namespace EMotionFX
|
|||||||
|
|
||||||
virtual const AZStd::string& GetSelectedRootBone() const = 0;
|
virtual const AZStd::string& GetSelectedRootBone() const = 0;
|
||||||
virtual void SetSelectedRootBone(const AZStd::string& selectedRootBone) = 0;
|
virtual void SetSelectedRootBone(const AZStd::string& selectedRootBone) = 0;
|
||||||
|
virtual void SetBestMatchingRootBone(const AZ::SceneAPI::Containers::SceneGraph& sceneGraph) = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -412,25 +412,24 @@ namespace EMotionFX
|
|||||||
|
|
||||||
void NonUniformMotionData::UpdateDuration()
|
void NonUniformMotionData::UpdateDuration()
|
||||||
{
|
{
|
||||||
|
m_duration = 0.0f;
|
||||||
|
|
||||||
for (const JointData& jointData : m_jointData)
|
for (const JointData& jointData : m_jointData)
|
||||||
{
|
{
|
||||||
if (!jointData.m_positionTrack.m_times.empty())
|
if (!jointData.m_positionTrack.m_times.empty())
|
||||||
{
|
{
|
||||||
m_duration = jointData.m_positionTrack.m_times.back();
|
m_duration = AZ::GetMax(m_duration, jointData.m_positionTrack.m_times.back());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!jointData.m_rotationTrack.m_times.empty())
|
if (!jointData.m_rotationTrack.m_times.empty())
|
||||||
{
|
{
|
||||||
m_duration = jointData.m_rotationTrack.m_times.back();
|
m_duration = AZ::GetMax(m_duration, jointData.m_rotationTrack.m_times.back());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef EMFX_SCALE_DISABLED
|
#ifndef EMFX_SCALE_DISABLED
|
||||||
if (!jointData.m_scaleTrack.m_times.empty())
|
if (!jointData.m_scaleTrack.m_times.empty())
|
||||||
{
|
{
|
||||||
m_duration = jointData.m_scaleTrack.m_times.back();
|
m_duration = AZ::GetMax(m_duration, jointData.m_scaleTrack.m_times.back());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -439,8 +438,7 @@ namespace EMotionFX
|
|||||||
{
|
{
|
||||||
if (!morphData.m_track.m_times.empty())
|
if (!morphData.m_track.m_times.empty())
|
||||||
{
|
{
|
||||||
m_duration = morphData.m_track.m_times.back();
|
m_duration = AZ::GetMax(m_duration, morphData.m_track.m_times.back());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,12 +446,9 @@ namespace EMotionFX
|
|||||||
{
|
{
|
||||||
if (!floatData.m_track.m_times.empty())
|
if (!floatData.m_track.m_times.empty())
|
||||||
{
|
{
|
||||||
m_duration = floatData.m_track.m_times.back();
|
m_duration = AZ::GetMax(m_duration, floatData.m_track.m_times.back());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_duration = 0.0f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NonUniformMotionData::AllocateJointPositionSamples(size_t jointDataIndex, size_t numSamples)
|
void NonUniformMotionData::AllocateJointPositionSamples(size_t jointDataIndex, size_t numSamples)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"type": "Code",
|
"type": "Code",
|
||||||
"summary": "The Expression Evaluation Gem provides a method for parsing and executing string expressions in Open 3D Engine.",
|
"summary": "The Expression Evaluation Gem provides a method for parsing and executing string expressions in Open 3D Engine.",
|
||||||
"canonical_tags": ["Gem"],
|
"canonical_tags": ["Gem"],
|
||||||
"user_tags": ["Scripting", "Utiltity"],
|
"user_tags": ["Scripting", "Utility"],
|
||||||
"icon_path": "preview.png",
|
"icon_path": "preview.png",
|
||||||
"requirements": ""
|
"requirements": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"type": "Code",
|
"type": "Code",
|
||||||
"summary": "The Game State Samples Gem provides a set of sample game states (built on top of the Game State Gem), including primary user selection, main menu, level loading, level running, and level paused.",
|
"summary": "The Game State Samples Gem provides a set of sample game states (built on top of the Game State Gem), including primary user selection, main menu, level loading, level running, and level paused.",
|
||||||
"canonical_tags": ["Gem"],
|
"canonical_tags": ["Gem"],
|
||||||
"user_tags": ["Gameplay", "Samples", "Assets"],
|
"user_tags": ["Gameplay", "Sample", "Assets"],
|
||||||
"icon_path": "preview.png",
|
"icon_path": "preview.png",
|
||||||
"requirements": ""
|
"requirements": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,6 @@ namespace LyShine
|
|||||||
{
|
{
|
||||||
LyShineAllocatorScope::ActivateAllocators();
|
LyShineAllocatorScope::ActivateAllocators();
|
||||||
|
|
||||||
LyShineRequestBus::Handler::BusConnect();
|
|
||||||
UiSystemBus::Handler::BusConnect();
|
UiSystemBus::Handler::BusConnect();
|
||||||
UiSystemToolsBus::Handler::BusConnect();
|
UiSystemToolsBus::Handler::BusConnect();
|
||||||
UiFrameworkBus::Handler::BusConnect();
|
UiFrameworkBus::Handler::BusConnect();
|
||||||
@@ -196,7 +195,6 @@ namespace LyShine
|
|||||||
UiSystemBus::Handler::BusDisconnect();
|
UiSystemBus::Handler::BusDisconnect();
|
||||||
UiSystemToolsBus::Handler::BusDisconnect();
|
UiSystemToolsBus::Handler::BusDisconnect();
|
||||||
UiFrameworkBus::Handler::BusDisconnect();
|
UiFrameworkBus::Handler::BusDisconnect();
|
||||||
LyShineRequestBus::Handler::BusDisconnect();
|
|
||||||
CrySystemEventBus::Handler::BusDisconnect();
|
CrySystemEventBus::Handler::BusDisconnect();
|
||||||
|
|
||||||
LyShineAllocatorScope::DeactivateAllocators();
|
LyShineAllocatorScope::DeactivateAllocators();
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
#include <LmbrCentral/Rendering/MaterialAsset.h>
|
#include <LmbrCentral/Rendering/MaterialAsset.h>
|
||||||
|
|
||||||
#include <LyShine/LyShineBus.h>
|
|
||||||
#include <LyShine/Bus/UiSystemBus.h>
|
#include <LyShine/Bus/UiSystemBus.h>
|
||||||
#include <LyShine/Bus/UiCanvasManagerBus.h>
|
#include <LyShine/Bus/UiCanvasManagerBus.h>
|
||||||
#include <LyShine/Bus/Tools/UiSystemToolsBus.h>
|
#include <LyShine/Bus/Tools/UiSystemToolsBus.h>
|
||||||
@@ -28,7 +27,6 @@ namespace LyShine
|
|||||||
|
|
||||||
class LyShineSystemComponent
|
class LyShineSystemComponent
|
||||||
: public AZ::Component
|
: public AZ::Component
|
||||||
, protected LyShineRequestBus::Handler
|
|
||||||
, protected UiSystemBus::Handler
|
, protected UiSystemBus::Handler
|
||||||
, protected UiSystemToolsBus::Handler
|
, protected UiSystemToolsBus::Handler
|
||||||
, protected LyShineAllocatorScope
|
, protected LyShineAllocatorScope
|
||||||
|
|||||||
@@ -577,7 +577,7 @@ namespace NvCloth
|
|||||||
const AZ::Vector3& renderTangent = renderTangents[renderVertexIndex];
|
const AZ::Vector3& renderTangent = renderTangents[renderVertexIndex];
|
||||||
destTangentsBuffer[index].Set(
|
destTangentsBuffer[index].Set(
|
||||||
renderTangent,
|
renderTangent,
|
||||||
1.0f);
|
-1.0f); // Shader function ConstructTBN inverts w to change bitangent sign, but the bitangents passed are already corrected, so passing -1.0 to counteract.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (destBitangentsBuffer)
|
if (destBitangentsBuffer)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace NvCloth
|
|||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
const float Tolerance = 0.0001f;
|
const float Tolerance = 1e-7f;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TangentSpaceHelper::CalculateNormals(
|
bool TangentSpaceHelper::CalculateNormals(
|
||||||
@@ -33,7 +33,8 @@ namespace NvCloth
|
|||||||
const size_t vertexCount = vertices.size();
|
const size_t vertexCount = vertices.size();
|
||||||
|
|
||||||
// Reset results
|
// Reset results
|
||||||
outNormals.resize(vertexCount, AZ::Vector3::CreateZero());
|
outNormals.resize(vertexCount);
|
||||||
|
AZStd::fill(outNormals.begin(), outNormals.end(), AZ::Vector3::CreateZero());
|
||||||
|
|
||||||
// calculate the normals per triangle
|
// calculate the normals per triangle
|
||||||
for (size_t i = 0; i < triangleCount; ++i)
|
for (size_t i = 0; i < triangleCount; ++i)
|
||||||
@@ -114,8 +115,10 @@ namespace NvCloth
|
|||||||
const size_t vertexCount = vertices.size();
|
const size_t vertexCount = vertices.size();
|
||||||
|
|
||||||
// Reset results
|
// Reset results
|
||||||
outTangents.resize(vertexCount, AZ::Vector3::CreateZero());
|
outTangents.resize(vertexCount);
|
||||||
outBitangents.resize(vertexCount, AZ::Vector3::CreateZero());
|
outBitangents.resize(vertexCount);
|
||||||
|
AZStd::fill(outTangents.begin(), outTangents.end(), AZ::Vector3::CreateZero());
|
||||||
|
AZStd::fill(outBitangents.begin(), outBitangents.end(), AZ::Vector3::CreateZero());
|
||||||
|
|
||||||
// calculate the base vectors per triangle
|
// calculate the base vectors per triangle
|
||||||
for (size_t i = 0; i < triangleCount; ++i)
|
for (size_t i = 0; i < triangleCount; ++i)
|
||||||
@@ -192,9 +195,12 @@ namespace NvCloth
|
|||||||
const size_t vertexCount = vertices.size();
|
const size_t vertexCount = vertices.size();
|
||||||
|
|
||||||
// Reset results
|
// Reset results
|
||||||
outTangents.resize(vertexCount, AZ::Vector3::CreateZero());
|
outTangents.resize(vertexCount);
|
||||||
outBitangents.resize(vertexCount, AZ::Vector3::CreateZero());
|
outBitangents.resize(vertexCount);
|
||||||
outNormals.resize(vertexCount, AZ::Vector3::CreateZero());
|
outNormals.resize(vertexCount);
|
||||||
|
AZStd::fill(outTangents.begin(), outTangents.end(), AZ::Vector3::CreateZero());
|
||||||
|
AZStd::fill(outBitangents.begin(), outBitangents.end(), AZ::Vector3::CreateZero());
|
||||||
|
AZStd::fill(outNormals.begin(), outNormals.end(), AZ::Vector3::CreateZero());
|
||||||
|
|
||||||
// calculate the base vectors per triangle
|
// calculate the base vectors per triangle
|
||||||
for (size_t i = 0; i < triangleCount; ++i)
|
for (size_t i = 0; i < triangleCount; ++i)
|
||||||
|
|||||||
@@ -124,6 +124,9 @@ namespace UnitTest
|
|||||||
const AZStd::vector<AZ::Vector4>& motionConstraints = clothConstraints->GetMotionConstraints();
|
const AZStd::vector<AZ::Vector4>& motionConstraints = clothConstraints->GetMotionConstraints();
|
||||||
|
|
||||||
EXPECT_TRUE(motionConstraints.size() == SimulationParticles.size());
|
EXPECT_TRUE(motionConstraints.size() == SimulationParticles.size());
|
||||||
|
EXPECT_THAT(motionConstraints[0].GetAsVector3(), IsCloseTolerance(SimulationParticles[0].GetAsVector3(), Tolerance));
|
||||||
|
EXPECT_THAT(motionConstraints[1].GetAsVector3(), IsCloseTolerance(SimulationParticles[1].GetAsVector3(), Tolerance));
|
||||||
|
EXPECT_THAT(motionConstraints[2].GetAsVector3(), IsCloseTolerance(SimulationParticles[2].GetAsVector3(), Tolerance));
|
||||||
EXPECT_NEAR(motionConstraints[0].GetW(), 6.0f, Tolerance);
|
EXPECT_NEAR(motionConstraints[0].GetW(), 6.0f, Tolerance);
|
||||||
EXPECT_NEAR(motionConstraints[1].GetW(), 0.0f, Tolerance);
|
EXPECT_NEAR(motionConstraints[1].GetW(), 0.0f, Tolerance);
|
||||||
EXPECT_NEAR(motionConstraints[2].GetW(), 0.0f, Tolerance);
|
EXPECT_NEAR(motionConstraints[2].GetW(), 0.0f, Tolerance);
|
||||||
@@ -277,6 +280,9 @@ namespace UnitTest
|
|||||||
const AZStd::vector<AZ::Vector4>& separationConstraints = clothConstraints->GetSeparationConstraints();
|
const AZStd::vector<AZ::Vector4>& separationConstraints = clothConstraints->GetSeparationConstraints();
|
||||||
|
|
||||||
EXPECT_TRUE(motionConstraints.size() == newParticles.size());
|
EXPECT_TRUE(motionConstraints.size() == newParticles.size());
|
||||||
|
EXPECT_THAT(motionConstraints[0].GetAsVector3(), IsCloseTolerance(newParticles[0].GetAsVector3(), Tolerance));
|
||||||
|
EXPECT_THAT(motionConstraints[1].GetAsVector3(), IsCloseTolerance(newParticles[1].GetAsVector3(), Tolerance));
|
||||||
|
EXPECT_THAT(motionConstraints[2].GetAsVector3(), IsCloseTolerance(newParticles[2].GetAsVector3(), Tolerance));
|
||||||
EXPECT_NEAR(motionConstraints[0].GetW(), 3.0f, Tolerance);
|
EXPECT_NEAR(motionConstraints[0].GetW(), 3.0f, Tolerance);
|
||||||
EXPECT_NEAR(motionConstraints[1].GetW(), 1.5f, Tolerance);
|
EXPECT_NEAR(motionConstraints[1].GetW(), 1.5f, Tolerance);
|
||||||
EXPECT_NEAR(motionConstraints[2].GetW(), 0.0f, Tolerance);
|
EXPECT_NEAR(motionConstraints[2].GetW(), 0.0f, Tolerance);
|
||||||
@@ -285,8 +291,8 @@ namespace UnitTest
|
|||||||
EXPECT_NEAR(separationConstraints[0].GetW(), 3.0f, Tolerance);
|
EXPECT_NEAR(separationConstraints[0].GetW(), 3.0f, Tolerance);
|
||||||
EXPECT_NEAR(separationConstraints[1].GetW(), 1.5f, Tolerance);
|
EXPECT_NEAR(separationConstraints[1].GetW(), 1.5f, Tolerance);
|
||||||
EXPECT_NEAR(separationConstraints[2].GetW(), 0.3f, Tolerance);
|
EXPECT_NEAR(separationConstraints[2].GetW(), 0.3f, Tolerance);
|
||||||
EXPECT_THAT(separationConstraints[0].GetAsVector3(), IsCloseTolerance(AZ::Vector3(-3.03902f, 2.80752f, 3.80752f), Tolerance));
|
EXPECT_THAT(separationConstraints[0].GetAsVector3(), IsCloseTolerance(AZ::Vector3(0.0f, 3.53553f, 4.53553f), Tolerance));
|
||||||
EXPECT_THAT(separationConstraints[1].GetAsVector3(), IsCloseTolerance(AZ::Vector3(-1.41659f, 0.651243f, -0.348757f), Tolerance));
|
EXPECT_THAT(separationConstraints[1].GetAsVector3(), IsCloseTolerance(AZ::Vector3(0.0f, 2.06066f, 1.06066f), Tolerance));
|
||||||
EXPECT_THAT(separationConstraints[2].GetAsVector3(), IsCloseTolerance(AZ::Vector3(6.15313f, -0.876132f, 0.123868f), Tolerance));
|
EXPECT_THAT(separationConstraints[2].GetAsVector3(), IsCloseTolerance(AZ::Vector3(1.0f, -3.74767f, -2.74767f), Tolerance));
|
||||||
}
|
}
|
||||||
} // namespace UnitTest
|
} // namespace UnitTest
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"type": "Tool",
|
"type": "Tool",
|
||||||
"summary": "The Script Canvas Gem provides Open 3D Engine's visual scripting environment, Script Canvas.",
|
"summary": "The Script Canvas Gem provides Open 3D Engine's visual scripting environment, Script Canvas.",
|
||||||
"canonical_tags": ["Gem"],
|
"canonical_tags": ["Gem"],
|
||||||
"user_tags": ["Scripting", "Tools", "Utiltiy"],
|
"user_tags": ["Scripting", "Tools", "Utility"],
|
||||||
"icon_path": "preview.png",
|
"icon_path": "preview.png",
|
||||||
"requirements": ""
|
"requirements": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"type": "Code",
|
"type": "Code",
|
||||||
"summary": "The Surface Data Gem provides functionality to emit signals or tags from surfaces such as meshes and terrain.",
|
"summary": "The Surface Data Gem provides functionality to emit signals or tags from surfaces such as meshes and terrain.",
|
||||||
"canonical_tags": ["Gem"],
|
"canonical_tags": ["Gem"],
|
||||||
"user_tags": ["Environment", "Utiltiy", "Design"],
|
"user_tags": ["Environment", "Utility", "Design"],
|
||||||
"icon_path": "preview.png",
|
"icon_path": "preview.png",
|
||||||
"requirements": ""
|
"requirements": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -529,6 +529,12 @@ function(ly_force_download_package package_name)
|
|||||||
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf ${temp_download_target}
|
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf ${temp_download_target}
|
||||||
WORKING_DIRECTORY ${final_folder} COMMAND_ECHO STDOUT OUTPUT_VARIABLE unpack_result)
|
WORKING_DIRECTORY ${final_folder} COMMAND_ECHO STDOUT OUTPUT_VARIABLE unpack_result)
|
||||||
|
|
||||||
|
# For the runtime dependencies cases, we need the timestamps of the files coming from 3rdParty to be newer than the ones
|
||||||
|
# from the output so the new versions get copied over. The untar from the previous step preserves timestamps so they
|
||||||
|
# can produce binaries with older timestamps to the ones that are in the build output.
|
||||||
|
file(GLOB_RECURSE package_files LIST_DIRECTORIES false ${final_folder}/*)
|
||||||
|
file(TOUCH_NOCREATE ${package_files})
|
||||||
|
|
||||||
if (NOT ${unpack_result} EQUAL 0)
|
if (NOT ${unpack_result} EQUAL 0)
|
||||||
message(SEND_ERROR "ly_package: required package {package_name} could not be unpacked. Compile may fail! Enable LY_PACKAGE_DEBUG to debug.")
|
message(SEND_ERROR "ly_package: required package {package_name} could not be unpacked. Compile may fail! Enable LY_PACKAGE_DEBUG to debug.")
|
||||||
return()
|
return()
|
||||||
|
|||||||
@@ -124,3 +124,42 @@ function(ly_file_read path content)
|
|||||||
set(${content} ${file_content} PARENT_SCOPE)
|
set(${content} ${file_content} PARENT_SCOPE)
|
||||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${path})
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${path})
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
|
||||||
|
#! ly_get_last_path_segment_concat_sha256 : Concatenates the last path segment of the absolute path
|
||||||
|
# with the first 8 characters of the absolute path SHA256 hash to make a unique relative path segment
|
||||||
|
function(ly_get_last_path_segment_concat_sha256 absolute_path output_path)
|
||||||
|
string(SHA256 target_source_hash ${absolute_path})
|
||||||
|
string(SUBSTRING ${target_source_hash} 0 8 target_source_hash)
|
||||||
|
cmake_path(GET absolute_path FILENAME last_path_segment)
|
||||||
|
cmake_path(SET last_path_segment_sha256_path "${last_path_segment}-${target_source_hash}")
|
||||||
|
|
||||||
|
set(${output_path} ${last_path_segment_sha256_path} PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
#! ly_get_engine_relative_source_dir: Attempts to form a path relative to the BASE_DIRECTORY.
|
||||||
|
# If that fails the last path segment of the absolute_target_source_dir concatenated with a SHA256 hash to form a target directory
|
||||||
|
# \arg:BASE_DIRECTORY - Directory to base relative path against. Defaults to LY_ROOT_FOLDER
|
||||||
|
function(ly_get_engine_relative_source_dir absolute_target_source_dir output_source_dir)
|
||||||
|
|
||||||
|
set(options)
|
||||||
|
set(oneValueArgs BASE_DIRECTORY)
|
||||||
|
set(multiValueArgs)
|
||||||
|
cmake_parse_arguments(ly_get_engine_relative_source_dir "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
if(NOT ly_get_engine_relative_source_dir_BASE_DIRECTORY)
|
||||||
|
set(ly_get_engine_relative_source_dir_BASE_DIRECTORY ${LY_ROOT_FOLDER})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Get a relative target source directory to the LY root folder if possible
|
||||||
|
# Otherwise use the final component name
|
||||||
|
cmake_path(IS_PREFIX LY_ROOT_FOLDER ${absolute_target_source_dir} is_target_source_dir_subdirectory_of_engine)
|
||||||
|
if(is_target_source_dir_subdirectory_of_engine)
|
||||||
|
cmake_path(RELATIVE_PATH absolute_target_source_dir BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE relative_target_source_dir)
|
||||||
|
else()
|
||||||
|
ly_get_last_path_segment_concat_sha256(${absolute_target_source_dir} target_source_dir_last_path_segment)
|
||||||
|
unset(relative_target_source_dir)
|
||||||
|
cmake_path(APPEND relative_target_source_dir "External" ${target_source_dir_last_path_segment})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(${output_source_dir} ${relative_target_source_dir} PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|||||||
@@ -90,6 +90,13 @@ function(ly_create_alias)
|
|||||||
# Replace the CMake list separator with a space to replicate the space separated TARGETS arguments
|
# Replace the CMake list separator with a space to replicate the space separated TARGETS arguments
|
||||||
string(REPLACE ";" " " create_alias_args "${ly_create_alias_NAME},${ly_create_alias_NAMESPACE},${ly_create_alias_TARGETS}")
|
string(REPLACE ";" " " create_alias_args "${ly_create_alias_NAME},${ly_create_alias_NAMESPACE},${ly_create_alias_TARGETS}")
|
||||||
set_property(DIRECTORY APPEND PROPERTY LY_CREATE_ALIAS_ARGUMENTS "${create_alias_args}")
|
set_property(DIRECTORY APPEND PROPERTY LY_CREATE_ALIAS_ARGUMENTS "${create_alias_args}")
|
||||||
|
|
||||||
|
# Store the directory path in the GLOBAL property so that it can be accessed
|
||||||
|
# in the layout install logic. Skip if the directory has already been added
|
||||||
|
get_property(ly_all_target_directories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES)
|
||||||
|
if(NOT CMAKE_CURRENT_SOURCE_DIR IN_LIST ly_all_target_directories)
|
||||||
|
set_property(GLOBAL APPEND PROPERTY LY_ALL_TARGET_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
# ly_enable_gems
|
# ly_enable_gems
|
||||||
|
|||||||
+10
-5
@@ -391,10 +391,10 @@ function(ly_delayed_target_link_libraries)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(item_type STREQUAL MODULE_LIBRARY)
|
if(item_type STREQUAL MODULE_LIBRARY)
|
||||||
target_include_directories(${target} ${visibility} $<TARGET_PROPERTY:${item},INTERFACE_INCLUDE_DIRECTORIES>)
|
target_include_directories(${target} ${visibility} $<GENEX_EVAL:$<TARGET_PROPERTY:${item},INTERFACE_INCLUDE_DIRECTORIES>>)
|
||||||
target_link_libraries(${target} ${visibility} $<TARGET_PROPERTY:${item},INTERFACE_LINK_LIBRARIES>)
|
target_link_libraries(${target} ${visibility} $<GENEX_EVAL:$<TARGET_PROPERTY:${item},INTERFACE_LINK_LIBRARIES>>)
|
||||||
target_compile_definitions(${target} ${visibility} $<TARGET_PROPERTY:${item},INTERFACE_COMPILE_DEFINITIONS>)
|
target_compile_definitions(${target} ${visibility} $<GENEX_EVAL:$<TARGET_PROPERTY:${item},INTERFACE_COMPILE_DEFINITIONS>>)
|
||||||
target_compile_options(${target} ${visibility} $<TARGET_PROPERTY:${item},INTERFACE_COMPILE_OPTIONS>)
|
target_compile_options(${target} ${visibility} $<GENEX_EVAL:$<TARGET_PROPERTY:${item},INTERFACE_COMPILE_OPTIONS>>)
|
||||||
else()
|
else()
|
||||||
ly_parse_third_party_dependencies(${item})
|
ly_parse_third_party_dependencies(${item})
|
||||||
target_link_libraries(${target} ${visibility} ${item})
|
target_link_libraries(${target} ${visibility} ${item})
|
||||||
@@ -659,7 +659,12 @@ function(ly_get_vs_folder_directory absolute_target_source_dir output_source_dir
|
|||||||
if(is_target_prefix_of_engine_root)
|
if(is_target_prefix_of_engine_root)
|
||||||
cmake_path(RELATIVE_PATH absolute_target_source_dir BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE relative_target_source_dir)
|
cmake_path(RELATIVE_PATH absolute_target_source_dir BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE relative_target_source_dir)
|
||||||
else()
|
else()
|
||||||
cmake_path(GET absolute_target_source_dir RELATIVE_PART relative_target_source_dir)
|
cmake_path(IS_PREFIX CMAKE_SOURCE_DIR ${absolute_target_source_dir} is_target_prefix_of_source_dir)
|
||||||
|
if(is_target_prefix_of_source_dir)
|
||||||
|
cmake_path(RELATIVE_PATH absolute_target_source_dir BASE_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE relative_target_source_dir)
|
||||||
|
else()
|
||||||
|
cmake_path(GET absolute_target_source_dir RELATIVE_PART relative_target_source_dir)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(${output_source_dir} ${relative_target_source_dir} PARENT_SCOPE)
|
set(${output_source_dir} ${relative_target_source_dir} PARENT_SCOPE)
|
||||||
|
|||||||
+23
-28
@@ -125,37 +125,32 @@ install(FILES ${_cmake_package_dest}
|
|||||||
DESTINATION ./Tools/Redistributables/CMake
|
DESTINATION ./Tools/Redistributables/CMake
|
||||||
)
|
)
|
||||||
|
|
||||||
# temporary workaround for acquiring the 3rd party SPDX license manifest, the desired location is from
|
# the version string and git tags are intended to be synchronized so it should be safe to use that instead
|
||||||
# another git repository that's private. once it's public, only how the URL is formed should change
|
# of directly calling into git which could get messy in certain scenarios
|
||||||
set(LY_INSTALLER_3RD_PARTY_LICENSE_URL "" CACHE STRING "URL to the 3rd party SPDX license manifest file for inclusion in packaging.")
|
if(${CPACK_PACKAGE_VERSION} VERSION_GREATER "0.0.0.0")
|
||||||
|
set(_3rd_party_license_filename NOTICES.txt)
|
||||||
|
|
||||||
if(${LY_VERSION_STRING} VERSION_GREATER "0.0.0.0" AND NOT LY_INSTALLER_3RD_PARTY_LICENSE_URL)
|
set(_3rd_party_license_url "https://raw.githubusercontent.com/o3de/3p-package-source/${CPACK_PACKAGE_VERSION}/${_3rd_party_license_filename}")
|
||||||
message(FATAL_ERROR "Missing required URL for the 3rd party SPDX license manifest file. "
|
set(_3rd_party_license_dest ${CPACK_BINARY_DIR}/${_3rd_party_license_filename})
|
||||||
"Please specifiy where to acquire the file via LY_INSTALLER_3RD_PARTY_LICENSE_URL")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
string(REPLACE "/" ";" _url_components ${LY_INSTALLER_3RD_PARTY_LICENSE_URL})
|
# use the plain file downloader as we don't have the file hash available and using a dummy will
|
||||||
list(POP_BACK _url_components _3rd_party_license_filename)
|
# delete the file once it fails hash verification
|
||||||
|
file(DOWNLOAD
|
||||||
set(_3rd_party_license_dest ${CPACK_BINARY_DIR}/${_3rd_party_license_filename})
|
${_3rd_party_license_url}
|
||||||
|
${_3rd_party_license_dest}
|
||||||
# use the plain file downloader as we don't have the file hash available and using a dummy will
|
STATUS _status
|
||||||
# delete the file once it fails hash verification
|
TLS_VERIFY ON
|
||||||
file(DOWNLOAD
|
|
||||||
${LY_INSTALLER_3RD_PARTY_LICENSE_URL}
|
|
||||||
${_3rd_party_license_dest}
|
|
||||||
STATUS _status
|
|
||||||
TLS_VERIFY ON
|
|
||||||
)
|
|
||||||
list(POP_FRONT _status _status_code)
|
|
||||||
|
|
||||||
if (${_status_code} EQUAL 0 AND EXISTS ${_3rd_party_license_dest})
|
|
||||||
install(FILES ${_3rd_party_license_dest}
|
|
||||||
DESTINATION .
|
|
||||||
)
|
)
|
||||||
else()
|
list(POP_FRONT _status _status_code)
|
||||||
file(REMOVE ${_3rd_party_license_dest})
|
|
||||||
message(FATAL_ERROR "Failed to acquire the 3rd Party license manifest file. Error: ${_status}")
|
if (${_status_code} EQUAL 0 AND EXISTS ${_3rd_party_license_dest})
|
||||||
|
install(FILES ${_3rd_party_license_dest}
|
||||||
|
DESTINATION .
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
file(REMOVE ${_3rd_party_license_dest})
|
||||||
|
message(FATAL_ERROR "Failed to acquire the 3rd Party license manifest file at ${_3rd_party_license_url}. Error: ${_status}")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# checks for and removes trailing slash
|
# checks for and removes trailing slash
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<UseMultiToolTask>true</UseMultiToolTask>
|
<UseMultiToolTask>true</UseMultiToolTask>
|
||||||
<EnforceProcessCountAcrossBuilds>true</EnforceProcessCountAcrossBuilds>
|
<EnforceProcessCountAcrossBuilds>true</EnforceProcessCountAcrossBuilds>
|
||||||
|
@VCPKG_CONFIGURATION_MAPPING@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemDefinitionGroup>
|
<ItemDefinitionGroup>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
|
|||||||
@@ -5,8 +5,19 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
|
include(cmake/FileUtil.cmake)
|
||||||
|
|
||||||
set(CMAKE_INSTALL_MESSAGE NEVER) # Simplify messages to reduce output noise
|
set(CMAKE_INSTALL_MESSAGE NEVER) # Simplify messages to reduce output noise
|
||||||
|
|
||||||
|
define_property(TARGET PROPERTY LY_INSTALL_GENERATE_RUN_TARGET
|
||||||
|
BRIEF_DOCS "Defines if a \"RUN\" targets should be created when installing this target Gem"
|
||||||
|
FULL_DOCS [[
|
||||||
|
Property which is set on targets that should generate a "RUN"
|
||||||
|
target when installed. This \"RUN\" target helps to run the
|
||||||
|
binary from the installed location directly from the IDE.
|
||||||
|
]]
|
||||||
|
)
|
||||||
|
|
||||||
ly_set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME Core)
|
ly_set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME Core)
|
||||||
|
|
||||||
cmake_path(RELATIVE_PATH CMAKE_RUNTIME_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE runtime_output_directory)
|
cmake_path(RELATIVE_PATH CMAKE_RUNTIME_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE runtime_output_directory)
|
||||||
@@ -18,26 +29,6 @@ cmake_path(RELATIVE_PATH CMAKE_LIBRARY_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_B
|
|||||||
set(install_output_folder "\${CMAKE_INSTALL_PREFIX}/${runtime_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>")
|
set(install_output_folder "\${CMAKE_INSTALL_PREFIX}/${runtime_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>")
|
||||||
|
|
||||||
|
|
||||||
function(ly_get_engine_relative_source_dir absolute_target_source_dir output_source_dir)
|
|
||||||
# Get a relative target source directory to the LY root folder if possible
|
|
||||||
# Otherwise use the final component name
|
|
||||||
cmake_path(IS_PREFIX LY_ROOT_FOLDER ${absolute_target_source_dir} is_target_prefix_of_engine_root)
|
|
||||||
if(is_target_prefix_of_engine_root)
|
|
||||||
cmake_path(RELATIVE_PATH absolute_target_source_dir BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE relative_target_source_dir)
|
|
||||||
else()
|
|
||||||
# In this case the target source directory is outside of the engine root of the target source directory and concatenate the first
|
|
||||||
# is used first 8 characters of the absolute path SHA256 hash to make a unique relative directory
|
|
||||||
# that can be used to install the generated CMakeLists.txt
|
|
||||||
# of a SHA256 hash
|
|
||||||
string(SHA256 target_source_hash ${absolute_target_source_dir})
|
|
||||||
string(SUBSTRING ${target_source_hash} 0 8 target_source_hash)
|
|
||||||
cmake_path(GET absolute_target_source_dir FILENAME target_source_dirname)
|
|
||||||
cmake_path(SET relative_target_source_dir "${target_source_dirname}-${target_source_hash}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(${output_source_dir} ${relative_target_source_dir} PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
#! ly_setup_target: Setup the data needed to re-create the cmake target commands for a single target
|
#! ly_setup_target: Setup the data needed to re-create the cmake target commands for a single target
|
||||||
function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_target_source_dir)
|
function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_target_source_dir)
|
||||||
# De-alias target name
|
# De-alias target name
|
||||||
@@ -82,6 +73,7 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar
|
|||||||
PATTERN *.h
|
PATTERN *.h
|
||||||
PATTERN *.hpp
|
PATTERN *.hpp
|
||||||
PATTERN *.inl
|
PATTERN *.inl
|
||||||
|
PATTERN *.hxx
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
@@ -122,15 +114,19 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar
|
|||||||
set(NAMESPACE_PLACEHOLDER "")
|
set(NAMESPACE_PLACEHOLDER "")
|
||||||
set(NAME_PLACEHOLDER ${TARGET_NAME})
|
set(NAME_PLACEHOLDER ${TARGET_NAME})
|
||||||
endif()
|
endif()
|
||||||
|
get_target_property(should_create_helper ${TARGET_NAME} LY_INSTALL_GENERATE_RUN_TARGET)
|
||||||
|
if(should_create_helper)
|
||||||
|
set(NAME_PLACEHOLDER ${NAME_PLACEHOLDER}.Imported)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(TARGET_TYPE_PLACEHOLDER "")
|
set(TARGET_TYPE_PLACEHOLDER "")
|
||||||
get_target_property(target_type ${NAME_PLACEHOLDER} TYPE)
|
get_target_property(target_type ${TARGET_NAME} TYPE)
|
||||||
# Remove the _LIBRARY since we dont need to pass that to ly_add_targets
|
# Remove the _LIBRARY since we dont need to pass that to ly_add_targets
|
||||||
string(REPLACE "_LIBRARY" "" TARGET_TYPE_PLACEHOLDER ${target_type})
|
string(REPLACE "_LIBRARY" "" TARGET_TYPE_PLACEHOLDER ${target_type})
|
||||||
# For HEADER_ONLY libs we end up generating "INTERFACE" libraries, need to specify HEADERONLY instead
|
# For HEADER_ONLY libs we end up generating "INTERFACE" libraries, need to specify HEADERONLY instead
|
||||||
string(REPLACE "INTERFACE" "HEADERONLY" TARGET_TYPE_PLACEHOLDER ${TARGET_TYPE_PLACEHOLDER})
|
string(REPLACE "INTERFACE" "HEADERONLY" TARGET_TYPE_PLACEHOLDER ${TARGET_TYPE_PLACEHOLDER})
|
||||||
if(TARGET_TYPE_PLACEHOLDER STREQUAL "MODULE")
|
if(TARGET_TYPE_PLACEHOLDER STREQUAL "MODULE")
|
||||||
get_target_property(gem_module ${NAME_PLACEHOLDER} GEM_MODULE)
|
get_target_property(gem_module ${TARGET_NAME} GEM_MODULE)
|
||||||
if(gem_module)
|
if(gem_module)
|
||||||
set(TARGET_TYPE_PLACEHOLDER "GEM_MODULE")
|
set(TARGET_TYPE_PLACEHOLDER "GEM_MODULE")
|
||||||
endif()
|
endif()
|
||||||
@@ -163,7 +159,6 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar
|
|||||||
unset(RUNTIME_DEPENDENCIES_PLACEHOLDER)
|
unset(RUNTIME_DEPENDENCIES_PLACEHOLDER)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
get_target_property(inteface_build_dependencies_props ${TARGET_NAME} INTERFACE_LINK_LIBRARIES)
|
get_target_property(inteface_build_dependencies_props ${TARGET_NAME} INTERFACE_LINK_LIBRARIES)
|
||||||
unset(INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER)
|
unset(INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER)
|
||||||
if(inteface_build_dependencies_props)
|
if(inteface_build_dependencies_props)
|
||||||
@@ -187,6 +182,23 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar
|
|||||||
list(REMOVE_DUPLICATES INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER)
|
list(REMOVE_DUPLICATES INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER)
|
||||||
string(REPLACE ";" "\n" INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER "${INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER}")
|
string(REPLACE ";" "\n" INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER "${INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER}")
|
||||||
|
|
||||||
|
# If the target is an executable/application, add a custom target so we can debug the target in project-centric workflow
|
||||||
|
if(should_create_helper)
|
||||||
|
string(REPLACE ".Imported" "" RUN_TARGET_NAME ${NAME_PLACEHOLDER})
|
||||||
|
set(target_types_with_debugging_helper EXECUTABLE APPLICATION)
|
||||||
|
if(NOT target_type IN_LIST target_types_with_debugging_helper)
|
||||||
|
message(FATAL_ERROR "Cannot generate a RUN target for ${TARGET_NAME}, type is ${target_type}")
|
||||||
|
endif()
|
||||||
|
set(TARGET_RUN_HELPER
|
||||||
|
"add_custom_target(${RUN_TARGET_NAME})
|
||||||
|
set_target_properties(${RUN_TARGET_NAME} PROPERTIES
|
||||||
|
FOLDER \"CMakePredefinedTargets/SDK\"
|
||||||
|
VS_DEBUGGER_COMMAND \$<GENEX_EVAL:\$<TARGET_PROPERTY:${NAME_PLACEHOLDER},IMPORTED_LOCATION>>
|
||||||
|
VS_DEBUGGER_COMMAND_ARGUMENTS \"--project-path=\${LY_DEFAULT_PROJECT_PATH}\"
|
||||||
|
)"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Config file
|
# Config file
|
||||||
set(target_file_contents "# Generated by O3DE install\n\n")
|
set(target_file_contents "# Generated by O3DE install\n\n")
|
||||||
if(NOT target_type STREQUAL INTERFACE_LIBRARY)
|
if(NOT target_type STREQUAL INTERFACE_LIBRARY)
|
||||||
@@ -199,13 +211,13 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar
|
|||||||
set(target_location "\${LY_ROOT_FOLDER}/${library_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>/${target_library_output_subdirectory}/$<TARGET_FILE_NAME:${TARGET_NAME}>")
|
set(target_location "\${LY_ROOT_FOLDER}/${library_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>/${target_library_output_subdirectory}/$<TARGET_FILE_NAME:${TARGET_NAME}>")
|
||||||
elseif(target_type STREQUAL SHARED_LIBRARY)
|
elseif(target_type STREQUAL SHARED_LIBRARY)
|
||||||
string(APPEND target_file_contents
|
string(APPEND target_file_contents
|
||||||
"set_property(TARGET ${TARGET_NAME}
|
"set_property(TARGET ${NAME_PLACEHOLDER}
|
||||||
APPEND_STRING PROPERTY IMPORTED_IMPLIB
|
APPEND_STRING PROPERTY IMPORTED_IMPLIB
|
||||||
$<$<CONFIG:$<CONFIG>$<ANGLE-R>:\"\${LY_ROOT_FOLDER}/${archive_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>/$<TARGET_LINKER_FILE_NAME:${TARGET_NAME}>\"$<ANGLE-R>
|
$<$<CONFIG:$<CONFIG>$<ANGLE-R>:\"\${LY_ROOT_FOLDER}/${archive_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>/$<TARGET_LINKER_FILE_NAME:${TARGET_NAME}>\"$<ANGLE-R>
|
||||||
)
|
)
|
||||||
")
|
")
|
||||||
string(APPEND target_file_contents
|
string(APPEND target_file_contents
|
||||||
"set_property(TARGET ${TARGET_NAME}
|
"set_property(TARGET ${NAME_PLACEHOLDER}
|
||||||
PROPERTY IMPORTED_IMPLIB_$<UPPER_CASE:$<CONFIG>>
|
PROPERTY IMPORTED_IMPLIB_$<UPPER_CASE:$<CONFIG>>
|
||||||
\"\${LY_ROOT_FOLDER}/${archive_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>/$<TARGET_LINKER_FILE_NAME:${TARGET_NAME}>\"
|
\"\${LY_ROOT_FOLDER}/${archive_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>/$<TARGET_LINKER_FILE_NAME:${TARGET_NAME}>\"
|
||||||
)
|
)
|
||||||
@@ -217,11 +229,11 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar
|
|||||||
|
|
||||||
if(target_location)
|
if(target_location)
|
||||||
string(APPEND target_file_contents
|
string(APPEND target_file_contents
|
||||||
"set_property(TARGET ${TARGET_NAME}
|
"set_property(TARGET ${NAME_PLACEHOLDER}
|
||||||
APPEND_STRING PROPERTY IMPORTED_LOCATION
|
APPEND_STRING PROPERTY IMPORTED_LOCATION
|
||||||
$<$<CONFIG:$<CONFIG>$<ANGLE-R>:${target_location}$<ANGLE-R>
|
$<$<CONFIG:$<CONFIG>$<ANGLE-R>:${target_location}$<ANGLE-R>
|
||||||
)
|
)
|
||||||
set_property(TARGET ${TARGET_NAME}
|
set_property(TARGET ${NAME_PLACEHOLDER}
|
||||||
PROPERTY IMPORTED_LOCATION_$<UPPER_CASE:$<CONFIG>>
|
PROPERTY IMPORTED_LOCATION_$<UPPER_CASE:$<CONFIG>>
|
||||||
${target_location}
|
${target_location}
|
||||||
)
|
)
|
||||||
@@ -254,7 +266,6 @@ endfunction()
|
|||||||
|
|
||||||
#! ly_setup_subdirectory: setup all targets in the subdirectory
|
#! ly_setup_subdirectory: setup all targets in the subdirectory
|
||||||
function(ly_setup_subdirectory absolute_target_source_dir)
|
function(ly_setup_subdirectory absolute_target_source_dir)
|
||||||
# Get the target source directory relative to the LY roo folder
|
|
||||||
ly_get_engine_relative_source_dir(${absolute_target_source_dir} relative_target_source_dir)
|
ly_get_engine_relative_source_dir(${absolute_target_source_dir} relative_target_source_dir)
|
||||||
|
|
||||||
# The builtin BUILDSYSTEM_TARGETS property isn't being used here as that returns the de-alised
|
# The builtin BUILDSYSTEM_TARGETS property isn't being used here as that returns the de-alised
|
||||||
@@ -540,56 +551,74 @@ function(ly_setup_others)
|
|||||||
)
|
)
|
||||||
# Exclude transient artifacts that shouldn't be copied to the install layout
|
# Exclude transient artifacts that shouldn't be copied to the install layout
|
||||||
list(FILTER external_subdir_files EXCLUDE REGEX "/([Bb]uild|[Cc]ache|[Uu]ser)$")
|
list(FILTER external_subdir_files EXCLUDE REGEX "/([Bb]uild|[Cc]ache|[Uu]ser)$")
|
||||||
list(APPEND filtered_asset_paths ${external_subdir_files})
|
# Storing a "mapping" of gem candidate directories, to external_subdirectory files using
|
||||||
|
# a DIRECTORY property for the "value" and the GLOBAL property for the "key"
|
||||||
|
set_property(DIRECTORY ${gem_candidate_dir} APPEND PROPERTY directory_filtered_asset_paths "${external_subdir_files}")
|
||||||
|
set_property(GLOBAL APPEND PROPERTY global_gem_candidate_dirs_prop ${gem_candidate_dir})
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
# At this point the filtered_assets_paths contains the list of all directories and files
|
# Iterate over each gem candidate directories and read populate a directory property
|
||||||
# that are non-excluded candidates that can be scanned for target directories and files
|
# containing the files to copy over
|
||||||
# to copy over to the install layout
|
get_property(gem_candidate_dirs GLOBAL PROPERTY global_gem_candidate_dirs_prop)
|
||||||
foreach(filtered_asset_path IN LISTS filtered_asset_paths)
|
foreach(gem_candidate_dir IN LISTS gem_candidate_dirs)
|
||||||
if(IS_DIRECTORY ${filtered_asset_path})
|
get_property(filtered_asset_paths DIRECTORY ${gem_candidate_dir} PROPERTY directory_filtered_asset_paths)
|
||||||
file(GLOB_RECURSE
|
ly_get_last_path_segment_concat_sha256(${gem_candidate_dir} last_gem_root_path_segment)
|
||||||
recurse_assets_paths
|
# Check if the gem is a subdirectory of the engine
|
||||||
LIST_DIRECTORIES TRUE
|
cmake_path(IS_PREFIX LY_ROOT_FOLDER ${gem_candidate_dir} is_gem_subdirectory_of_engine)
|
||||||
"${filtered_asset_path}/*"
|
|
||||||
)
|
# At this point the filtered_assets_paths contains the list of all directories and files
|
||||||
set(gem_file_paths ${recurse_assets_paths})
|
# that are non-excluded candidates that can be scanned for target directories and files
|
||||||
# Make sure to prepend the current path iteration to the gem_dirs_path to filter
|
# to copy over to the install layout
|
||||||
set(gem_dir_paths ${filtered_asset_path} ${recurse_assets_paths})
|
foreach(filtered_asset_path IN LISTS filtered_asset_paths)
|
||||||
|
if(IS_DIRECTORY ${filtered_asset_path})
|
||||||
|
file(GLOB_RECURSE
|
||||||
|
recurse_assets_paths
|
||||||
|
LIST_DIRECTORIES TRUE
|
||||||
|
"${filtered_asset_path}/*"
|
||||||
|
)
|
||||||
|
set(gem_file_paths ${recurse_assets_paths})
|
||||||
|
# Make sure to prepend the current path iteration to the gem_dirs_path to filter
|
||||||
|
set(gem_dir_paths ${filtered_asset_path} ${recurse_assets_paths})
|
||||||
|
|
||||||
# Gather directories to copy over
|
# Gather directories to copy over
|
||||||
# Currently only the Assets, Registry and Config directories are copied over
|
# Currently only the Assets, Registry and Config directories are copied over
|
||||||
list(FILTER gem_dir_paths INCLUDE REGEX "/(Assets|Registry|Config)$")
|
list(FILTER gem_dir_paths INCLUDE REGEX "/(Assets|Registry|Config|Editor/Scripts)$")
|
||||||
list(APPEND gems_assets_dir_path ${gem_dir_paths})
|
set_property(DIRECTORY ${gem_candidate_dir} APPEND PROPERTY gems_assets_paths ${gem_dir_paths})
|
||||||
else()
|
else()
|
||||||
set(gem_file_paths ${filtered_asset_path})
|
set(gem_file_paths ${filtered_asset_path})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Gather files to copy over
|
# Gather files to copy over
|
||||||
# Currently only the gem.json file is copied over
|
# Currently only the gem.json file is copied over
|
||||||
list(FILTER gem_file_paths INCLUDE REGEX "/(gem.json)$")
|
list(FILTER gem_file_paths INCLUDE REGEX "/(gem.json|preview.png)$")
|
||||||
list(APPEND gems_assets_file_path "${gem_file_paths}")
|
set_property(DIRECTORY ${gem_candidate_dir} APPEND PROPERTY gems_assets_paths "${gem_file_paths}")
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
# gem directories to install
|
# gem directories and files to install
|
||||||
foreach(gem_absolute_dir_path ${gems_assets_dir_path})
|
get_property(gems_assets_paths DIRECTORY ${gem_candidate_dir} PROPERTY gems_assets_paths)
|
||||||
cmake_path(RELATIVE_PATH gem_absolute_dir_path BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE gem_relative_dir_path)
|
foreach(gem_absolute_path IN LISTS gems_assets_paths)
|
||||||
if (EXISTS ${gem_absolute_dir_path})
|
if(is_gem_subdirectory_of_engine)
|
||||||
# The trailing slash is IMPORTANT here as that is needed to prevent
|
cmake_path(RELATIVE_PATH gem_absolute_path BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE gem_install_dest_dir)
|
||||||
# the "Assets" folder from being copied underneath the <gem-root>/Assets folder
|
else()
|
||||||
install(DIRECTORY "${gem_absolute_dir_path}/"
|
# The gem resides outside of the LY_ROOT_FOLDER, so the destination is made relative to the
|
||||||
DESTINATION ${gem_relative_dir_path}
|
# gem candidate directory and placed under the "External" directory"
|
||||||
)
|
# directory
|
||||||
endif()
|
cmake_path(RELATIVE_PATH gem_absolute_path BASE_DIRECTORY ${gem_candidate_dir} OUTPUT_VARIABLE gem_relative_path)
|
||||||
endforeach()
|
unset(gem_install_dest_dir)
|
||||||
|
cmake_path(APPEND gem_install_dest_dir "External" ${last_gem_root_path_segment} ${gem_relative_path})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
cmake_path(GET gem_install_dest_dir PARENT_PATH gem_install_dest_dir)
|
||||||
|
if (NOT gem_install_dest_dir)
|
||||||
|
cmake_path(SET gem_install_dest_dir .)
|
||||||
|
endif()
|
||||||
|
if(IS_DIRECTORY ${gem_absolute_path})
|
||||||
|
install(DIRECTORY "${gem_absolute_path}" DESTINATION ${gem_install_dest_dir})
|
||||||
|
elseif (EXISTS ${gem_absolute_path})
|
||||||
|
install(FILES ${gem_absolute_path} DESTINATION ${gem_install_dest_dir})
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
# gem files to install
|
|
||||||
foreach(gem_absolute_file_path ${gems_assets_file_path})
|
|
||||||
cmake_path(RELATIVE_PATH gem_absolute_file_path BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE gem_relative_file_path)
|
|
||||||
cmake_path(GET gem_relative_file_path PARENT_PATH gem_relative_parent_dir)
|
|
||||||
install(FILES ${gem_absolute_file_path}
|
|
||||||
DESTINATION ${gem_relative_parent_dir}
|
|
||||||
)
|
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
# Templates
|
# Templates
|
||||||
@@ -626,3 +655,46 @@ function(ly_setup_target_generator)
|
|||||||
)
|
)
|
||||||
|
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
#! ly_add_install_paths: Adds the list of path to copy to the install layout relative to the same folder
|
||||||
|
# \arg:PATHS - Paths to copy over to the install layout. The DESTINATION sub argument is optional
|
||||||
|
# The INPUT sub-argument is required
|
||||||
|
# \arg:BASE_DIRECTORY(Optional) - Absolute path where a relative path from the each input path will be
|
||||||
|
# based off of. Defaults to LY_ROOT_FOLDER if not supplied
|
||||||
|
function(ly_add_install_paths)
|
||||||
|
set(options)
|
||||||
|
set(oneValueArgs BASE_DIRECTORY)
|
||||||
|
set(multiValueArgs PATHS)
|
||||||
|
cmake_parse_arguments(ly_add_install_paths "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
if(NOT ly_add_install_paths_PATHS)
|
||||||
|
message(FATAL_ERROR "ly_add_install_paths requires at least one input path to copy to the destination")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# The default is the "." directory if not supplied
|
||||||
|
if(NOT ly_add_install_paths_BASE_DIRECTORY)
|
||||||
|
cmake_path(SET ly_add_install_paths_BASE_DIRECTORY ${LY_ROOT_FOLDER})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Separate each path into an INPUT and DESTINATION parameter
|
||||||
|
set(options)
|
||||||
|
set(oneValueArgs INPUT DESTINATION)
|
||||||
|
set(multiValueArgs)
|
||||||
|
foreach(install_path IN LISTS ly_add_install_paths_PATHS)
|
||||||
|
string(REPLACE " " ";" install_path ${install_path})
|
||||||
|
cmake_parse_arguments(install "${options}" "${oneValueArgs}" "${multiValueArgs}" ${install_path})
|
||||||
|
if(NOT install_DESTINATION)
|
||||||
|
ly_get_engine_relative_source_dir(${install_INPUT} rel_to_root_input_path
|
||||||
|
BASE_DIRECTORY ${ly_add_install_paths_BASE_DIRECTORY})
|
||||||
|
cmake_path(GET rel_to_root_input_path PARENT_PATH install_DESTINATION)
|
||||||
|
endif()
|
||||||
|
if(NOT install_DESTINATION)
|
||||||
|
cmake_path(SET install_DESTINATION .)
|
||||||
|
endif()
|
||||||
|
if(IS_DIRECTORY ${install_INPUT})
|
||||||
|
install(DIRECTORY ${install_INPUT} DESTINATION ${install_DESTINATION})
|
||||||
|
elseif(EXISTS ${install_INPUT})
|
||||||
|
install(FILES ${install_INPUT} DESTINATION ${install_DESTINATION})
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
endfunction()
|
||||||
|
|||||||
@@ -8,39 +8,16 @@
|
|||||||
include(cmake/Platform/Common/Configurations_common.cmake)
|
include(cmake/Platform/Common/Configurations_common.cmake)
|
||||||
include(cmake/Platform/Common/VisualStudio_common.cmake)
|
include(cmake/Platform/Common/VisualStudio_common.cmake)
|
||||||
|
|
||||||
set(LY_MSVC_SUPPORTED_GENERATORS
|
if(NOT CMAKE_GENERATOR MATCHES "Visual Studio 1[6-7]")
|
||||||
"Visual Studio 15"
|
message(FATAL_ERROR "Generator ${CMAKE_GENERATOR} not supported")
|
||||||
"Visual Studio 16"
|
endif()
|
||||||
)
|
|
||||||
set(FOUND_SUPPORTED_GENERATOR)
|
|
||||||
foreach(supported_generator ${LY_MSVC_SUPPORTED_GENERATORS})
|
|
||||||
if(CMAKE_GENERATOR MATCHES ${supported_generator})
|
|
||||||
set(FOUND_SUPPORTED_GENERATOR TRUE)
|
|
||||||
break()
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
# VS2017's checks since it defaults the toolchain and target architecture to x86
|
|
||||||
if(CMAKE_GENERATOR MATCHES "Visual Studio 15")
|
|
||||||
if(CMAKE_VS_PLATFORM_NAME AND CMAKE_VS_PLATFORM_NAME STREQUAL "Win32") # VS2017 has Win32 as the default architecture
|
|
||||||
message(FATAL_ERROR "Win32 architecture not supported, specify \"-A x64\" when invoking cmake")
|
|
||||||
endif()
|
|
||||||
if(NOT CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE STREQUAL "x64") # There is at least one library (EditorLib) that make the x86 linker to run out of memory
|
|
||||||
message(FATAL_ERROR "x86 toolset not supported, specify \"-T host=x64\" when invoking cmake")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
# For the other cases, verify that it wasn't invoked with an unsupported architecture. defaults to x86 architecture
|
|
||||||
if(SUPPORTED_VS_PLATFORM_NAME_OVERRIDE)
|
|
||||||
set(SUPPORTED_VS_PLATFORM_NAME ${SUPPORTED_VS_PLATFORM_NAME_OVERRIDE})
|
|
||||||
else()
|
|
||||||
set(SUPPORTED_VS_PLATFORM_NAME x64)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CMAKE_VS_PLATFORM_NAME AND NOT CMAKE_VS_PLATFORM_NAME STREQUAL "${SUPPORTED_VS_PLATFORM_NAME}")
|
# Verify that it wasn't invoked with an unsupported target/host architecture. Currently only supports x64/x64
|
||||||
message(FATAL_ERROR "${CMAKE_VS_PLATFORM_NAME} architecture not supported")
|
if(CMAKE_VS_PLATFORM_NAME AND NOT CMAKE_VS_PLATFORM_NAME STREQUAL "x64")
|
||||||
endif()
|
message(FATAL_ERROR "${CMAKE_VS_PLATFORM_NAME} target architecture is not supported, it must be 'x64'")
|
||||||
if(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE AND NOT CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE STREQUAL "x64")
|
endif()
|
||||||
message(FATAL_ERROR "${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE} toolset not supported")
|
if(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE AND NOT CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE STREQUAL "x64")
|
||||||
endif()
|
message(FATAL_ERROR "${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE} host toolset is not supported, it must be 'x64'")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
ly_append_configurations_options(
|
ly_append_configurations_options(
|
||||||
|
|||||||
@@ -5,6 +5,13 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
if(CMAKE_GENERATOR MATCHES "Visual Studio 16")
|
foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES)
|
||||||
configure_file("${CMAKE_CURRENT_LIST_DIR}/Directory.Build.props" "${CMAKE_BINARY_DIR}/Directory.Build.props" COPYONLY)
|
if(conf STREQUAL debug)
|
||||||
endif()
|
string(APPEND VCPKG_CONFIGURATION_MAPPING " <VcpkgConfiguration Condition=\"'$(Configuration)' == '${conf}'\">Debug</VcpkgConfiguration>\n")
|
||||||
|
else()
|
||||||
|
string(APPEND VCPKG_CONFIGURATION_MAPPING " <VcpkgConfiguration Condition=\"'$(Configuration)' == '${conf}'\">Release</VcpkgConfiguration>\n")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
configure_file("${CMAKE_CURRENT_LIST_DIR}/Directory.Build.props" "${CMAKE_BINARY_DIR}/Directory.Build.props" @ONLY)
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ function(ly_copy source_file target_directory)
|
|||||||
if("${source_file}" IS_NEWER_THAN "${target_directory}/${target_filename}")
|
if("${source_file}" IS_NEWER_THAN "${target_directory}/${target_filename}")
|
||||||
message(STATUS "Copying \"${source_file}\" to \"${target_directory}\"...")
|
message(STATUS "Copying \"${source_file}\" to \"${target_directory}\"...")
|
||||||
file(COPY "${source_file}" DESTINATION "${target_directory}" FILE_PERMISSIONS @LY_COPY_PERMISSIONS@ FOLLOW_SYMLINK_CHAIN)
|
file(COPY "${source_file}" DESTINATION "${target_directory}" FILE_PERMISSIONS @LY_COPY_PERMISSIONS@ FOLLOW_SYMLINK_CHAIN)
|
||||||
|
file(TOUCH_NOCREATE ${target_directory}/${target_filename})
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ function(ly_copy source_file target_directory)
|
|||||||
file(LOCK ${target_directory}/${target_filename}.lock GUARD FUNCTION TIMEOUT 300)
|
file(LOCK ${target_directory}/${target_filename}.lock GUARD FUNCTION TIMEOUT 300)
|
||||||
endif()
|
endif()
|
||||||
file(COPY "${source_file}" DESTINATION "${target_directory}" FILE_PERMISSIONS @LY_COPY_PERMISSIONS@ FOLLOW_SYMLINK_CHAIN)
|
file(COPY "${source_file}" DESTINATION "${target_directory}" FILE_PERMISSIONS @LY_COPY_PERMISSIONS@ FOLLOW_SYMLINK_CHAIN)
|
||||||
file(TOUCH ${target_directory}/${target_filename})
|
file(TOUCH_NOCREATE ${target_directory}/${target_filename})
|
||||||
set(anything_new TRUE PARENT_SCOPE)
|
set(anything_new TRUE PARENT_SCOPE)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -4,15 +4,15 @@
|
|||||||
<Window Width="672" Height="500" HexStyle="100a0000" FontId="2">#(loc.WindowTitle)</Window>
|
<Window Width="672" Height="500" HexStyle="100a0000" FontId="2">#(loc.WindowTitle)</Window>
|
||||||
|
|
||||||
<!-- header font -->
|
<!-- header font -->
|
||||||
<Font Id="0" Height="-24" Weight="500" Foreground="000000">Open Sans</Font>
|
<Font Id="0" Height="-24" Weight="500" Foreground="000000">Segoe UI</Font>
|
||||||
<!-- body font -->
|
<!-- body font -->
|
||||||
<Font Id="1" Height="-12" Weight="500" Foreground="000000">Open Sans</Font>
|
<Font Id="1" Height="-12" Weight="500" Foreground="000000">Segoe UI</Font>
|
||||||
<!-- special font -->
|
<!-- special font -->
|
||||||
<Font Id="2" Height="-12" Weight="500" Foreground="000000" Background="FFFFFF">Open Sans</Font>
|
<Font Id="2" Height="-12" Weight="500" Foreground="000000" Background="FFFFFF">Segoe UI</Font>
|
||||||
<!-- throwaway font for button bar -->
|
<!-- throwaway font for button bar -->
|
||||||
<Font Id="3" Height="-12" Weight="500" Foreground="EEEEEE" Background="EEEEEE">Open Sans</Font>
|
<Font Id="3" Height="-12" Weight="500" Foreground="EEEEEE" Background="EEEEEE">Segoe UI</Font>
|
||||||
<!-- special font for install warning title -->
|
<!-- special font for install warning title -->
|
||||||
<Font Id="4" Height="-14" Weight="500" Foreground="000000">Open Sans</Font>
|
<Font Id="4" Height="-14" Weight="500" Foreground="000000">Segoe UI</Font>
|
||||||
|
|
||||||
<!-- top banner -->
|
<!-- top banner -->
|
||||||
<Image X="28" Y="24" Width="168" Height="64" ImageFile="logo.png" Visible="yes"/>
|
<Image X="28" Y="24" Width="168" Height="64" ImageFile="logo.png" Visible="yes"/>
|
||||||
|
|||||||
@@ -158,10 +158,6 @@ function(ly_delayed_generate_settings_registry)
|
|||||||
message(FATAL_ERROR "Dependency ${gem_target} from ${target} does not exist")
|
message(FATAL_ERROR "Dependency ${gem_target} from ${target} does not exist")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
get_property(has_manually_added_dependencies TARGET ${gem_target} PROPERTY MANUALLY_ADDED_DEPENDENCIES SET)
|
|
||||||
get_target_property(target_type ${gem_target} TYPE)
|
|
||||||
|
|
||||||
|
|
||||||
ly_get_gem_module_root(gem_module_root ${gem_target})
|
ly_get_gem_module_root(gem_module_root ${gem_target})
|
||||||
file(RELATIVE_PATH gem_module_root_relative_to_engine_root ${LY_ROOT_FOLDER} ${gem_module_root})
|
file(RELATIVE_PATH gem_module_root_relative_to_engine_root ${LY_ROOT_FOLDER} ${gem_module_root})
|
||||||
|
|
||||||
@@ -179,7 +175,8 @@ function(ly_delayed_generate_settings_registry)
|
|||||||
list(JOIN target_gem_dependencies_names ",\n" target_gem_dependencies_names)
|
list(JOIN target_gem_dependencies_names ",\n" target_gem_dependencies_names)
|
||||||
string(CONFIGURE ${gems_json_template} gem_json @ONLY)
|
string(CONFIGURE ${gems_json_template} gem_json @ONLY)
|
||||||
get_target_property(is_imported ${target} IMPORTED)
|
get_target_property(is_imported ${target} IMPORTED)
|
||||||
if(is_imported)
|
get_target_property(target_type ${target} TYPE)
|
||||||
|
if(is_imported OR target_type STREQUAL UTILITY)
|
||||||
unset(target_dir)
|
unset(target_dir)
|
||||||
foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES)
|
foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES)
|
||||||
string(TOUPPER ${conf} UCONF)
|
string(TOUPPER ${conf} UCONF)
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ ly_add_target(
|
|||||||
@RUNTIME_DEPENDENCIES_PLACEHOLDER@
|
@RUNTIME_DEPENDENCIES_PLACEHOLDER@
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@TARGET_RUN_HELPER@
|
||||||
|
|
||||||
set(configs @CMAKE_CONFIGURATION_TYPES@)
|
set(configs @CMAKE_CONFIGURATION_TYPES@)
|
||||||
foreach(config ${configs})
|
foreach(config ${configs})
|
||||||
include("@NAME_PLACEHOLDER@_${config}.cmake" OPTIONAL)
|
include("@NAME_PLACEHOLDER@_${config}.cmake" OPTIONAL)
|
||||||
|
|||||||
Vendored
+12
-15
@@ -204,8 +204,6 @@ def CheckoutRepo(boolean disableSubmodules = false) {
|
|||||||
if (!fileExists(ENGINE_REPOSITORY_NAME)) {
|
if (!fileExists(ENGINE_REPOSITORY_NAME)) {
|
||||||
palMkdir(ENGINE_REPOSITORY_NAME)
|
palMkdir(ENGINE_REPOSITORY_NAME)
|
||||||
}
|
}
|
||||||
|
|
||||||
palSh('git lfs uninstall', 'Git LFS Uninstall') // Prevent git from pulling lfs objects during checkout
|
|
||||||
|
|
||||||
if(fileExists('.git')) {
|
if(fileExists('.git')) {
|
||||||
// If the repository after checkout is locked, likely we took a snapshot while git was running,
|
// If the repository after checkout is locked, likely we took a snapshot while git was running,
|
||||||
@@ -239,13 +237,6 @@ def CheckoutRepo(boolean disableSubmodules = false) {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run lfs in a separate step. Jenkins is unable to load the credentials for the custom LFS endpoint
|
|
||||||
withCredentials([usernamePassword(credentialsId: "${env.GITHUB_USER}", passwordVariable: 'accesstoken', usernameVariable: 'username')]) {
|
|
||||||
palSh("git config -f .lfsconfig lfs.url https://${username}:${accesstoken}@${env.LFS_URL}", 'Set credentials', false)
|
|
||||||
}
|
|
||||||
palSh('git lfs install', 'Git LFS Install')
|
|
||||||
palSh('git lfs pull', 'Git LFS Pull')
|
|
||||||
|
|
||||||
// CHANGE_ID is used by some scripts to identify uniquely the current change (usually metric jobs)
|
// CHANGE_ID is used by some scripts to identify uniquely the current change (usually metric jobs)
|
||||||
palSh('git rev-parse HEAD > commitid', 'Getting commit id')
|
palSh('git rev-parse HEAD > commitid', 'Getting commit id')
|
||||||
env.CHANGE_ID = readFile file: 'commitid'
|
env.CHANGE_ID = readFile file: 'commitid'
|
||||||
@@ -586,13 +577,19 @@ finally {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
node('controller') {
|
node('controller') {
|
||||||
step([
|
if("${currentBuild.currentResult}" == "SUCCESS") {
|
||||||
$class: 'Mailer',
|
emailBody = "${BUILD_URL}\nSuccess!"
|
||||||
notifyEveryUnstableBuild: true,
|
} else {
|
||||||
recipients: emailextrecipients([
|
buildFailure = tm('${BUILD_FAILURE_ANALYZER}')
|
||||||
|
emailBody = "${BUILD_URL}\n${buildFailure}!"
|
||||||
|
}
|
||||||
|
emailext (
|
||||||
|
body: "${emailBody}",
|
||||||
|
subject: "${currentBuild.currentResult}: ${JOB_NAME} - Build # ${BUILD_NUMBER}",
|
||||||
|
recipientProviders: [
|
||||||
[$class: 'RequesterRecipientProvider']
|
[$class: 'RequesterRecipientProvider']
|
||||||
])
|
]
|
||||||
])
|
)
|
||||||
}
|
}
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,18 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import ast
|
|
||||||
import boto3
|
import boto3
|
||||||
import datetime
|
import datetime
|
||||||
import urllib.request, urllib.error, urllib.parse
|
import urllib.request, urllib.error, urllib.parse
|
||||||
import os
|
import os
|
||||||
import psutil
|
import psutil
|
||||||
import time
|
import time
|
||||||
import requests
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import traceback
|
from contextlib import contextmanager
|
||||||
|
import threading
|
||||||
|
import _thread
|
||||||
|
|
||||||
DEFAULT_REGION = 'us-west-2'
|
DEFAULT_REGION = 'us-west-2'
|
||||||
DEFAULT_DISK_SIZE = 300
|
DEFAULT_DISK_SIZE = 300
|
||||||
@@ -42,14 +42,18 @@ if os.name == 'nt':
|
|||||||
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
|
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
|
||||||
kernel32.GetDiskFreeSpaceExW.argtypes = (ctypes.c_wchar_p,) + (PULARGE_INTEGER,) * 3
|
kernel32.GetDiskFreeSpaceExW.argtypes = (ctypes.c_wchar_p,) + (PULARGE_INTEGER,) * 3
|
||||||
|
|
||||||
|
|
||||||
class UsageTuple(collections.namedtuple('UsageTuple', 'total, used, free')):
|
class UsageTuple(collections.namedtuple('UsageTuple', 'total, used, free')):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
# Add thousands separator to numbers displayed
|
# Add thousands separator to numbers displayed
|
||||||
return self.__class__.__name__ + '(total={:n}, used={:n}, free={:n})'.format(*self)
|
return self.__class__.__name__ + '(total={:n}, used={:n}, free={:n})'.format(*self)
|
||||||
|
|
||||||
|
|
||||||
def is_dir_symlink(path):
|
def is_dir_symlink(path):
|
||||||
FILE_ATTRIBUTE_REPARSE_POINT = 0x0400
|
FILE_ATTRIBUTE_REPARSE_POINT = 0x0400
|
||||||
return os.path.isdir(path) and (ctypes.windll.kernel32.GetFileAttributesW(str(path)) & FILE_ATTRIBUTE_REPARSE_POINT)
|
return os.path.isdir(path) and (
|
||||||
|
ctypes.windll.kernel32.GetFileAttributesW(str(path)) & FILE_ATTRIBUTE_REPARSE_POINT)
|
||||||
|
|
||||||
|
|
||||||
def get_free_space_mb(path):
|
def get_free_space_mb(path):
|
||||||
if sys.version_info < (3,): # Python 2?
|
if sys.version_info < (3,): # Python 2?
|
||||||
@@ -77,16 +81,39 @@ if os.name == 'nt':
|
|||||||
|
|
||||||
used = total.value - free.value
|
used = total.value - free.value
|
||||||
|
|
||||||
return free.value / 1024 / 1024#for now
|
return free.value / 1024 / 1024 # for now
|
||||||
else:
|
else:
|
||||||
def get_free_space_mb(dirname):
|
def get_free_space_mb(dirname):
|
||||||
st = os.statvfs(dirname)
|
st = os.statvfs(dirname)
|
||||||
return st.f_bavail * st.f_frsize / 1024 / 1024
|
return st.f_bavail * st.f_frsize / 1024 / 1024
|
||||||
|
|
||||||
|
|
||||||
def error(message):
|
def error(message):
|
||||||
print(message)
|
print(message)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def timeout(duration, timeout_message):
|
||||||
|
timer = threading.Timer(duration, lambda: _thread.interrupt_main())
|
||||||
|
timer.start()
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print(timeout_message)
|
||||||
|
raise TimeoutError
|
||||||
|
finally:
|
||||||
|
# If the action ends in specified time, timer is canceled
|
||||||
|
timer.cancel()
|
||||||
|
|
||||||
|
|
||||||
|
def print_drives():
|
||||||
|
if os.name == 'nt':
|
||||||
|
drives_before = win32api.GetLogicalDriveStrings()
|
||||||
|
drives_before = drives_before.split('\000')[:-1]
|
||||||
|
print(drives_before)
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
def parse_args():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('-a', '--action', dest="action", help="Action (mount|unmount|delete)")
|
parser.add_argument('-a', '--action', dest="action", help="Action (mount|unmount|delete)")
|
||||||
@@ -96,8 +123,10 @@ def parse_args():
|
|||||||
parser.add_argument('-b', '--branch', dest="branch", help="Branch")
|
parser.add_argument('-b', '--branch', dest="branch", help="Branch")
|
||||||
parser.add_argument('-plat', '--platform', dest="platform", help="Platform")
|
parser.add_argument('-plat', '--platform', dest="platform", help="Platform")
|
||||||
parser.add_argument('-c', '--build_type', dest="build_type", help="Build type")
|
parser.add_argument('-c', '--build_type', dest="build_type", help="Build type")
|
||||||
parser.add_argument('-ds', '--disk_size', dest="disk_size", help="Disk size in Gigabytes (defaults to {})".format(DEFAULT_DISK_SIZE), default=DEFAULT_DISK_SIZE)
|
parser.add_argument('-ds', '--disk_size', dest="disk_size",
|
||||||
parser.add_argument('-dt', '--disk_type', dest="disk_type", help="Disk type (defaults to {})".format(DEFAULT_DISK_TYPE), default=DEFAULT_DISK_TYPE)
|
help=f"Disk size in Gigabytes (defaults to {DEFAULT_DISK_SIZE})", default=DEFAULT_DISK_SIZE)
|
||||||
|
parser.add_argument('-dt', '--disk_type', dest="disk_type", help=f"Disk type (defaults to {DEFAULT_DISK_TYPE})",
|
||||||
|
default=DEFAULT_DISK_TYPE)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# Input validation
|
# Input validation
|
||||||
@@ -117,19 +146,30 @@ def parse_args():
|
|||||||
error('No platform specified')
|
error('No platform specified')
|
||||||
if args.build_type is None:
|
if args.build_type is None:
|
||||||
error('No build_type specified')
|
error('No build_type specified')
|
||||||
|
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
|
||||||
def get_mount_name(repository_name, project, pipeline, branch, platform, build_type):
|
def get_mount_name(repository_name, project, pipeline, branch, platform, build_type):
|
||||||
mount_name = "{}_{}_{}_{}_{}_{}".format(repository_name, project, pipeline, branch, platform, build_type)
|
mount_name = f"{repository_name}_{project}_{pipeline}_{branch}_{platform}_{build_type}"
|
||||||
mount_name = mount_name.replace('/','_').replace('\\','_')
|
mount_name = mount_name.replace('/', '_').replace('\\', '_')
|
||||||
return mount_name
|
return mount_name
|
||||||
|
|
||||||
|
|
||||||
def get_pipeline_and_branch(pipeline, branch):
|
def get_pipeline_and_branch(pipeline, branch):
|
||||||
pipeline_and_branch = "{}_{}".format(pipeline, branch)
|
pipeline_and_branch = f"{pipeline}_{branch}"
|
||||||
pipeline_and_branch = pipeline_and_branch.replace('/','_').replace('\\','_')
|
pipeline_and_branch = pipeline_and_branch.replace('/', '_').replace('\\', '_')
|
||||||
return pipeline_and_branch
|
return pipeline_and_branch
|
||||||
|
|
||||||
|
|
||||||
|
def get_region_name():
|
||||||
|
session = boto3.session.Session()
|
||||||
|
region = session.region_name
|
||||||
|
if region is None:
|
||||||
|
region = DEFAULT_REGION
|
||||||
|
return region
|
||||||
|
|
||||||
|
|
||||||
def get_ec2_client(region):
|
def get_ec2_client(region):
|
||||||
client = boto3.client('ec2', region_name=region)
|
client = boto3.client('ec2', region_name=region)
|
||||||
return client
|
return client
|
||||||
@@ -140,48 +180,51 @@ def get_ec2_instance_id():
|
|||||||
instance_id = urllib.request.urlopen('http://169.254.169.254/latest/meta-data/instance-id').read()
|
instance_id = urllib.request.urlopen('http://169.254.169.254/latest/meta-data/instance-id').read()
|
||||||
return instance_id.decode("utf-8")
|
return instance_id.decode("utf-8")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e.message)
|
print(e)
|
||||||
error('No EC2 metadata! Check if you are running this script on an EC2 instance.')
|
error('No EC2 metadata! Check if you are running this script on an EC2 instance.')
|
||||||
|
|
||||||
|
|
||||||
def get_availability_zone():
|
def get_availability_zone():
|
||||||
try:
|
try:
|
||||||
availability_zone = urllib.request.urlopen('http://169.254.169.254/latest/meta-data/placement/availability-zone').read()
|
availability_zone = urllib.request.urlopen(
|
||||||
|
'http://169.254.169.254/latest/meta-data/placement/availability-zone').read()
|
||||||
return availability_zone.decode("utf-8")
|
return availability_zone.decode("utf-8")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e.message)
|
print(e)
|
||||||
error('No EC2 metadata! Check if you are running this script on an EC2 instance.')
|
error('No EC2 metadata! Check if you are running this script on an EC2 instance.')
|
||||||
|
|
||||||
|
|
||||||
def kill_processes(workspace='/dev/'):
|
def kill_processes(workspace='/dev/'):
|
||||||
'''
|
"""
|
||||||
Kills all processes that have open file paths associated with the workspace.
|
Kills all processes that have open file paths associated with the workspace.
|
||||||
Uses PSUtil for cross-platform compatibility
|
Uses PSUtil for cross-platform compatibility
|
||||||
'''
|
"""
|
||||||
print('Checking for any stuck processes...')
|
print('Checking for any stuck processes...')
|
||||||
for proc in psutil.process_iter():
|
for proc in psutil.process_iter():
|
||||||
try:
|
try:
|
||||||
if workspace in str(proc.open_files()):
|
if workspace in str(proc.open_files()):
|
||||||
print("{} has open files in {}. Terminating".format(proc.name(), proc.open_files()))
|
print(f"{proc.name()} has open files in {proc.open_files()}. Terminating")
|
||||||
proc.kill()
|
proc.kill()
|
||||||
time.sleep(1) # Just to make sure a parent process has time to close
|
time.sleep(1) # Just to make sure a parent process has time to close
|
||||||
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
def delete_volume(ec2_client, volume_id):
|
def delete_volume(ec2_client, volume_id):
|
||||||
response = ec2_client.delete_volume(VolumeId=volume_id)
|
response = ec2_client.delete_volume(VolumeId=volume_id)
|
||||||
print('Volume {} deleted'.format(volume_id))
|
print(f'Volume {volume_id} deleted')
|
||||||
|
|
||||||
|
|
||||||
def find_snapshot_id(ec2_client, repository_name, project, pipeline, platform, build_type, disk_size):
|
def find_snapshot_id(ec2_client, repository_name, project, pipeline, platform, build_type, disk_size):
|
||||||
mount_name = get_mount_name(repository_name, project, pipeline, 'stabilization_2106', platform, build_type) # we take snapshots out of stabilization_2106
|
mount_name = get_mount_name(repository_name, project, pipeline, 'stabilization_2106', platform,
|
||||||
response = ec2_client.describe_snapshots(Filters= [{
|
build_type) # we take snapshots out of stabilization_2106
|
||||||
|
response = ec2_client.describe_snapshots(Filters=[{
|
||||||
'Name': 'tag:Name', 'Values': [mount_name]
|
'Name': 'tag:Name', 'Values': [mount_name]
|
||||||
}])
|
}])
|
||||||
|
|
||||||
snapshot_id = None
|
snapshot_id = None
|
||||||
if 'Snapshots' in response and len(response['Snapshots']) > 0:
|
if 'Snapshots' in response and len(response['Snapshots']) > 0:
|
||||||
snapshot_start_time_max = None # find the latest snapshot
|
snapshot_start_time_max = None # find the latest snapshot
|
||||||
for snapshot in response['Snapshots']:
|
for snapshot in response['Snapshots']:
|
||||||
if snapshot['State'] == 'completed' and snapshot['VolumeSize'] == disk_size:
|
if snapshot['State'] == 'completed' and snapshot['VolumeSize'] == disk_size:
|
||||||
snapshot_start_time = snapshot['StartTime']
|
snapshot_start_time = snapshot['StartTime']
|
||||||
@@ -190,28 +233,33 @@ def find_snapshot_id(ec2_client, repository_name, project, pipeline, platform, b
|
|||||||
snapshot_id = snapshot['SnapshotId']
|
snapshot_id = snapshot['SnapshotId']
|
||||||
return snapshot_id
|
return snapshot_id
|
||||||
|
|
||||||
def create_volume(ec2_client, availability_zone, repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type):
|
|
||||||
# The actual EBS default calculation for IOps is a floating point number, the closest approxmiation is 4x of the disk size for simplicity
|
def create_volume(ec2_client, availability_zone, repository_name, project, pipeline, branch, platform, build_type,
|
||||||
|
disk_size, disk_type):
|
||||||
mount_name = get_mount_name(repository_name, project, pipeline, branch, platform, build_type)
|
mount_name = get_mount_name(repository_name, project, pipeline, branch, platform, build_type)
|
||||||
pipeline_and_branch = get_pipeline_and_branch(pipeline, branch)
|
pipeline_and_branch = get_pipeline_and_branch(pipeline, branch)
|
||||||
parameters = dict(
|
parameters = dict(
|
||||||
AvailabilityZone = availability_zone,
|
AvailabilityZone=availability_zone,
|
||||||
VolumeType=disk_type,
|
VolumeType=disk_type,
|
||||||
TagSpecifications= [{
|
TagSpecifications=[{
|
||||||
'ResourceType': 'volume',
|
'ResourceType': 'volume',
|
||||||
'Tags': [
|
'Tags': [
|
||||||
{ 'Key': 'Name', 'Value': mount_name },
|
{'Key': 'Name', 'Value': mount_name},
|
||||||
{ 'Key': 'RepositoryName', 'Value': repository_name},
|
{'Key': 'RepositoryName', 'Value': repository_name},
|
||||||
{ 'Key': 'Project', 'Value': project },
|
{'Key': 'Project', 'Value': project},
|
||||||
{ 'Key': 'Pipeline', 'Value': pipeline },
|
{'Key': 'Pipeline', 'Value': pipeline},
|
||||||
{ 'Key': 'BranchName', 'Value': branch },
|
{'Key': 'BranchName', 'Value': branch},
|
||||||
{ 'Key': 'Platform', 'Value': platform },
|
{'Key': 'Platform', 'Value': platform},
|
||||||
{ 'Key': 'BuildType', 'Value': build_type },
|
{'Key': 'BuildType', 'Value': build_type},
|
||||||
{ 'Key': 'PipelineAndBranch', 'Value': pipeline_and_branch }, # used so the snapshoting easily identifies which volumes to snapshot
|
# Used so the snapshoting easily identifies which volumes to snapshot
|
||||||
|
{'Key': 'PipelineAndBranch', 'Value': pipeline_and_branch},
|
||||||
|
|
||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
)
|
)
|
||||||
if 'io1' in disk_type.lower():
|
# The actual EBS default calculation for IOps is a floating point number,
|
||||||
|
# the closest approxmiation is 4x of the disk size for simplicity
|
||||||
|
if 'io1' in disk_type.lower():
|
||||||
parameters['Iops'] = (4 * disk_size)
|
parameters['Iops'] = (4 * disk_size)
|
||||||
|
|
||||||
snapshot_id = find_snapshot_id(ec2_client, repository_name, project, pipeline, platform, build_type, disk_size)
|
snapshot_id = find_snapshot_id(ec2_client, repository_name, project, pipeline, platform, build_type, disk_size)
|
||||||
@@ -230,16 +278,17 @@ def create_volume(ec2_client, availability_zone, repository_name, project, pipel
|
|||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
response = ec2_client.describe_volumes(VolumeIds=[volume_id, ])
|
response = ec2_client.describe_volumes(VolumeIds=[volume_id, ])
|
||||||
while (response['Volumes'][0]['State'] != 'available'):
|
with timeout(DEFAULT_TIMEOUT, 'ERROR: Timeout reached trying to create EBS.'):
|
||||||
time.sleep(1)
|
while response['Volumes'][0]['State'] != 'available':
|
||||||
response = ec2_client.describe_volumes(VolumeIds=[volume_id, ])
|
time.sleep(1)
|
||||||
|
response = ec2_client.describe_volumes(VolumeIds=[volume_id, ])
|
||||||
|
|
||||||
print(("Volume {} created\n\tSnapshot: {}\n\tRepository {}\n\tProject {}\n\tPipeline {}\n\tBranch {}\n\tPlatform: {}\n\tBuild type: {}"
|
print(f"Volume {volume_id} created\n\tSnapshot: {snapshot_id}\n\tRepository {repository_name}\n\t"
|
||||||
.format(volume_id, snapshot_id, repository_name, project, pipeline, branch, platform, build_type)))
|
f"Project {project}\n\tPipeline {pipeline}\n\tBranch {branch}\n\tPlatform: {platform}\n\tBuild type: {build_type}")
|
||||||
return volume_id, created
|
return volume_id, created
|
||||||
|
|
||||||
|
|
||||||
def mount_volume(created):
|
def mount_volume_to_device(created):
|
||||||
print('Mounting volume...')
|
print('Mounting volume...')
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
f = tempfile.NamedTemporaryFile(delete=False)
|
f = tempfile.NamedTemporaryFile(delete=False)
|
||||||
@@ -247,7 +296,7 @@ def mount_volume(created):
|
|||||||
select disk 1
|
select disk 1
|
||||||
online disk
|
online disk
|
||||||
attribute disk clear readonly
|
attribute disk clear readonly
|
||||||
""".encode('utf-8')) # assume disk # for now
|
""".encode('utf-8')) # assume disk # for now
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
print('Creating filesystem on new volume')
|
print('Creating filesystem on new volume')
|
||||||
@@ -259,18 +308,12 @@ def mount_volume(created):
|
|||||||
""".encode('utf-8'))
|
""".encode('utf-8'))
|
||||||
|
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
subprocess.call(['diskpart', '/s', f.name])
|
subprocess.call(['diskpart', '/s', f.name])
|
||||||
|
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|
||||||
drives_after = win32api.GetLogicalDriveStrings()
|
print_drives()
|
||||||
drives_after = drives_after.split('\000')[:-1]
|
|
||||||
|
|
||||||
print(drives_after)
|
|
||||||
|
|
||||||
#drive_letter = next(item for item in drives_after if item not in drives_before)
|
|
||||||
drive_letter = MOUNT_PATH
|
|
||||||
|
|
||||||
os.unlink(f.name)
|
os.unlink(f.name)
|
||||||
|
|
||||||
@@ -283,8 +326,8 @@ def mount_volume(created):
|
|||||||
subprocess.call(['mount', '/dev/xvdf', MOUNT_PATH])
|
subprocess.call(['mount', '/dev/xvdf', MOUNT_PATH])
|
||||||
|
|
||||||
|
|
||||||
def attach_volume(volume, volume_id, instance_id, timeout=DEFAULT_TIMEOUT):
|
def attach_volume_to_ec2_instance(volume, volume_id, instance_id, timeout_duration=DEFAULT_TIMEOUT):
|
||||||
print('Attaching volume {} to instance {}'.format(volume_id, instance_id))
|
print(f'Attaching volume {volume_id} to instance {instance_id}')
|
||||||
volume.attach_to_instance(Device='xvdf',
|
volume.attach_to_instance(Device='xvdf',
|
||||||
InstanceId=instance_id,
|
InstanceId=instance_id,
|
||||||
VolumeId=volume_id)
|
VolumeId=volume_id)
|
||||||
@@ -292,13 +335,10 @@ def attach_volume(volume, volume_id, instance_id, timeout=DEFAULT_TIMEOUT):
|
|||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
# reload the volume just in case
|
# reload the volume just in case
|
||||||
volume.load()
|
volume.load()
|
||||||
timeout_init = time.clock()
|
with timeout(timeout_duration, 'ERROR: Timeout reached trying to mount EBS.'):
|
||||||
while (len(volume.attachments) and volume.attachments[0]['State'] != 'attached'):
|
while len(volume.attachments) and volume.attachments[0]['State'] != 'attached':
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
volume.load()
|
volume.load()
|
||||||
if (time.clock() - timeout_init) > timeout:
|
|
||||||
print('ERROR: Timeout reached trying to mount EBS')
|
|
||||||
exit(1)
|
|
||||||
volume.create_tags(
|
volume.create_tags(
|
||||||
Tags=[
|
Tags=[
|
||||||
{
|
{
|
||||||
@@ -307,11 +347,11 @@ def attach_volume(volume, volume_id, instance_id, timeout=DEFAULT_TIMEOUT):
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
print('Volume {} has been attached to instance {}'.format(volume_id, instance_id))
|
print(f'Volume {volume_id} has been attached to instance {instance_id}')
|
||||||
|
|
||||||
|
|
||||||
def unmount_volume():
|
def unmount_volume_from_device():
|
||||||
print('Umounting volume...')
|
print('Unmounting EBS volume from device...')
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
kill_processes(MOUNT_PATH + 'workspace')
|
kill_processes(MOUNT_PATH + 'workspace')
|
||||||
f = tempfile.NamedTemporaryFile(delete=False)
|
f = tempfile.NamedTemporaryFile(delete=False)
|
||||||
@@ -327,50 +367,31 @@ def unmount_volume():
|
|||||||
subprocess.call(['umount', '-f', MOUNT_PATH])
|
subprocess.call(['umount', '-f', MOUNT_PATH])
|
||||||
|
|
||||||
|
|
||||||
def detach_volume(volume, ec2_instance_id, force, timeout=DEFAULT_TIMEOUT):
|
def detach_volume_from_ec2_instance(volume, ec2_instance_id, force, timeout_duration=DEFAULT_TIMEOUT):
|
||||||
print('Detaching volume {} from instance {}'.format(volume.volume_id, ec2_instance_id))
|
print(f'Detaching volume {volume.volume_id} from instance {ec2_instance_id}')
|
||||||
volume.detach_from_instance(Device='xvdf',
|
volume.detach_from_instance(Device='xvdf',
|
||||||
Force=force,
|
Force=force,
|
||||||
InstanceId=ec2_instance_id,
|
InstanceId=ec2_instance_id,
|
||||||
VolumeId=volume.volume_id)
|
VolumeId=volume.volume_id)
|
||||||
timeout_init = time.clock()
|
try:
|
||||||
while len(volume.attachments) and volume.attachments[0]['State'] != 'detached':
|
with timeout(timeout_duration, 'ERROR: Timeout reached trying to unmount EBS.'):
|
||||||
time.sleep(1)
|
while len(volume.attachments) and volume.attachments[0]['State'] != 'detached':
|
||||||
volume.load()
|
time.sleep(1)
|
||||||
if (time.clock() - timeout_init) > timeout:
|
volume.load()
|
||||||
print('ERROR: Timeout reached trying to unmount EBS.')
|
except TimeoutError:
|
||||||
volume.detach_from_instance(Device='xvdf',Force=True,InstanceId=ec2_instance_id,VolumeId=volume.volume_id)
|
print('Force detaching EBS.')
|
||||||
exit(1)
|
volume.detach_from_instance(Device='xvdf', Force=True, InstanceId=ec2_instance_id, VolumeId=volume.volume_id)
|
||||||
|
|
||||||
print('Volume {} has been detached from instance {}'.format(volume.volume_id, ec2_instance_id))
|
print(f'Volume {volume.volume_id} has been detached from instance {ec2_instance_id}')
|
||||||
volume.load()
|
volume.load()
|
||||||
if len(volume.attachments):
|
if len(volume.attachments):
|
||||||
print('Volume still has attachments')
|
print('Volume still has attachments')
|
||||||
for attachment in volume.attachments:
|
for attachment in volume.attachments:
|
||||||
print('Volume {} {} to instance {}'.format(attachment['VolumeId'], attachment['State'], attachment['InstanceId']))
|
print(f"Volume {attachment['VolumeId']} {attachment['State']} to instance {attachment['InstanceId']}")
|
||||||
|
|
||||||
|
|
||||||
def attach_ebs_and_create_partition_with_retry(volume, volume_id, ec2_instance_id, created):
|
|
||||||
attach_volume(volume, volume_id, ec2_instance_id)
|
|
||||||
mount_volume(created)
|
|
||||||
attempt = 1
|
|
||||||
while attempt <= MAX_EBS_MOUNTING_ATTEMPT:
|
|
||||||
if os.name == 'nt':
|
|
||||||
drives_after = win32api.GetLogicalDriveStrings()
|
|
||||||
drives_after = drives_after.split('\000')[:-1]
|
|
||||||
if MOUNT_PATH not in drives_after:
|
|
||||||
print('Disk partitioning failed, retrying...')
|
|
||||||
unmount_volume()
|
|
||||||
detach_volume(volume, ec2_instance_id, False)
|
|
||||||
attach_volume(volume, volume_id, ec2_instance_id)
|
|
||||||
mount_volume(created)
|
|
||||||
attempt += 1
|
|
||||||
|
|
||||||
def mount_ebs(repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type):
|
def mount_ebs(repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type):
|
||||||
session = boto3.session.Session()
|
region = get_region_name()
|
||||||
region = session.region_name
|
|
||||||
if region is None:
|
|
||||||
region = DEFAULT_REGION
|
|
||||||
ec2_client = get_ec2_client(region)
|
ec2_client = get_ec2_client(region)
|
||||||
ec2_instance_id = get_ec2_instance_id()
|
ec2_instance_id = get_ec2_instance_id()
|
||||||
ec2_availability_zone = get_availability_zone()
|
ec2_availability_zone = get_availability_zone()
|
||||||
@@ -379,70 +400,70 @@ def mount_ebs(repository_name, project, pipeline, branch, platform, build_type,
|
|||||||
|
|
||||||
for volume in ec2_instance.volumes.all():
|
for volume in ec2_instance.volumes.all():
|
||||||
for attachment in volume.attachments:
|
for attachment in volume.attachments:
|
||||||
print('attachment device: {}'.format(attachment['Device']))
|
print(f"attachment device: {attachment['Device']}")
|
||||||
if 'xvdf' in attachment['Device'] and attachment['State'] != 'detached':
|
if 'xvdf' in attachment['Device'] and attachment['State'] != 'detached':
|
||||||
print('A device is already attached to xvdf. This likely means a previous build failed to detach its ' \
|
print('A device is already attached to xvdf. This likely means a previous build failed to detach its '
|
||||||
'build volume. This volume is considered orphaned and will be detached from this instance.')
|
'build volume. This volume is considered orphaned and will be detached from this instance.')
|
||||||
unmount_volume()
|
unmount_volume_from_device()
|
||||||
detach_volume(volume, ec2_instance_id, False) # Force unmounts should not be used, as that will cause the EBS block device driver to fail the remount
|
detach_volume_from_ec2_instance(volume, ec2_instance_id,
|
||||||
|
False) # Force unmounts should not be used, as that will cause the EBS block device driver to fail the remount
|
||||||
|
|
||||||
mount_name = get_mount_name(repository_name, project, pipeline, branch, platform, build_type)
|
mount_name = get_mount_name(repository_name, project, pipeline, branch, platform, build_type)
|
||||||
response = ec2_client.describe_volumes(Filters=[{
|
response = ec2_client.describe_volumes(Filters=[{
|
||||||
'Name': 'tag:Name', 'Values': [mount_name]
|
'Name': 'tag:Name', 'Values': [mount_name]
|
||||||
}])
|
}])
|
||||||
|
|
||||||
created = False
|
created = False
|
||||||
if 'Volumes' in response and not len(response['Volumes']):
|
if 'Volumes' in response and not len(response['Volumes']):
|
||||||
print('Volume for {} doesn\'t exist creating it...'.format(mount_name))
|
print(f'Volume for {mount_name} doesn\'t exist creating it...')
|
||||||
# volume doesn't exist, create it
|
# volume doesn't exist, create it
|
||||||
volume_id, created = create_volume(ec2_client, ec2_availability_zone, repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type)
|
volume_id, created = create_volume(ec2_client, ec2_availability_zone, repository_name, project, pipeline,
|
||||||
|
branch, platform, build_type, disk_size, disk_type)
|
||||||
else:
|
else:
|
||||||
volume = response['Volumes'][0]
|
volume = response['Volumes'][0]
|
||||||
volume_id = volume['VolumeId']
|
volume_id = volume['VolumeId']
|
||||||
print('Current volume {} is a {} GB {}'.format(volume_id, volume['Size'], volume['VolumeType']))
|
print(f"Current volume {volume_id} is a {volume['Size']} GB {volume['VolumeType']}")
|
||||||
if (volume['Size'] != disk_size or volume['VolumeType'] != disk_type):
|
if volume['Size'] != disk_size or volume['VolumeType'] != disk_type:
|
||||||
print('Override disk attributes does not match the existing volume, deleting {} and replacing the volume'.format(volume_id))
|
print(
|
||||||
|
f'Override disk attributes does not match the existing volume, deleting {volume_id} and replacing the volume')
|
||||||
delete_volume(ec2_client, volume_id)
|
delete_volume(ec2_client, volume_id)
|
||||||
volume_id, created = create_volume(ec2_client, ec2_availability_zone, repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type)
|
volume_id, created = create_volume(ec2_client, ec2_availability_zone, repository_name, project, pipeline,
|
||||||
|
branch, platform, build_type, disk_size, disk_type)
|
||||||
if len(volume['Attachments']):
|
if len(volume['Attachments']):
|
||||||
# this is bad we shouldn't be attached, we should have detached at the end of a build
|
# this is bad we shouldn't be attached, we should have detached at the end of a build
|
||||||
attachment = volume['Attachments'][0]
|
attachment = volume['Attachments'][0]
|
||||||
print(('Volume already has attachment {}, detaching...'.format(attachment)))
|
print(f'Volume already has attachment {attachment}, detaching...')
|
||||||
detach_volume(ec2_resource.Volume(volume_id), attachment['InstanceId'], True)
|
detach_volume_from_ec2_instance(ec2_resource.Volume(volume_id), attachment['InstanceId'], True)
|
||||||
|
|
||||||
volume = ec2_resource.Volume(volume_id)
|
volume = ec2_resource.Volume(volume_id)
|
||||||
|
|
||||||
if os.name == 'nt':
|
print_drives()
|
||||||
drives_before = win32api.GetLogicalDriveStrings()
|
attach_volume_to_ec2_instance(volume, volume_id, ec2_instance_id)
|
||||||
drives_before = drives_before.split('\000')[:-1]
|
mount_volume_to_device(created)
|
||||||
|
print_drives()
|
||||||
print(drives_before)
|
|
||||||
|
|
||||||
attach_ebs_and_create_partition_with_retry(volume, volume_id, ec2_instance_id, created)
|
|
||||||
|
|
||||||
free_space_mb = get_free_space_mb(MOUNT_PATH)
|
free_space_mb = get_free_space_mb(MOUNT_PATH)
|
||||||
print('Free disk space {}MB'.format(free_space_mb))
|
print(f'Free disk space {free_space_mb}MB')
|
||||||
|
|
||||||
if free_space_mb < LOW_EBS_DISK_SPACE_LIMIT:
|
if free_space_mb < LOW_EBS_DISK_SPACE_LIMIT:
|
||||||
print('Volume is running below EBS free disk space treshhold {}MB. Recreating volume and running clean build.'.format(LOW_EBS_DISK_SPACE_LIMIT))
|
print(f'Volume is running below EBS free disk space treshhold {LOW_EBS_DISK_SPACE_LIMIT}MB. Recreating volume and running clean build.')
|
||||||
unmount_volume()
|
unmount_volume_from_device()
|
||||||
detach_volume(volume, ec2_instance_id, False)
|
detach_volume_from_ec2_instance(volume, ec2_instance_id, False)
|
||||||
delete_volume(ec2_client, volume_id)
|
delete_volume(ec2_client, volume_id)
|
||||||
new_disk_size = int(volume.size * 1.25)
|
new_disk_size = int(volume.size * 1.25)
|
||||||
if new_disk_size > MAX_EBS_DISK_SIZE:
|
if new_disk_size > MAX_EBS_DISK_SIZE:
|
||||||
print('Error: EBS disk size reached to the allowed maximum disk size {}MB, please contact ly-infra@ and ly-build@ to investigate.'.format(MAX_EBS_DISK_SIZE))
|
print(f'Error: EBS disk size reached to the allowed maximum disk size {MAX_EBS_DISK_SIZE}MB, please contact ly-infra@ and ly-build@ to investigate.')
|
||||||
exit(1)
|
exit(1)
|
||||||
print('Recreating the EBS with disk size {}'.format(new_disk_size))
|
print(f'Recreating the EBS with disk size {new_disk_size}')
|
||||||
volume_id, created = create_volume(ec2_client, ec2_availability_zone, repository_name, project, pipeline, branch, platform, build_type, new_disk_size, disk_type)
|
volume_id, created = create_volume(ec2_client, ec2_availability_zone, repository_name, project, pipeline,
|
||||||
|
branch, platform, build_type, new_disk_size, disk_type)
|
||||||
volume = ec2_resource.Volume(volume_id)
|
volume = ec2_resource.Volume(volume_id)
|
||||||
attach_ebs_and_create_partition_with_retry(volume, volume_id, ec2_instance_id, created)
|
attach_volume_to_ec2_instance(volume, volume_id, ec2_instance_id)
|
||||||
|
mount_volume_to_device(created)
|
||||||
|
|
||||||
|
|
||||||
def unmount_ebs():
|
def unmount_ebs():
|
||||||
session = boto3.session.Session()
|
region = get_region_name()
|
||||||
region = session.region_name
|
|
||||||
if region is None:
|
|
||||||
region = DEFAULT_REGION
|
|
||||||
ec2_client = get_ec2_client(region)
|
|
||||||
ec2_instance_id = get_ec2_instance_id()
|
ec2_instance_id = get_ec2_instance_id()
|
||||||
ec2_resource = boto3.resource('ec2', region_name=region)
|
ec2_resource = boto3.resource('ec2', region_name=region)
|
||||||
ec2_instance = ec2_resource.Instance(ec2_instance_id)
|
ec2_instance = ec2_resource.Instance(ec2_instance_id)
|
||||||
@@ -454,7 +475,7 @@ def unmount_ebs():
|
|||||||
|
|
||||||
for attached_volume in ec2_instance.volumes.all():
|
for attached_volume in ec2_instance.volumes.all():
|
||||||
for attachment in attached_volume.attachments:
|
for attachment in attached_volume.attachments:
|
||||||
print('attachment device: {}'.format(attachment['Device']))
|
print(f"attachment device: {attachment['Device']}")
|
||||||
if attachment['Device'] == 'xvdf':
|
if attachment['Device'] == 'xvdf':
|
||||||
volume = attached_volume
|
volume = attached_volume
|
||||||
|
|
||||||
@@ -462,24 +483,18 @@ def unmount_ebs():
|
|||||||
# volume is not mounted
|
# volume is not mounted
|
||||||
print('Volume is not mounted')
|
print('Volume is not mounted')
|
||||||
else:
|
else:
|
||||||
unmount_volume()
|
unmount_volume_from_device()
|
||||||
detach_volume(volume, ec2_instance_id, False)
|
detach_volume_from_ec2_instance(volume, ec2_instance_id, False)
|
||||||
|
|
||||||
|
|
||||||
def delete_ebs(repository_name, project, pipeline, branch, platform, build_type):
|
def delete_ebs(repository_name, project, pipeline, branch, platform, build_type):
|
||||||
unmount_ebs()
|
unmount_ebs()
|
||||||
|
region = get_region_name()
|
||||||
session = boto3.session.Session()
|
|
||||||
region = session.region_name
|
|
||||||
if region is None:
|
|
||||||
region = DEFAULT_REGION
|
|
||||||
ec2_client = get_ec2_client(region)
|
ec2_client = get_ec2_client(region)
|
||||||
ec2_instance_id = get_ec2_instance_id()
|
|
||||||
ec2_resource = boto3.resource('ec2', region_name=region)
|
|
||||||
ec2_instance = ec2_resource.Instance(ec2_instance_id)
|
|
||||||
|
|
||||||
mount_name = get_mount_name(repository_name, project, pipeline, branch, platform, build_type)
|
mount_name = get_mount_name(repository_name, project, pipeline, branch, platform, build_type)
|
||||||
response = ec2_client.describe_volumes(Filters=[
|
response = ec2_client.describe_volumes(Filters=[
|
||||||
{ 'Name': 'tag:Name', 'Values': [mount_name] }
|
{'Name': 'tag:Name', 'Values': [mount_name]}
|
||||||
])
|
])
|
||||||
|
|
||||||
if 'Volumes' in response and len(response['Volumes']):
|
if 'Volumes' in response and len(response['Volumes']):
|
||||||
@@ -496,7 +511,8 @@ def main(action, repository_name, project, pipeline, branch, platform, build_typ
|
|||||||
elif action == 'delete':
|
elif action == 'delete':
|
||||||
delete_ebs(repository_name, project, pipeline, branch, platform, build_type)
|
delete_ebs(repository_name, project, pipeline, branch, platform, build_type)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
args = parse_args()
|
args = parse_args()
|
||||||
ret = main(args.action, args.repository_name, args.project, args.pipeline, args.branch, args.platform, args.build_type, args.disk_size, args.disk_type)
|
main(args.action, args.repository_name, args.project, args.pipeline, args.branch, args.platform,
|
||||||
sys.exit(ret)
|
args.build_type, args.disk_size, args.disk_type)
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import fnmatch
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
class LicenseScanner:
|
||||||
|
"""Class to contain license scanner.
|
||||||
|
|
||||||
|
Scans source tree for license files using provided filename patterns and generates a file
|
||||||
|
with the contents of all the licenses.
|
||||||
|
|
||||||
|
:param config_file: Config file with license patterns and scanner settings
|
||||||
|
"""
|
||||||
|
|
||||||
|
DEFAULT_CONFIG_FILE = 'scanner_config.json'
|
||||||
|
|
||||||
|
def __init__(self, config_file=None):
|
||||||
|
self.config_file = config_file
|
||||||
|
self.config_data = self._load_config()
|
||||||
|
self.license_regex = self._load_license_regex()
|
||||||
|
|
||||||
|
def _load_config(self):
|
||||||
|
"""Load config from the provided file. Sets default file if one is not provided."""
|
||||||
|
if self.config_file is None:
|
||||||
|
script_directory = os.path.dirname(os.path.abspath(__file__)) # Default file expected in same dir as script
|
||||||
|
self.config_file = os.path.join(script_directory, self.DEFAULT_CONFIG_FILE)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(self.config_file) as f:
|
||||||
|
return json.load(f)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print('Config file cannot be found')
|
||||||
|
raise
|
||||||
|
|
||||||
|
def _load_license_regex(self):
|
||||||
|
"""Returns regex object with case-insensitive matching from the list of filename patterns."""
|
||||||
|
regex_patterns = []
|
||||||
|
for pattern in self.config_data['license_patterns']:
|
||||||
|
regex_patterns.append(fnmatch.translate(pattern))
|
||||||
|
return re.compile('|'.join(regex_patterns), re.IGNORECASE)
|
||||||
|
|
||||||
|
def scan(self, path=os.curdir):
|
||||||
|
"""Scan directory tree for filenames matching license_regex.
|
||||||
|
|
||||||
|
:param path: Path of the directory to run scanner
|
||||||
|
:return: Package paths and their corresponding license file contents
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
licenses = 0
|
||||||
|
license_files = {}
|
||||||
|
|
||||||
|
for dirpath, dirnames, filenames in os.walk(path):
|
||||||
|
for file in filenames:
|
||||||
|
if self.license_regex.match(file):
|
||||||
|
license_file_content = self._get_license_file_contents(os.path.join(dirpath, file))
|
||||||
|
rel_dirpath = os.path.relpath(dirpath, path) # Limit path inside scanned directory
|
||||||
|
license_files[rel_dirpath] = license_file_content
|
||||||
|
licenses += 1
|
||||||
|
print(f'License file: {os.path.join(dirpath, file)}')
|
||||||
|
|
||||||
|
# Remove directories that should not be scanned
|
||||||
|
for dir in self.config_data['excluded_directories']:
|
||||||
|
if dir in dirnames:
|
||||||
|
dirnames.remove(dir)
|
||||||
|
print(f'{licenses} license files found.')
|
||||||
|
return license_files
|
||||||
|
|
||||||
|
def _get_license_file_contents(self, filepath):
|
||||||
|
try:
|
||||||
|
with open(filepath, encoding='utf8') as f:
|
||||||
|
return f.read()
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
print(f'Unable to read license file: {filepath}')
|
||||||
|
pass
|
||||||
|
|
||||||
|
def create_license_file(self, licenses, filepath='NOTICES.txt'):
|
||||||
|
"""Creates file with all the provided license file contents.
|
||||||
|
|
||||||
|
:param licenses: Dict with package paths and their corresponding license file contents
|
||||||
|
:param filepath: Path to write the file
|
||||||
|
"""
|
||||||
|
package_separator = '------------------------------------'
|
||||||
|
with open(filepath, 'w', encoding='utf8') as f:
|
||||||
|
for directory, license in licenses.items():
|
||||||
|
license_output = '\n\n'.join([
|
||||||
|
f'{package_separator}',
|
||||||
|
f'Package path: {directory}',
|
||||||
|
'License:',
|
||||||
|
f'{license}\n'
|
||||||
|
])
|
||||||
|
f.write(license_output)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Script to run LicenseScanner and generate license file')
|
||||||
|
parser.add_argument('--config-file', '-c', type=pathlib.Path, help='Config file for LicenseScanner')
|
||||||
|
parser.add_argument('--license-file-path', '-l', type=pathlib.Path, help='Create license file in the provided path')
|
||||||
|
parser.add_argument('--scan-path', '-s', default=os.curdir, type=pathlib.Path, help='Path to scan')
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
args = parse_args()
|
||||||
|
ls = LicenseScanner(args.config_file)
|
||||||
|
licenses = ls.scan(args.scan_path)
|
||||||
|
|
||||||
|
if args.license_file_path:
|
||||||
|
ls.create_license_file(licenses, args.license_file_path)
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
print(f'Type: {type(e).__name__}, Error: {e}')
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"excluded_directories": [
|
||||||
|
".git",
|
||||||
|
".venv",
|
||||||
|
"build",
|
||||||
|
"license_scanner"
|
||||||
|
],
|
||||||
|
"license_patterns": [
|
||||||
|
"LICENSE*",
|
||||||
|
"COPYING*"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user