Merge branch 'main' into LoadPipelineFromGitHub
This commit is contained in:
@@ -35,7 +35,7 @@ subprojects {
|
||||
sdkVer = ${SDK_VER}
|
||||
ndkPlatformVer = ${NDK_PLATFORM_VER}
|
||||
buildToolsVer = '${SDK_BUILD_TOOL_VER}'
|
||||
lyDevRoot = '${LY_DEV_ROOT}'
|
||||
lyEngineRoot = '${LY_ENGINE_ROOT}'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <AzCore/Asset/AssetManagerBus.h>
|
||||
#include <AzCore/Asset/AssetManagerComponent.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/Jobs/Algorithms.h>
|
||||
#include <AzCore/Jobs/JobManagerComponent.h>
|
||||
#include <AzCore/Memory/MemoryComponent.h>
|
||||
#include <AzCore/Module/DynamicModuleHandle.h>
|
||||
@@ -23,6 +22,7 @@
|
||||
#include <AzCore/Slice/SliceSystemComponent.h>
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
#include <AzCore/UserSettings/UserSettingsComponent.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
#include <AzFramework/Asset/AssetCatalogComponent.h>
|
||||
#include <AzFramework/Driller/RemoteDrillerInterface.h>
|
||||
@@ -38,18 +38,12 @@
|
||||
#include <AzToolsFramework/Asset/AssetUtils.h>
|
||||
#include <AzToolsFramework/AssetBundle/AssetBundleComponent.h>
|
||||
#include <AzToolsFramework/AssetCatalog/PlatformAddressedAssetCatalogBus.h>
|
||||
#include <AzCore/Jobs/Algorithms.h>
|
||||
|
||||
namespace AssetBundler
|
||||
{
|
||||
const char compareVariablePrefix = '$';
|
||||
|
||||
GemInfo::GemInfo(AZStd::string name, AZStd::string relativeFilePath, AZStd::string absoluteFilePath)
|
||||
: m_gemName(name)
|
||||
, m_relativeFilePath(relativeFilePath)
|
||||
, m_absoluteFilePath(absoluteFilePath)
|
||||
{
|
||||
}
|
||||
|
||||
ApplicationManager::ApplicationManager(int* argc, char*** argv)
|
||||
: AzToolsFramework::ToolsApplication(argc, argv)
|
||||
{
|
||||
@@ -123,41 +117,21 @@ namespace AssetBundler
|
||||
}
|
||||
m_showVerboseOutput = ShouldPrintVerbose(parser);
|
||||
|
||||
ComputeEngineRoot();
|
||||
m_currentProjectName = AZStd::string_view{ AZ::Utils::GetProjectName() };
|
||||
|
||||
const char* devRoot = nullptr;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(devRoot, &AzFramework::ApplicationRequests::GetEngineRoot);
|
||||
if (devRoot)
|
||||
if (m_currentProjectName.empty())
|
||||
{
|
||||
AZ_TracePrintf(AssetBundler::AppWindowNameVerbose, "Setting devroot alias to ( %s ).\n", devRoot);
|
||||
AZ::IO::FileIOBase::GetInstance()->SetAlias("@devroot@", devRoot);
|
||||
}
|
||||
|
||||
AZStd::string platformName(AzToolsFramework::AssetSystem::GetHostAssetPlatform());
|
||||
AZStd::string assetsAlias;
|
||||
AZStd::string assetCatalogFile;
|
||||
|
||||
AZ::Outcome<void, AZStd::string> result = AssetBundler::ComputeAssetAliasAndGameName(platformName, assetCatalogFile, assetsAlias, m_currentProjectName);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
AZ_Error(AppWindowName, false, result.GetError().c_str());
|
||||
AZ_Error(AppWindowName, false, "Unable to retrieve project name from the Settings Registry");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* appRoot = nullptr;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(appRoot, &AzFramework::ApplicationRequests::GetAppRoot);
|
||||
|
||||
// Gems
|
||||
if (!AzToolsFramework::AssetUtils::GetGemsInfo(g_cachedEngineRoot, appRoot, m_currentProjectName.c_str(), m_gemInfoList))
|
||||
if (!AzFramework::GetGemsInfo(m_gemInfoList, *m_settingsRegistry))
|
||||
{
|
||||
AZ_Error(AppWindowName, false, "Failed to read Gems for project: %s\n", m_currentProjectName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// @assets@ alias
|
||||
AZ_TracePrintf(AssetBundler::AppWindowNameVerbose, "Setting asset alias to ( %s ).\n", assetsAlias.c_str());
|
||||
AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", assetsAlias.c_str());
|
||||
|
||||
m_platformCatalogManager = AZStd::make_unique<AzToolsFramework::PlatformAddressedAssetCatalogManager>();
|
||||
|
||||
InitArgValidationLists();
|
||||
@@ -1304,13 +1278,18 @@ namespace AssetBundler
|
||||
|
||||
AZ::Outcome<void, AZStd::string> ApplicationManager::ValidateInputArgs(const AzFramework::CommandLine* parser, const AZStd::vector<const char*>& validArgList)
|
||||
{
|
||||
for (const auto& paramInfo : parser->GetSwitchList())
|
||||
for (const auto& paramInfo : *parser)
|
||||
{
|
||||
// Skip positional arguments
|
||||
if (paramInfo.m_option.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
bool isValidArg = false;
|
||||
|
||||
for (const auto& validArg : validArgList)
|
||||
{
|
||||
if (AzFramework::StringFunc::Equal(paramInfo.first.c_str(), validArg))
|
||||
if (AzFramework::StringFunc::Equal(paramInfo.m_option, validArg))
|
||||
{
|
||||
isValidArg = true;
|
||||
break;
|
||||
@@ -1319,7 +1298,7 @@ namespace AssetBundler
|
||||
|
||||
if (!isValidArg)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Invalid command: \"--%s\" is not a valid argument for this sub-command.", paramInfo.first.c_str()));
|
||||
return AZ::Failure(AZStd::string::format("Invalid command: \"--%s\" is not a valid argument for this sub-command.", paramInfo.m_option.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1407,7 +1386,7 @@ namespace AssetBundler
|
||||
const char* appRoot = nullptr;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(appRoot, &AzFramework::ApplicationRequests::GetAppRoot);
|
||||
|
||||
AzFramework::PlatformFlags platformFlags = GetEnabledPlatformFlags(g_cachedEngineRoot, appRoot, m_currentProjectName.c_str());
|
||||
AzFramework::PlatformFlags platformFlags = GetEnabledPlatformFlags(GetEngineRoot(), appRoot, AZ::Utils::GetProjectPath().c_str());
|
||||
auto platformsString = AzFramework::PlatformHelper::GetCommaSeparatedPlatformList(platformFlags);
|
||||
|
||||
AZ_TracePrintf(AppWindowName, "No platform specified, defaulting to platforms ( %s ).\n", platformsString.c_str());
|
||||
@@ -1573,7 +1552,8 @@ namespace AssetBundler
|
||||
// Add Default Seed List Files
|
||||
if (params.m_addDefaultSeedListFiles)
|
||||
{
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> defaultSeedListFiles = GetDefaultSeedListFiles(AssetBundler::g_cachedEngineRoot, m_currentProjectName.c_str(), m_gemInfoList, params.m_platformFlags);
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> defaultSeedListFiles = GetDefaultSeedListFiles(GetEngineRoot(), AZ::Utils::GetProjectPath(),
|
||||
m_gemInfoList, params.m_platformFlags);
|
||||
if (defaultSeedListFiles.empty())
|
||||
{
|
||||
// Error has already been thrown
|
||||
@@ -1590,7 +1570,7 @@ namespace AssetBundler
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::vector<AZStd::string> defaultSeeds = GetDefaultSeeds(AssetBundler::g_cachedEngineRoot, m_currentProjectName.c_str());
|
||||
AZStd::vector<AZStd::string> defaultSeeds = GetDefaultSeeds(GetEngineRoot(), AZ::Utils::GetProjectPath(), m_currentProjectName);
|
||||
if (defaultSeeds.empty())
|
||||
{
|
||||
// Error has already been thrown
|
||||
@@ -1830,9 +1810,9 @@ namespace AssetBundler
|
||||
|
||||
bool hasError = false;
|
||||
|
||||
for (const AZStd::string_view& platformName : AzFramework::PlatformHelper::GetPlatformsInterpreted(paramsOutcome.GetValue().m_platformFlags))
|
||||
for (AZStd::string platformName : AzFramework::PlatformHelper::GetPlatformsInterpreted(paramsOutcome.GetValue().m_platformFlags))
|
||||
{
|
||||
AZ_TracePrintf(AssetBundler::AppWindowName, "Running Compare command for the %.*s platform...\n", aznumeric_cast<int>(platformName.size()), platformName.data());
|
||||
AZ_TracePrintf(AssetBundler::AppWindowName, "Running Compare command for the %s platform...\n", platformName.c_str());
|
||||
|
||||
ComparisonParams params = paramsOutcome.GetValue();
|
||||
AddPlatformToAllComparisonParams(params, platformName);
|
||||
@@ -2687,7 +2667,7 @@ namespace AssetBundler
|
||||
AZ_Printf(AppWindowName, " \"path\" - This refers to an Engine-Root-Relative path.\n");
|
||||
AZ_Printf(AppWindowName, " - Example: \"C:\\Lumberyard\\dev\\SamplesProject\\test.txt\" can be represented as \"SamplesProject\\test.txt\".\n");
|
||||
AZ_Printf(AppWindowName, " \"cache path\" - This refers to a Cache-Relative path.\n");
|
||||
AZ_Printf(AppWindowName, " - Example: \"C:\\Lumberyard\\dev\\Cache\\SamplesProject\\pc\\samplesproject\\animations\\skeletonlist.xml\" is represented as \"animations\\skeletonlist.xml\".\n");
|
||||
AZ_Printf(AppWindowName, " - Example: \"C:\\Lumberyard\\dev\\SamplesProject\\Cache\\pc\\animations\\skeletonlist.xml\" is represented as \"animations\\skeletonlist.xml\".\n");
|
||||
AZ_Printf(AppWindowName, "\n");
|
||||
|
||||
OutputHelpSeeds();
|
||||
@@ -2718,7 +2698,7 @@ namespace AssetBundler
|
||||
AZ_Printf(AppWindowName, "%-31s---Takes in a cache path to a pre-processed asset.\n", "");
|
||||
AZ_Printf(AppWindowName, " --%-25s-Removes the asset from the list of root assets for the specified platform.\n", RemoveSeedArg);
|
||||
AZ_Printf(AppWindowName, "%-31s---To completely remove the asset, it must be removed for all platforms.\n", "");
|
||||
AZ_Printf(AppWindowName, "%-31s---Takes in a cache path to a pre-processed asset. A cache path is a path relative to \"...\\dev\\Cache\\ProjectName\\platform\\projectname\\\"\n", "");
|
||||
AZ_Printf(AppWindowName, "%-31s---Takes in a cache path to a pre-processed asset. A cache path is a path relative to \"ProjectPath\\Cache\\platform\\\"\n", "");
|
||||
AZ_Printf(AppWindowName, " --%-25s-Adds the specified platform to every Seed in the Seed List file, if possible.\n", AddPlatformToAllSeedsFlag);
|
||||
AZ_Printf(AppWindowName, " --%-25s-Removes the specified platform from every Seed in the Seed List file, if possible.\n", RemovePlatformFromAllSeedsFlag);
|
||||
AZ_Printf(AppWindowName, " --%-25s-Outputs the contents of the Seed List file after performing any specified operations.\n", PrintFlag);
|
||||
@@ -2730,7 +2710,7 @@ namespace AssetBundler
|
||||
AZ_Printf(AppWindowName, " --%-25s-Allows input file path to still match if the file path case is different than on disk.\n", IgnoreFileCaseFlag);
|
||||
AZ_Printf(AppWindowName, " --%-25s-[Testing] Specifies the Asset Catalog file referenced by all Seed operations.\n", AssetCatalogFileArg);
|
||||
AZ_Printf(AppWindowName, "%-31s---Designed to be used in Unit Tests.\n", "");
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the game project to use rather than the current default project set in bootsrap.cfg's sys_game_folder.\n", ProjectArg);
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the game project to use rather than the current default project set in bootstrap.cfg's project_path.\n", ProjectArg);
|
||||
}
|
||||
|
||||
void ApplicationManager::OutputHelpAssetLists()
|
||||
@@ -2740,7 +2720,7 @@ namespace AssetBundler
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the Asset List file to operate on by path. Must include (.%s) file extension.\n", AssetListFileArg, AssetSeedManager::GetAssetListFileExtension());
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the Seed List file(s) that will be used as root(s) when generating this Asset List file.\n", SeedListFileArg);
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the Seed(s) to use as root(s) when generating this Asset List File.\n", AddSeedArg);
|
||||
AZ_Printf(AppWindowName, "%-31s---Takes in a cache path to a pre-processed asset. A cache path is a path relative to \"...\\dev\\Cache\\ProjectName\\platform\\projectname\\\"\n", "");
|
||||
AZ_Printf(AppWindowName, "%-31s---Takes in a cache path to a pre-processed asset. A cache path is a path relative to \"ProjectPath\\Cache\\platform\\\"\n", "");
|
||||
AZ_Printf(AppWindowName, " --%-25s-The specified files and all dependencies will be ignored when generating the Asset List file.\n", SkipArg);
|
||||
AZ_Printf(AppWindowName, "%-31s---Takes in a comma-separated list of either: cache paths to pre-processed assets, or wildcard patterns.\n", "");
|
||||
AZ_Printf(AppWindowName, " --%-25s-Automatically include all default Seed List files in generated Asset List File.\n", AddDefaultSeedListFilesFlag);
|
||||
@@ -2754,7 +2734,7 @@ namespace AssetBundler
|
||||
AZ_Printf(AppWindowName, " --%-25s-Run all input commands, without saving to the specified Asset List file.\n", DryRunFlag);
|
||||
AZ_Printf(AppWindowName, " --%-25s-Generates a human-readable file that maps every entry in the Asset List file to the Seed that generated it.\n", GenerateDebugFileFlag);
|
||||
AZ_Printf(AppWindowName, " --%-25s-Allow destructive overwrites of files. Include this arg in automation.\n", AllowOverwritesFlag);
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the game project to use rather than the current default project set in bootsrap.cfg's sys_game_folder.\n", ProjectArg);
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the game project to use rather than the current default project set in bootstrap.cfg's project_path.\n", ProjectArg);
|
||||
}
|
||||
|
||||
void ApplicationManager::OutputHelpComparisonRules()
|
||||
@@ -2781,7 +2761,7 @@ namespace AssetBundler
|
||||
AZ_Printf(AppWindowName, " --%-25s-The Token name of the Comparison Step you wish to use as the second input of this Comparison Step.\n", ComparisonSecondInputArg);
|
||||
AZ_Printf(AppWindowName, "%-31s---Comparison Steps of the ( FilePattern ) type only accept one input Token, and cannot be used with this arg.\n", "");
|
||||
AZ_Printf(AppWindowName, " --%-25s-Outputs the contents of the Comparison Rules file after performing any specified operations.\n", PrintFlag);
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the game project to use rather than the current default project set in bootsrap.cfg's sys_game_folder.\n", ProjectArg);
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the game project to use rather than the current default project set in bootstrap.cfg's project_path.\n", ProjectArg);
|
||||
}
|
||||
|
||||
void ApplicationManager::OutputHelpCompare()
|
||||
@@ -2813,7 +2793,7 @@ namespace AssetBundler
|
||||
AZ_Printf(AppWindowName, "%-31s---All input Asset List files must exist for all specified platforms\n", "");
|
||||
AZ_Printf(AppWindowName, "%-31s---Defaults to all enabled platforms. Platforms can be changed by modifying AssetProcessorPlatformConfig.ini.\n", "");
|
||||
AZ_Printf(AppWindowName, " --%-25s-Allow destructive overwrites of files. Include this arg in automation.\n", AllowOverwritesFlag);
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the game project to use rather than the current default project set in bootsrap.cfg's sys_game_folder.\n", ProjectArg);
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the game project to use rather than the current default project set in bootstrap.cfg's project_path.\n", ProjectArg);
|
||||
}
|
||||
|
||||
void ApplicationManager::OutputHelpBundleSettings()
|
||||
@@ -2829,7 +2809,7 @@ namespace AssetBundler
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the platform(s) referenced by all Bundle Settings operations.\n", PlatformArg);
|
||||
AZ_Printf(AppWindowName, "%-31s---Defaults to all enabled platforms. Platforms can be changed by modifying AssetProcessorPlatformConfig.ini.\n", "");
|
||||
AZ_Printf(AppWindowName, " --%-25s-Outputs the contents of the Bundle Settings file after modifying any specified values.\n", PrintFlag);
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the game project to use rather than the current default project set in bootsrap.cfg's sys_game_folder.\n", ProjectArg);
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the game project to use rather than the current default project set in bootstrap.cfg's project_path.\n", ProjectArg);
|
||||
}
|
||||
|
||||
void ApplicationManager::OutputHelpBundles()
|
||||
@@ -2846,7 +2826,7 @@ namespace AssetBundler
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the platform(s) that will be referenced when generating Bundles.\n", PlatformArg);
|
||||
AZ_Printf(AppWindowName, "%-31s---If no platforms are specified, Bundles will be generated for all available platforms.\n", "");
|
||||
AZ_Printf(AppWindowName, " --%-25s-Allow destructive overwrites of files. Include this arg in automation.\n", AllowOverwritesFlag);
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the game project to use rather than the current default project set in bootsrap.cfg's sys_game_folder.\n", ProjectArg);
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the game project to use rather than the current default project set in bootstrap.cfg's project_path.\n", ProjectArg);
|
||||
}
|
||||
|
||||
void ApplicationManager::OutputHelpBundleSeed()
|
||||
@@ -2854,7 +2834,7 @@ namespace AssetBundler
|
||||
using namespace AzToolsFramework;
|
||||
AZ_Printf(AppWindowName, "\n%-25s-Subcommand for generating bundles directly from seeds. Must provide either (--%s) or (--%s).\n", BundleSeedCommand, BundleSettingsFileArg, OutputBundlePathArg);
|
||||
AZ_Printf(AppWindowName, " --%-25s-Adds the asset to the list of root assets for the specified platform.\n", AddSeedArg);
|
||||
AZ_Printf(AppWindowName, "%-31s---Takes in a cache path to a pre-processed asset. A cache path is a path relative to \"...\\dev\\Cache\\ProjectName\\platform\\projectname\\\"\n", "");
|
||||
AZ_Printf(AppWindowName, "%-31s---Takes in a cache path to a pre-processed asset. A cache path is a path relative to \"ProjectPath\\Cache\\platform\\\"\n", "");
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the Bundle Settings file to operate on by path. Must include (.%s) file extension.\n", BundleSettingsFileArg, AssetBundleSettings::GetBundleSettingsFileExtension());
|
||||
AZ_Printf(AppWindowName, " --%-25s-Sets the path where generated Bundles will be stored. Must include (.%s) file extension.\n", OutputBundlePathArg, AssetBundleSettings::GetBundleFileExtension());
|
||||
AZ_Printf(AppWindowName, " --%-25s-Determines which version of Lumberyard Bundles to generate. Current version is (%i).\n", BundleVersionArg, AzFramework::AssetBundleManifest::CurrentBundleVersion);
|
||||
@@ -2865,7 +2845,7 @@ namespace AssetBundler
|
||||
AZ_Printf(AppWindowName, " --%-25s-Allow destructive overwrites of files. Include this arg in automation.\n", AllowOverwritesFlag);
|
||||
AZ_Printf(AppWindowName, " --%-25s-[Testing] Specifies the Asset Catalog file referenced by all Bundle operations.\n", AssetCatalogFileArg);
|
||||
AZ_Printf(AppWindowName, "%-31s---Designed to be used in Unit Tests.\n", "");
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the game project to use rather than the current default project set in bootsrap.cfg's sys_game_folder.\n", ProjectArg);
|
||||
AZ_Printf(AppWindowName, " --%-25s-Specifies the game project to use rather than the current default project set in bootstrap.cfg's project_path.\n", ProjectArg);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -185,7 +185,7 @@ namespace AssetBundler
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AZStd::string GetCurrentProjectName() { return m_currentProjectName; }
|
||||
AZStd::vector<AzToolsFramework::AssetUtils::GemInfo> GetGemInfoList() { return m_gemInfoList; }
|
||||
AZStd::vector<AzFramework::GemInfo> GetGemInfoList() { return m_gemInfoList; }
|
||||
|
||||
protected:
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -288,7 +288,7 @@ namespace AssetBundler
|
||||
|
||||
AZStd::unique_ptr<AzToolsFramework::AssetSeedManager> m_assetSeedManager;
|
||||
AZStd::unique_ptr<AzToolsFramework::PlatformAddressedAssetCatalogManager> m_platformCatalogManager;
|
||||
AZStd::vector<AzToolsFramework::AssetUtils::GemInfo> m_gemInfoList;
|
||||
AZStd::vector<AzFramework::GemInfo> m_gemInfoList;
|
||||
bool m_showVerboseOutput = false;
|
||||
AZStd::string m_currentProjectName;
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/std/string/regex.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <cctype>
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(4244 4251, "-Wunknown-warning-option")
|
||||
@@ -57,8 +58,8 @@ namespace AssetBundler
|
||||
const char* RemovePlatformFromAllSeedsFlag = "removePlatformFromSeeds";
|
||||
const char* UpdateSeedPathArg = "updateSeedPath";
|
||||
const char* RemoveSeedPathArg = "removeSeedPath";
|
||||
const char* DefaultProjectTemplatePath = "ProjectTemplates/DefaultTemplate/${ProjectName}";
|
||||
const char* ProjectName = "${ProjectName}";
|
||||
const char* DefaultProjectTemplatePath = "Templates/DefaultProject/Template";
|
||||
const char* ProjectName = "${Name}";
|
||||
const char* DependenciesFileSuffix = "_Dependencies";
|
||||
const char* DependenciesFileExtension = "xml";
|
||||
|
||||
@@ -107,8 +108,6 @@ namespace AssetBundler
|
||||
|
||||
const char* AssetCatalogFilename = "assetcatalog.xml";
|
||||
|
||||
char g_cachedEngineRoot[AZ_MAX_PATH_LEN];
|
||||
|
||||
|
||||
const char EngineDirectoryName[] = "Engine";
|
||||
const char RestrictedDirectoryName[] = "restricted";
|
||||
@@ -124,16 +123,15 @@ namespace AssetBundler
|
||||
const AZ::u32 PlatformFlags_RESTRICTED = aznumeric_cast<AZ::u32>(AzFramework::PlatformFlags::Platform_JASPER | AzFramework::PlatformFlags::Platform_PROVO | AzFramework::PlatformFlags::Platform_SALEM);
|
||||
|
||||
void AddPlatformSeeds(
|
||||
AZStd::string rootFolder,
|
||||
const AZ::IO::Path& engineDirectory,
|
||||
const AZStd::string& rootFolderDisplayName,
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string>& defaultSeedLists,
|
||||
AzFramework::PlatformFlags platformFlags)
|
||||
{
|
||||
AZ::IO::FixedMaxPath engineRoot(GetEngineRoot());
|
||||
AZ::IO::FixedMaxPath engineRestrcitedRoot = engineRoot / RestrictedDirectoryName;
|
||||
AZ::IO::FixedMaxPath engineRestrictedRoot = engineRoot / RestrictedDirectoryName;
|
||||
|
||||
AZ::IO::FixedMaxPath inputPath = AZ::IO::FixedMaxPath(rootFolder);
|
||||
AZ::IO::FixedMaxPath engineLocalPath = inputPath.LexicallyRelative(engineRoot);
|
||||
AZ::IO::FixedMaxPath engineLocalPath = AZ::IO::PathView(engineDirectory.LexicallyRelative(engineRoot));
|
||||
|
||||
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
|
||||
auto platformsIdxList = AzFramework::PlatformHelper::GetPlatformIndicesInterpreted(platformFlags);
|
||||
@@ -146,11 +144,11 @@ namespace AssetBundler
|
||||
AZ::IO::FixedMaxPath platformDirectory;
|
||||
if (aznumeric_cast<AZ::u32>(platformFlag) & PlatformFlags_RESTRICTED)
|
||||
{
|
||||
platformDirectory = engineRestrcitedRoot / platformDirName / engineLocalPath;
|
||||
platformDirectory = engineRestrictedRoot / platformDirName / engineLocalPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
platformDirectory = inputPath / PlatformsDirectoryName / platformDirName;
|
||||
platformDirectory = engineDirectory / PlatformsDirectoryName / platformDirName;
|
||||
}
|
||||
|
||||
if (fileIO->Exists(platformDirectory.c_str()))
|
||||
@@ -174,7 +172,7 @@ namespace AssetBundler
|
||||
}
|
||||
|
||||
void AddPlatformsDirectorySeeds(
|
||||
const AZStd::string& rootFolder,
|
||||
const AZ::IO::Path& engineDirectory,
|
||||
const AZStd::string& rootFolderDisplayName,
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string>& defaultSeedLists,
|
||||
AzFramework::PlatformFlags platformFlags)
|
||||
@@ -185,8 +183,7 @@ namespace AssetBundler
|
||||
// Check whether platforms directory exists inside the root, if yes than add
|
||||
// * All seed files from the platforms directory
|
||||
// * All platform specific seed files based on the platform flags specified.
|
||||
AZStd::string platformsDirectory;
|
||||
AzFramework::StringFunc::Path::Join(rootFolder.c_str(), PlatformsDirectoryName, platformsDirectory);
|
||||
auto platformsDirectory = engineDirectory / PlatformsDirectoryName;
|
||||
if (fileIO->Exists(platformsDirectory.c_str()))
|
||||
{
|
||||
fileIO->FindFiles(platformsDirectory.c_str(),
|
||||
@@ -200,119 +197,19 @@ namespace AssetBundler
|
||||
});
|
||||
}
|
||||
|
||||
AddPlatformSeeds(rootFolder, rootFolderDisplayName, defaultSeedLists, platformFlags);
|
||||
AddPlatformSeeds(engineDirectory, rootFolderDisplayName, defaultSeedLists, platformFlags);
|
||||
}
|
||||
}
|
||||
|
||||
bool ComputeEngineRoot()
|
||||
AZ::IO::FixedMaxPath GetEngineRoot()
|
||||
{
|
||||
if (g_cachedEngineRoot[0])
|
||||
AZ::IO::FixedMaxPath engineRootPath;
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
return true;
|
||||
settingsRegistry->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
}
|
||||
|
||||
const char* engineRoot = nullptr;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot);
|
||||
if (!engineRoot)
|
||||
{
|
||||
AZ_Error(AssetBundler::AppWindowName, false, "Unable to locate engine root.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
azstrcpy(g_cachedEngineRoot, AZ_MAX_PATH_LEN, engineRoot);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* GetEngineRoot()
|
||||
{
|
||||
if (!g_cachedEngineRoot[0])
|
||||
{
|
||||
ComputeEngineRoot();
|
||||
}
|
||||
|
||||
return g_cachedEngineRoot;
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> ComputeAssetAliasAndGameName(const AZStd::string& platformIdentifier, const AZStd::string& assetCatalogFile, AZStd::string& assetAlias, AZStd::string& gameName)
|
||||
{
|
||||
AZStd::string assetPath;
|
||||
AZStd::string gameFolder;
|
||||
if (!ComputeEngineRoot())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Unable to compute engine root.\n"));
|
||||
}
|
||||
if (assetCatalogFile.empty())
|
||||
{
|
||||
if (gameName.empty())
|
||||
{
|
||||
bool checkPlatform = false;
|
||||
bool result{};
|
||||
|
||||
auto gameFolderKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/%s",
|
||||
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, AzFramework::AssetSystem::ProjectName);
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
result = settingsRegistry->Get(gameFolder, gameFolderKey);
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Unable to locate game name in bootstrap.\n"));
|
||||
}
|
||||
|
||||
gameName = gameFolder;
|
||||
}
|
||||
else
|
||||
{
|
||||
gameFolder = gameName;
|
||||
}
|
||||
|
||||
// Appending Cache/%gamename%/%platform%/%gameName% to the engine root
|
||||
bool success = AzFramework::StringFunc::Path::ConstructFull(g_cachedEngineRoot, "Cache", assetPath) &&
|
||||
AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), gameFolder.c_str(), assetPath) &&
|
||||
AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), platformIdentifier.c_str(), assetPath);
|
||||
if (success)
|
||||
{
|
||||
AZStd::to_lower(gameFolder.begin(), gameFolder.end());
|
||||
success = AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), gameFolder.c_str(), assetPath); // game name is lowercase
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
assetAlias = assetPath;
|
||||
}
|
||||
}
|
||||
else if (AzFramework::StringFunc::Path::GetFullPath(assetCatalogFile.c_str(), assetPath))
|
||||
{
|
||||
AzFramework::StringFunc::Strip(assetPath, AZ_CORRECT_FILESYSTEM_SEPARATOR, false, false, true);
|
||||
assetAlias = assetPath;
|
||||
|
||||
// 3rd component from reverse should give us the correct case game name because the assetalias directory
|
||||
// looks like ./Cache/%GameName%/%platform%/%gameName%/assetcatalog.xml
|
||||
// GetComponent util method returns the component with the separator appended at the end
|
||||
// therefore we need to strip the separator to get the game name string
|
||||
gameFolder = AZ::IO::PathView(assetPath).ParentPath().ParentPath().Filename().Native();
|
||||
if (gameFolder.empty())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Unable to retrieve game name from assetCatalog file (%s).\n", assetCatalogFile.c_str()));
|
||||
}
|
||||
|
||||
if (!AzFramework::StringFunc::Strip(gameFolder, AZ_CORRECT_FILESYSTEM_SEPARATOR, false, false, true))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Unable to strip separator from game name (%s).\n", gameFolder.c_str()));
|
||||
}
|
||||
|
||||
if (!gameName.empty() && !AzFramework::StringFunc::Equal(gameFolder.c_str(), gameName.c_str()))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Game name retrieved from the assetCatalog file (%s) does not match the inputted game name (%s).\n", gameFolder.c_str(), gameName.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
gameName = gameFolder;
|
||||
}
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
return engineRootPath;
|
||||
}
|
||||
|
||||
void AddPlatformIdentifier(AZStd::string& filePath, const AZStd::string& platformIdentifier)
|
||||
@@ -346,7 +243,8 @@ namespace AssetBundler
|
||||
return platformFlags;
|
||||
}
|
||||
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> GetDefaultSeedListFiles(const char* root, const char* projectName, const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlag)
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> GetDefaultSeedListFiles(AZStd::string_view enginePath, AZStd::string_view projectPath,
|
||||
const AZStd::vector<AzFramework::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlag)
|
||||
{
|
||||
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
|
||||
AZ_Assert(fileIO, "AZ::IO::FileIOBase must be ready for use.\n");
|
||||
@@ -354,63 +252,65 @@ namespace AssetBundler
|
||||
// Add all seed list files of enabled gems for the given project
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> defaultSeedLists = GetGemSeedListFilePathToGemNameMap(gemInfoList, platformFlag);
|
||||
|
||||
|
||||
// Add the engine seed list file
|
||||
AZStd::string engineDirectory;
|
||||
AzFramework::StringFunc::Path::Join(root, EngineDirectoryName, engineDirectory);
|
||||
AZStd::string absoluteEngineSeedFilePath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(engineDirectory.c_str(), EngineSeedFileName, AzToolsFramework::AssetSeedManager::GetSeedFileExtension(), absoluteEngineSeedFilePath, true);
|
||||
AZ::IO::Path engineDirectory = AZ::IO::Path(enginePath) / EngineDirectoryName;
|
||||
auto absoluteEngineSeedFilePath = engineDirectory / EngineSeedFileName;
|
||||
absoluteEngineSeedFilePath.ReplaceExtension(AzToolsFramework::AssetSeedManager::GetSeedFileExtension());
|
||||
if (fileIO->Exists(absoluteEngineSeedFilePath.c_str()))
|
||||
{
|
||||
defaultSeedLists[absoluteEngineSeedFilePath] = EngineDirectoryName;
|
||||
defaultSeedLists[absoluteEngineSeedFilePath.Native()] = EngineDirectoryName;
|
||||
}
|
||||
|
||||
// Add Seed Lists from the Platforms directory
|
||||
Internal::AddPlatformsDirectorySeeds(engineDirectory, EngineDirectoryName, defaultSeedLists, platformFlag);
|
||||
|
||||
|
||||
AZStd::string absoluteProjectDefaultSeedFilePath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(root, projectName, EngineSeedFileName, AzToolsFramework::AssetSeedManager::GetSeedFileExtension(), absoluteProjectDefaultSeedFilePath, true);
|
||||
auto absoluteProjectDefaultSeedFilePath = AZ::IO::Path(projectPath) / EngineSeedFileName;
|
||||
absoluteProjectDefaultSeedFilePath.ReplaceExtension(AzToolsFramework::AssetSeedManager::GetSeedFileExtension());
|
||||
|
||||
if (fileIO->Exists(absoluteProjectDefaultSeedFilePath.c_str()))
|
||||
{
|
||||
defaultSeedLists[absoluteProjectDefaultSeedFilePath] = projectName;
|
||||
defaultSeedLists[absoluteProjectDefaultSeedFilePath.Native()] = projectPath;
|
||||
}
|
||||
|
||||
return defaultSeedLists;
|
||||
}
|
||||
|
||||
AZStd::vector<AZStd::string> GetDefaultSeeds(const char* root, const char* projectName)
|
||||
AZStd::vector<AZStd::string> GetDefaultSeeds(AZStd::string_view enginePath, AZStd::string_view projectPath, AZStd::string_view projectName)
|
||||
{
|
||||
AZStd::vector<AZStd::string> defaultSeeds;
|
||||
|
||||
defaultSeeds.emplace_back(GetProjectDependenciesAssetPath(root, projectName));
|
||||
defaultSeeds.emplace_back(GetProjectDependenciesAssetPath(enginePath, projectPath, projectName));
|
||||
|
||||
return defaultSeeds;
|
||||
}
|
||||
|
||||
AZStd::string GetProjectDependenciesFile(const char* root, const char* projectName)
|
||||
AZ::IO::Path GetProjectDependenciesFile(AZStd::string_view projectPath, AZStd::string_view projectName)
|
||||
{
|
||||
AZStd::string projectDependenciesFilePath = AZStd::string::format("%s%s", projectName, DependenciesFileSuffix);
|
||||
AzFramework::StringFunc::Path::ConstructFull(root, projectName, projectDependenciesFilePath.c_str(), DependenciesFileExtension, projectDependenciesFilePath, true);
|
||||
return projectDependenciesFilePath;
|
||||
AZ::IO::Path projectDependenciesFilePath = projectPath;
|
||||
projectDependenciesFilePath /= AZStd::string::format("%.*s%s", aznumeric_cast<int>(projectName.size()), projectName.data(),
|
||||
DependenciesFileSuffix);
|
||||
projectDependenciesFilePath.ReplaceExtension(DependenciesFileExtension);
|
||||
return projectDependenciesFilePath.LexicallyNormal();
|
||||
}
|
||||
|
||||
AZStd::string GetProjectDependenciesFileTemplate(const char* root)
|
||||
AZ::IO::Path GetProjectDependenciesFileTemplate(AZStd::string_view engineRoot)
|
||||
{
|
||||
AZStd::string projectDependenciesFileTemplate = ProjectName;
|
||||
projectDependenciesFileTemplate += DependenciesFileSuffix;
|
||||
AzFramework::StringFunc::Path::ConstructFull(root, DefaultProjectTemplatePath, projectDependenciesFileTemplate.c_str(), DependenciesFileExtension, projectDependenciesFileTemplate, true);
|
||||
return projectDependenciesFileTemplate;
|
||||
AZ::IO::Path projectDependenciesFileTemplate = engineRoot;
|
||||
projectDependenciesFileTemplate /= DefaultProjectTemplatePath;
|
||||
projectDependenciesFileTemplate /= AZStd::string::format("%s%s", ProjectName, DependenciesFileSuffix);
|
||||
projectDependenciesFileTemplate.ReplaceExtension(DependenciesFileExtension);
|
||||
return projectDependenciesFileTemplate.LexicallyNormal();
|
||||
}
|
||||
|
||||
AZStd::string GetProjectDependenciesAssetPath(const char* root, const char* projectName)
|
||||
AZ::IO::Path GetProjectDependenciesAssetPath(AZStd::string_view enginePath, AZStd::string_view projectPath, AZStd::string_view projectName)
|
||||
{
|
||||
AZStd::string projectDependenciesFile = AZStd::move(GetProjectDependenciesFile(root, projectName));
|
||||
AZ::IO::Path projectDependenciesFile = GetProjectDependenciesFile(projectPath, projectName);
|
||||
if (!AZ::IO::FileIOBase::GetInstance()->Exists(projectDependenciesFile.c_str()))
|
||||
{
|
||||
AZ_TracePrintf(AssetBundler::AppWindowName, "Project dependencies file %s doesn't exist.\n", projectDependenciesFile.c_str());
|
||||
|
||||
AZStd::string projectDependenciesFileTemplate = AZStd::move(GetProjectDependenciesFileTemplate(root));
|
||||
AZ::IO::Path projectDependenciesFileTemplate = GetProjectDependenciesFileTemplate(enginePath);
|
||||
if (AZ::IO::FileIOBase::GetInstance()->Copy(projectDependenciesFileTemplate.c_str(), projectDependenciesFile.c_str()))
|
||||
{
|
||||
AZ_TracePrintf(AssetBundler::AppWindowName, "Copied project dependencies file template %s to the current project.\n",
|
||||
@@ -425,37 +325,39 @@ namespace AssetBundler
|
||||
}
|
||||
|
||||
// Turn the absolute path into a cache-relative path
|
||||
AZStd::string relativeProductPath;
|
||||
AzFramework::StringFunc::Path::GetFullFileName(projectDependenciesFile.c_str(), relativeProductPath);
|
||||
AZStd::to_lower(relativeProductPath.begin(), relativeProductPath.end());
|
||||
AZ::IO::Path relativeProductPath = projectDependenciesFile.Filename().Native();
|
||||
AZStd::to_lower(relativeProductPath.Native().begin(), relativeProductPath.Native().end());
|
||||
|
||||
return relativeProductPath;
|
||||
}
|
||||
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> GetGemSeedListFilePathToGemNameMap(const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlags)
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> GetGemSeedListFilePathToGemNameMap(const AZStd::vector<AzFramework::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlags)
|
||||
{
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> filePathToGemNameMap;
|
||||
for (const AzToolsFramework::AssetUtils::GemInfo& gemInfo : gemInfoList)
|
||||
for (const AzFramework::GemInfo& gemInfo : gemInfoList)
|
||||
{
|
||||
AZ::IO::Path gemInfoAssetFilePath = gemInfo.m_absoluteFilePath;
|
||||
gemInfoAssetFilePath /= gemInfo.GetGemAssetFolder();
|
||||
AZ::IO::Path absoluteGemSeedFilePath = gemInfoAssetFilePath / GemsSeedFileName;
|
||||
absoluteGemSeedFilePath.ReplaceExtension(AZ::IO::PathView{ AzToolsFramework::AssetSeedManager::GetSeedFileExtension() });
|
||||
absoluteGemSeedFilePath = absoluteGemSeedFilePath.LexicallyNormal();
|
||||
|
||||
AZStd::string gemName = gemInfo.m_gemName + " Gem";
|
||||
if (AZ::IO::FileIOBase::GetInstance()->Exists(absoluteGemSeedFilePath.c_str()))
|
||||
for (const AZ::IO::Path& gemAbsoluteSourcePath : gemInfo.m_absoluteSourcePaths)
|
||||
{
|
||||
filePathToGemNameMap[absoluteGemSeedFilePath.Native()] = gemName;
|
||||
}
|
||||
AZ::IO::Path gemInfoAssetFilePath = gemAbsoluteSourcePath;
|
||||
gemInfoAssetFilePath /= gemInfo.GetGemAssetFolder();
|
||||
AZ::IO::Path absoluteGemSeedFilePath = gemInfoAssetFilePath / GemsSeedFileName;
|
||||
absoluteGemSeedFilePath.ReplaceExtension(AZ::IO::PathView{ AzToolsFramework::AssetSeedManager::GetSeedFileExtension() });
|
||||
absoluteGemSeedFilePath = absoluteGemSeedFilePath.LexicallyNormal();
|
||||
|
||||
Internal::AddPlatformsDirectorySeeds(gemInfoAssetFilePath.Native(), gemName, filePathToGemNameMap, platformFlags);
|
||||
AZStd::string gemName = gemInfo.m_gemName + " Gem";
|
||||
if (AZ::IO::FileIOBase::GetInstance()->Exists(absoluteGemSeedFilePath.c_str()))
|
||||
{
|
||||
filePathToGemNameMap[absoluteGemSeedFilePath.Native()] = gemName;
|
||||
}
|
||||
|
||||
Internal::AddPlatformsDirectorySeeds(gemInfoAssetFilePath.Native(), gemName, filePathToGemNameMap, platformFlags);
|
||||
}
|
||||
}
|
||||
|
||||
return filePathToGemNameMap;
|
||||
}
|
||||
|
||||
bool IsGemSeedFilePathValid(const char* root, AZStd::string seedAbsoluteFilePath, const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlags)
|
||||
bool IsGemSeedFilePathValid(AZStd::string_view engineRoot, AZStd::string seedAbsoluteFilePath, const AZStd::vector<AzFramework::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlags)
|
||||
{
|
||||
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
|
||||
AZ_Assert(fileIO, "AZ::IO::FileIOBase must be ready for use.\n");
|
||||
@@ -465,9 +367,8 @@ namespace AssetBundler
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::IO::Path gemsFolder{ root };
|
||||
AZ::IO::Path gemsFolder{ engineRoot };
|
||||
gemsFolder /= GemsDirectoryName;
|
||||
gemsFolder /= GemsAssetsDirectoryName;
|
||||
gemsFolder = gemsFolder.LexicallyNormal();
|
||||
if (!AzFramework::StringFunc::StartsWith(seedAbsoluteFilePath, gemsFolder.Native()))
|
||||
{
|
||||
@@ -476,36 +377,45 @@ namespace AssetBundler
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const AzToolsFramework::AssetUtils::GemInfo& gemInfo : gemInfoList)
|
||||
for (const AzFramework::GemInfo& gemInfo : gemInfoList)
|
||||
{
|
||||
// We want to check the path before going through the effort of creating the default Seed List file map
|
||||
if (!AzFramework::StringFunc::StartsWith(seedAbsoluteFilePath, gemInfo.m_absoluteFilePath))
|
||||
for (const AZ::IO::Path& gemAbsoluteSourcePath : gemInfo.m_absoluteSourcePaths)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// We want to check the path before going through the effort of creating the default Seed List file map
|
||||
if (!AzFramework::StringFunc::StartsWith(seedAbsoluteFilePath, gemAbsoluteSourcePath.Native()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> seeds = GetGemSeedListFilePathToGemNameMap({gemInfo}, platformFlags);
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> seeds = GetGemSeedListFilePathToGemNameMap({ gemInfo }, platformFlags);
|
||||
|
||||
if (seeds.find(seedAbsoluteFilePath) != seeds.end())
|
||||
{
|
||||
return true;
|
||||
if (seeds.find(seedAbsoluteFilePath) != seeds.end())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// If we have not validated the input path yet, we need to keep looking, or we will return false negatives
|
||||
// for Gems that have the same prefix in their name
|
||||
}
|
||||
// If we have not validated the input path yet, we need to keep looking, or we will return false negatives
|
||||
// for Gems that have the same prefix in their name
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AzFramework::PlatformFlags GetEnabledPlatformFlags(const char* root, const char* assetRoot, const char* gameName)
|
||||
AzFramework::PlatformFlags GetEnabledPlatformFlags(AZStd::string_view engineRoot, AZStd::string_view assetRoot, AZStd::string_view projectPath)
|
||||
{
|
||||
QStringList configFiles = AzToolsFramework::AssetUtils::GetConfigFiles(root, assetRoot, gameName, true, true);
|
||||
|
||||
QStringList enabaledPlatformList = AzToolsFramework::AssetUtils::GetEnabledPlatforms(configFiles);
|
||||
AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_NONE;
|
||||
for (const QString& enabledPlatform : enabaledPlatformList)
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
if (settingsRegistry == nullptr)
|
||||
{
|
||||
AzFramework::PlatformFlags platformFlag = AzFramework::PlatformHelper::GetPlatformFlag(enabledPlatform.toUtf8().data());
|
||||
AZ_Error(AssetBundler::AppWindowName, false, "Settings Registry is not available, enabled platform flags cannot be queried");
|
||||
return AzFramework::PlatformFlags::Platform_NONE;
|
||||
}
|
||||
|
||||
auto configFiles = AzToolsFramework::AssetUtils::GetConfigFiles(engineRoot, assetRoot, projectPath, true, true, settingsRegistry);
|
||||
auto enabledPlatformList = AzToolsFramework::AssetUtils::GetEnabledPlatforms(*settingsRegistry, configFiles);
|
||||
AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_NONE;
|
||||
for (const auto& enabledPlatform : enabledPlatformList)
|
||||
{
|
||||
AzFramework::PlatformFlags platformFlag = AzFramework::PlatformHelper::GetPlatformFlag(enabledPlatform);
|
||||
|
||||
if (platformFlag != AzFramework::PlatformFlags::Platform_NONE)
|
||||
{
|
||||
@@ -513,7 +423,7 @@ namespace AssetBundler
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning(AssetBundler::AppWindowName, false, "Platform Helper is not aware of the platform (%s).\n ", enabledPlatform.toUtf8().data());
|
||||
AZ_Warning(AssetBundler::AppWindowName, false, "Platform Helper is not aware of the platform (%s).\n ", enabledPlatform.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -535,68 +445,60 @@ namespace AssetBundler
|
||||
|
||||
AZ::Outcome<AZStd::string, AZStd::string> GetCurrentProjectName()
|
||||
{
|
||||
AZStd::string gameName;
|
||||
bool result{ false };
|
||||
auto gameFolderKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/%s",
|
||||
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, AzFramework::AssetSystem::ProjectName);
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
AZStd::string projectName{ AZStd::string_view(AZ::Utils::GetProjectName()) };
|
||||
if (!projectName.empty())
|
||||
{
|
||||
result = settingsRegistry->Get(gameName, gameFolderKey);
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
return AZ::Success(gameName);
|
||||
return AZ::Success(projectName);
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Unable to locate current project name in bootstrap.cfg"));
|
||||
return AZ::Failure(AZStd::string("Unable to obtain current project name from registry"));
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Outcome<AZStd::string, AZStd::string> GetProjectFolderPath(const AZStd::string& engineRoot, const AZStd::string& projectName)
|
||||
AZ::Outcome<AZ::IO::Path, AZStd::string> GetProjectFolderPath()
|
||||
{
|
||||
AZStd::string projectFolderPath;
|
||||
bool success = AzFramework::StringFunc::Path::ConstructFull(engineRoot.c_str(), projectName.c_str(), projectFolderPath);
|
||||
|
||||
if (success && AZ::IO::FileIOBase::GetInstance()->Exists(projectFolderPath.c_str()))
|
||||
AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath();
|
||||
if (!projectPath.empty())
|
||||
{
|
||||
return AZ::Success(projectFolderPath);
|
||||
return AZ::Success(AZ::IO::Path{ AZ::IO::PathView(projectPath) });
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format( "Unable to locate the current Project folder: %s", projectName.c_str()));
|
||||
return AZ::Failure(AZStd::string::format("Unable to obtain current project path from registry"));
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Outcome<AZStd::string, AZStd::string> GetProjectCacheFolderPath(const AZStd::string& engineRoot, const AZStd::string& projectName)
|
||||
AZ::Outcome<AZ::IO::Path, AZStd::string> GetProjectCacheFolderPath()
|
||||
{
|
||||
AZStd::string projectCacheFolderPath;
|
||||
AZ::IO::Path projectCacheFolderPath;
|
||||
|
||||
bool success = AzFramework::StringFunc::Path::ConstructFull(engineRoot.c_str(), "Cache", projectCacheFolderPath);
|
||||
if (!success || !AZ::IO::FileIOBase::GetInstance()->Exists(projectCacheFolderPath.c_str()))
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
if (settingsRegistry && settingsRegistry->Get(projectCacheFolderPath.Native(),
|
||||
AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Unable to locate the Cache in the engine directory: %s. Please run the Lumberyard Asset Processor to generate a Cache and build assets.",
|
||||
engineRoot.c_str()));
|
||||
if (AZ::IO::FileIOBase::GetInstance()->Exists(projectCacheFolderPath.c_str()))
|
||||
{
|
||||
return AZ::Success(projectCacheFolderPath);
|
||||
}
|
||||
}
|
||||
|
||||
success = AzFramework::StringFunc::Path::ConstructFull(projectCacheFolderPath.c_str(), projectName.c_str(), projectCacheFolderPath);
|
||||
if (success && AZ::IO::FileIOBase::GetInstance()->Exists(projectCacheFolderPath.c_str()))
|
||||
{
|
||||
return AZ::Success(projectCacheFolderPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Unable to locate the current Project in the Cache folder: %s. Please run the Lumberyard Asset Processor to generate a Cache and build assets.",
|
||||
projectName.c_str()));
|
||||
}
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Unable to locate the Project Cache path from Settings Registry at key %s."
|
||||
" Please run the Lumberyard Asset Processor to generate a Cache and build assets.",
|
||||
AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder));
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> GetPlatformNamesFromCacheFolder(const AZStd::string& projectCacheFolder, AZStd::vector<AZStd::string>& platformNames)
|
||||
AZ::Outcome<void, AZStd::string> GetPlatformNamesFromCacheFolder(AZStd::vector<AZStd::string>& platformNames)
|
||||
{
|
||||
QDir projectCacheDir(QString(projectCacheFolder.c_str()));
|
||||
AZ::Outcome<AZ::IO::Path, AZStd::string> projectCacheRootFolder = GetProjectCacheFolderPath();
|
||||
if (!projectCacheRootFolder)
|
||||
{
|
||||
return AZ::Failure(projectCacheRootFolder.TakeError());
|
||||
}
|
||||
|
||||
const AZStd::string& projectCacheRootPath = projectCacheRootFolder.GetValue().Native();
|
||||
QDir projectCacheDir(QString::fromUtf8(projectCacheRootPath.c_str(), aznumeric_cast<int>(projectCacheRootPath.size())));
|
||||
auto tempPlatformList = projectCacheDir.entryList(QDir::Filter::Dirs | QDir::Filter::NoDotAndDotDot);
|
||||
|
||||
if (tempPlatformList.empty())
|
||||
@@ -612,66 +514,33 @@ namespace AssetBundler
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
AZ::Outcome<AZStd::string, AZStd::string> GetAssetCatalogFilePath(const char* pathToCacheFolder, const char* platformIdentifier, const char* projectName)
|
||||
AZ::Outcome<AZ::IO::Path, AZStd::string> GetAssetCatalogFilePath()
|
||||
{
|
||||
AZStd::string assetCatalogFilePath;
|
||||
bool success = AzFramework::StringFunc::Path::ConstructFull(pathToCacheFolder, platformIdentifier, assetCatalogFilePath, true);
|
||||
|
||||
if (!success)
|
||||
AZ::IO::Path assetCatalogFilePath = GetPlatformSpecificCacheFolderPath();
|
||||
if (assetCatalogFilePath.empty())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Unable to find platform folder %s in cache found at: %s. Please run the Lumberyard Asset Processor to generate platform-specific cache folders and build assets.",
|
||||
platformIdentifier,
|
||||
pathToCacheFolder));
|
||||
}
|
||||
|
||||
// Project name is lower case in the platform-specific cache folder
|
||||
AZStd::string lowerCaseProjectName = AZStd::string(projectName);
|
||||
AZStd::to_lower(lowerCaseProjectName.begin(), lowerCaseProjectName.end());
|
||||
success = AzFramework::StringFunc::Path::ConstructFull(assetCatalogFilePath.c_str(), lowerCaseProjectName.c_str(), assetCatalogFilePath)
|
||||
&& AzFramework::StringFunc::Path::ConstructFull(assetCatalogFilePath.c_str(), AssetCatalogFilename, assetCatalogFilePath);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Unable to find the asset catalog. Please run the Lumberyard Asset Processor to generate a Cache and build assets."));
|
||||
"Unable to retrieve cache platform path from Settings Registry at key: %s. Please run the Lumberyard Asset Processor to generate platform-specific cache folders and build assets.",
|
||||
AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder));
|
||||
}
|
||||
|
||||
assetCatalogFilePath /= AssetCatalogFilename;
|
||||
return AZ::Success(assetCatalogFilePath);
|
||||
}
|
||||
|
||||
AZStd::string GetPlatformSpecificCacheFolderPath(const AZStd::string& projectSpecificCacheFolderAbsolutePath, const AZStd::string& platform, const AZStd::string& projectName)
|
||||
AZ::IO::Path GetPlatformSpecificCacheFolderPath()
|
||||
{
|
||||
// C:/dev/Cache/ProjectName -> C:/dev/Cache/ProjectName/platform
|
||||
AZStd::string platformSpecificCacheFolderPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(projectSpecificCacheFolderAbsolutePath.c_str(), platform.c_str(), platformSpecificCacheFolderPath, true);
|
||||
|
||||
// C:/dev/Cache/ProjectName/platform -> C:/dev/Cache/ProjectName/platform/projectname
|
||||
AZStd::string lowerCaseProjectName = AZStd::string(projectName);
|
||||
AZStd::to_lower(lowerCaseProjectName.begin(), lowerCaseProjectName.end());
|
||||
AzFramework::StringFunc::Path::ConstructFull(platformSpecificCacheFolderPath.c_str(), lowerCaseProjectName.c_str(), platformSpecificCacheFolderPath, true);
|
||||
|
||||
AZ::IO::Path platformSpecificCacheFolderPath;
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
settingsRegistry->Get(platformSpecificCacheFolderPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder);
|
||||
}
|
||||
return platformSpecificCacheFolderPath;
|
||||
}
|
||||
|
||||
AZStd::string GenerateKeyFromAbsolutePath(const AZStd::string& absoluteFilePath)
|
||||
void ConvertToRelativePath(AZStd::string_view parentFolderPath, AZStd::string& absoluteFilePath)
|
||||
{
|
||||
AZStd::string key(absoluteFilePath);
|
||||
AzFramework::StringFunc::Path::Normalize(key);
|
||||
AzFramework::StringFunc::Path::StripDrive(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
void ConvertToRelativePath(const AZStd::string& parentFolderPath, AZStd::string& absoluteFilePath)
|
||||
{
|
||||
// Qt and AZ return different Drive Letter formats, so strip them away before doing a comparison
|
||||
AZStd::string parentFolderPathWithoutDrive(parentFolderPath);
|
||||
AzFramework::StringFunc::Path::StripDrive(parentFolderPathWithoutDrive);
|
||||
AzFramework::StringFunc::Path::Normalize(parentFolderPathWithoutDrive);
|
||||
|
||||
AzFramework::StringFunc::Path::StripDrive(absoluteFilePath);
|
||||
AzFramework::StringFunc::Path::Normalize(absoluteFilePath);
|
||||
|
||||
AzFramework::StringFunc::Replace(absoluteFilePath, parentFolderPathWithoutDrive.c_str(), "");
|
||||
absoluteFilePath = AZ::IO::PathView(absoluteFilePath).LexicallyRelative(parentFolderPath).String();
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> MakePath(const AZStd::string& path)
|
||||
|
||||
@@ -120,20 +120,8 @@ namespace AssetBundler
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
extern const char* AssetCatalogFilename;
|
||||
extern char g_cachedEngineRoot[AZ_MAX_PATH_LEN];
|
||||
static const size_t MaxErrorMessageLength = 4096;
|
||||
|
||||
//! This struct stores gem related information
|
||||
struct GemInfo
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR(GemInfo, AZ::SystemAllocator, 0);
|
||||
GemInfo(AZStd::string name, AZStd::string relativeFilePath, AZStd::string absoluteFilePath);
|
||||
GemInfo() = default;
|
||||
AZStd::string m_gemName;
|
||||
AZStd::string m_relativeFilePath;
|
||||
AZStd::string m_absoluteFilePath;
|
||||
};
|
||||
|
||||
|
||||
// The Warning Absorber is used to absorb warnings
|
||||
// One case that this is being used is during loading of the asset catalog.
|
||||
@@ -149,14 +137,8 @@ namespace AssetBundler
|
||||
bool OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message) override;
|
||||
};
|
||||
|
||||
// computes the asset alias and game name either through the asset catalog file provided by the user or using the platform and game folder
|
||||
AZ::Outcome<void, AZStd::string> ComputeAssetAliasAndGameName(const AZStd::string& platformIdentifier, const AZStd::string& assetCatalogFile, AZStd::string& assetAlias, AZStd::string& gameName);
|
||||
|
||||
// Computes the engine root and cache it locally
|
||||
bool ComputeEngineRoot();
|
||||
|
||||
// Retrurns the engine root
|
||||
const char* GetEngineRoot();
|
||||
// Returns the engine root
|
||||
AZ::IO::FixedMaxPath GetEngineRoot();
|
||||
|
||||
/**
|
||||
* Determines the name of the currently enabled game project
|
||||
@@ -166,58 +148,51 @@ namespace AssetBundler
|
||||
|
||||
|
||||
/**
|
||||
* Constructs an absolute path to the project folder found at: dev/ProjectName
|
||||
* @param engineRoot The absolute path of the dev/ folder
|
||||
* @param projectName A project present in the dev/ folder
|
||||
* Retrieve the project path from the Settings Registry
|
||||
* @return Absolute path of the Project Folder on success, error message on failure
|
||||
*/
|
||||
AZ::Outcome<AZStd::string, AZStd::string> GetProjectFolderPath(const AZStd::string& engineRoot, const AZStd::string& projectName);
|
||||
AZ::Outcome<AZ::IO::Path, AZStd::string> GetProjectFolderPath();
|
||||
|
||||
/**
|
||||
* Constructs an absolute path to the project-specific cache folder found at: dev/Cache/ProjectName
|
||||
* @param engineRoot The absolute path of the dev/ folder
|
||||
* @param projectName A project present in the dev/ folder
|
||||
* Retrieve the project path from the Settings Registry
|
||||
* @return Absolute path of the project-specific cache folder on success, error message on failure
|
||||
*/
|
||||
AZ::Outcome<AZStd::string, AZStd::string> GetProjectCacheFolderPath(const AZStd::string& engineRoot, const AZStd::string& projectName);
|
||||
AZ::Outcome<AZ::IO::Path, AZStd::string> GetProjectCacheFolderPath();
|
||||
|
||||
/**
|
||||
* Calculates the list of enabled platforms for the input project by reading the folder names inside the project-specific cache folder.
|
||||
* If the Asset Processor has not been run yet, or has not been run since the enabled platform list inside AssetProcessorPlatformConfig.ini
|
||||
* If the Asset Processor has not been run yet, or has not been run since the enabled platform list inside AssetProcessorPlatformConfig.setreg
|
||||
* was changed, the output of this function will be incorrect.
|
||||
*
|
||||
* @param projectCacheFolder The directory of a project-specific cache folder: dev/Cache/ProjectName
|
||||
* @param projectCacheFolder The directory of a project-specific cache folder: /ProjectPath/Cache
|
||||
* @param platformNames [out] The list of platforms enabled in the project
|
||||
* @return void on success, error message on failure
|
||||
*/
|
||||
AZ::Outcome<void, AZStd::string> GetPlatformNamesFromCacheFolder(const AZStd::string& projectCacheFolder, AZStd::vector<AZStd::string>& platformNames);
|
||||
AZ::Outcome<void, AZStd::string> GetPlatformNamesFromCacheFolder(AZStd::vector<AZStd::string>& platformNames);
|
||||
|
||||
/**
|
||||
* Computes the absolute path to the Asset Catalog file for a specified project and platform.
|
||||
* With platform set as "pc" and project as "ProjectName", the path will resemble: C:/dev/Cache/ProjectName/pc/projectname/assetcatalog.xml
|
||||
* With platform set as "pc" and project as "ProjectName", the path will resemble: C:/ProjectPath/Cache/pc/assetcatalog.xml
|
||||
*
|
||||
* @param pathToCacheFolder The absolute path to the Cache folder. ex: C:/dev/Cache
|
||||
* @param pathToCacheFolder The absolute path to the Cache folder. ex: C:/ProjectPath/Cache
|
||||
* @param platformIdentifier The platform identifier of the desired Asset Catalog. Valid inputs can be found by reading the folder names
|
||||
* found inside dev/Cache/ProjectName
|
||||
* found inside ProjectPath/Cache
|
||||
* @param projectName The name of the project you want to search
|
||||
* @return Absolute Path to the Asset Catalog file on success, error message on failure
|
||||
*/
|
||||
AZ::Outcome<AZStd::string, AZStd::string> GetAssetCatalogFilePath(const char* pathToCacheFolder, const char* platformIdentifier, const char* projectName);
|
||||
AZ::Outcome<AZ::IO::Path, AZStd::string> GetAssetCatalogFilePath();
|
||||
|
||||
/**
|
||||
* Computes the absolute path to the platform-specific Cache folder where product assets are stored.
|
||||
* With platform set as "pc" and project as "ProjectName", the path will resemble: C:/dev/Cache/ProjectName/pc/projectname/
|
||||
* With platform set as "pc" the path will resemble: C:/ProjectPath/Cache/pc/projectname/
|
||||
*
|
||||
* @param projectSpecificCacheFolderAbsolutePath The absolute path to the Cache folder. Example: C:/dev/Cache/ProjectName
|
||||
* @param projectSpecificCacheFolderAbsolutePath The absolute path to the Cache folder. Example: C:/ProjectPath/Cache
|
||||
* @param platform the platform of the desired cache location
|
||||
* @param projectName The name of the current project
|
||||
* @return Absolute path to the platform-specific Cache folder where product assets are stored
|
||||
*/
|
||||
AZStd::string GetPlatformSpecificCacheFolderPath(const AZStd::string& projectSpecificCacheFolderAbsolutePath, const AZStd::string& platform, const AZStd::string& projectName);
|
||||
AZ::IO::Path GetPlatformSpecificCacheFolderPath();
|
||||
|
||||
AZStd::string GenerateKeyFromAbsolutePath(const AZStd::string& absoluteFilePath);
|
||||
|
||||
void ConvertToRelativePath(const AZStd::string& parentFolderPath, AZStd::string& absoluteFilePath);
|
||||
void ConvertToRelativePath(AZStd::string_view parentFolderPath, AZStd::string& absoluteFilePath);
|
||||
|
||||
AZ::Outcome<void, AZStd::string> MakePath(const AZStd::string& path);
|
||||
|
||||
@@ -228,30 +203,31 @@ namespace AssetBundler
|
||||
AzFramework::PlatformFlags GetPlatformsOnDiskForPlatformSpecificFile(const AZStd::string& platformIndependentAbsolutePath);
|
||||
|
||||
//! Returns a map of <absolute file path, source folder display name> of all default Seed List files for the current game project.
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> GetDefaultSeedListFiles(const char* root, const char* projectName, const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlags);
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> GetDefaultSeedListFiles(AZStd::string_view enginePath, AZStd::string_view projectPath,
|
||||
const AZStd::vector<AzFramework::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlags);
|
||||
|
||||
//! Returns a vector of relative paths to Assets that should be included as default Seeds, but are not already in a Seed List file.
|
||||
AZStd::vector<AZStd::string> GetDefaultSeeds(const char* root, const char* projectName);
|
||||
AZStd::vector<AZStd::string> GetDefaultSeeds(AZStd::string_view enginePath, AZStd::string_view projectPath, AZStd::string_view projectName);
|
||||
|
||||
//! Returns the absolute path of {ProjectName}_Dependencies.xml
|
||||
AZStd::string GetProjectDependenciesFile(const char* root, const char* projectName);
|
||||
AZ::IO::Path GetProjectDependenciesFile(AZStd::string_view productPath, AZStd::string_view projectName);
|
||||
|
||||
//! Returns the absolute path of the project dependencies file in the default project template
|
||||
AZStd::string GetProjectDependenciesFileTemplate(const char* root);
|
||||
AZ::IO::Path GetProjectDependenciesFileTemplate(AZStd::string_view enginePath);
|
||||
|
||||
//! Creates the ProjectName_Dependencies.xml file if it does not exist, and adds returns the relative path to the asset in the Cache.
|
||||
AZStd::string GetProjectDependenciesAssetPath(const char* root, const char* projectName);
|
||||
AZ::IO::Path GetProjectDependenciesAssetPath(AZStd::string_view enginePath, AZStd::string_view projectPath, AZStd::string_view projectName);
|
||||
|
||||
//! Returns the map from gem seed list file path to gem name
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> GetGemSeedListFilePathToGemNameMap(const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlags);
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> GetGemSeedListFilePathToGemNameMap(const AZStd::vector<AzFramework::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlags);
|
||||
|
||||
//! Given an absolute gem seed file path determines whether the file is valid for the current game project.
|
||||
//! This method is for validating gem seed list files only.
|
||||
bool IsGemSeedFilePathValid(const char* root, AZStd::string seedAbsoluteFilePath, const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlags);
|
||||
bool IsGemSeedFilePathValid(AZStd::string_view enginePath, AZStd::string seedAbsoluteFilePath, const AZStd::vector<AzFramework::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlags);
|
||||
|
||||
//! Returns platformFlags of all enabled platforms by parsing all the asset processor config files.
|
||||
//! Please note that the game project could be in a different location to the engine therefore we need the assetRoot param.
|
||||
AzFramework::PlatformFlags GetEnabledPlatformFlags(const char* root, const char* assetRoot, const char* gameName);
|
||||
AzFramework::PlatformFlags GetEnabledPlatformFlags(AZStd::string_view enginePath, AZStd::string_view assetRoot, AZStd::string_view projectPath);
|
||||
|
||||
//! Filepath is a helper class that is used to find the absolute path of a file
|
||||
//! if the inputted file path is an absolute path than it does nothing
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"GemListFormatVersion": 2,
|
||||
"Gems": [
|
||||
{
|
||||
"Path": "Gems/GemA",
|
||||
"Uuid": "044a63ea67d04479aa5daf62ded9d9cb",
|
||||
"Version": "0.1.0",
|
||||
"_comment": "GemA"
|
||||
},
|
||||
{
|
||||
"Path": "Gems/GemB",
|
||||
"Uuid": "07375b61b1a2424bb03088bbdf28b2c9",
|
||||
"Version": "0.1.0",
|
||||
"_comment": "GemB"
|
||||
},
|
||||
{
|
||||
"Path": "Gems/GemC",
|
||||
"Uuid": "0945e21b7ae848ac80b4ec1f34c459cd",
|
||||
"Version": "0.1.0",
|
||||
"_comment": "GemC"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace AssetBundler
|
||||
// AzFramework::ApplicationRequests::Bus::Handler interface
|
||||
void NormalizePath(AZStd::string& /*path*/) override {}
|
||||
void NormalizePathKeepCase(AZStd::string& /*path*/) override {}
|
||||
void CalculateBranchTokenForAppRoot(AZStd::string& /*token*/) const override {}
|
||||
void CalculateBranchTokenForEngineRoot(AZStd::string& /*token*/) const override {}
|
||||
|
||||
const char* GetAppRoot() const override
|
||||
{
|
||||
|
||||
@@ -115,9 +115,9 @@ namespace AssetBundler
|
||||
settingsRegistry->Set(gemSourcePathKey, gemSourcePath.Native());
|
||||
}
|
||||
|
||||
AzToolsFramework::AssetUtils::GetGemsInfo(m_data->m_testEngineRoot.c_str(), m_data->m_testEngineRoot.c_str(), DummyProjectName, m_data->m_applicationManager->m_gemInfoList);
|
||||
AzFramework::GetGemsInfo(m_data->m_applicationManager->m_gemInfoList, *settingsRegistry);
|
||||
EXPECT_GE(m_data->m_applicationManager->m_gemInfoList.size(), 3);
|
||||
for (const AzToolsFramework::AssetUtils::GemInfo& gemInfo : m_data->m_applicationManager->m_gemInfoList)
|
||||
for (const AzFramework::GemInfo& gemInfo : m_data->m_applicationManager->m_gemInfoList)
|
||||
{
|
||||
gemsNameMap.erase(gemInfo.m_gemName);
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace AssetBundler
|
||||
// underneath code assumes that we might be leaking the previous instance
|
||||
AZ::IO::FileIOBase::SetInstance(nullptr);
|
||||
AZ::IO::FileIOBase::SetInstance(m_data->m_localFileIO);
|
||||
|
||||
|
||||
AddGemData(m_data->m_testEngineRoot.c_str(), "GemA");
|
||||
AddGemData(m_data->m_testEngineRoot.c_str(), "GemB");
|
||||
|
||||
@@ -169,20 +169,21 @@ namespace AssetBundler
|
||||
|
||||
m_data->m_gemSeedFilePairList.emplace_back(absoluteGemSeedFilePath, seedFileExists);
|
||||
|
||||
m_data->m_gemInfoList.emplace_back(AzToolsFramework::AssetUtils::GemInfo(gemName, relativeGemPath.Native(), absoluteGemPath.Native(), AZ::Uuid::CreateRandom().ToString<AZStd::string>().c_str(), false, false));
|
||||
|
||||
m_data->m_gemInfoList.emplace_back(gemName);
|
||||
m_data->m_gemInfoList.back().m_absoluteSourcePaths.push_back(absoluteGemPath.Native());
|
||||
|
||||
AZ::IO::Path platformsDirectory = absoluteGemPath / "Assets" / PlatformsFolder;
|
||||
if (m_data->m_localFileIO->Exists(platformsDirectory.c_str()))
|
||||
{
|
||||
m_data->m_localFileIO->FindFiles(platformsDirectory.c_str(),
|
||||
AZStd::string::format("*.%s", AzToolsFramework::AssetSeedManager::GetSeedFileExtension()).c_str(),
|
||||
[&](const char* fileName)
|
||||
{
|
||||
AZStd::string normalizedFilePath = fileName;
|
||||
AzFramework::StringFunc::Path::Normalize(normalizedFilePath);
|
||||
m_data->m_gemSeedFilePairList.emplace_back(AZStd::make_pair(normalizedFilePath, seedFileExists));
|
||||
return true;
|
||||
});
|
||||
{
|
||||
AZStd::string normalizedFilePath = fileName;
|
||||
AzFramework::StringFunc::Path::Normalize(normalizedFilePath);
|
||||
m_data->m_gemSeedFilePairList.emplace_back(AZStd::make_pair(normalizedFilePath, seedFileExists));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
AZ::IO::Path iosDirectory = platformsDirectory / AzFramework::PlatformIOS;
|
||||
@@ -195,7 +196,7 @@ namespace AssetBundler
|
||||
if (result.IsSuccess())
|
||||
{
|
||||
AZStd::list<AZStd::string> seedFiles = result.TakeValue();
|
||||
for(AZStd::string& seedFile : seedFiles)
|
||||
for (AZStd::string& seedFile : seedFiles)
|
||||
{
|
||||
AZStd::string normalizedFilePath = seedFile;
|
||||
AzFramework::StringFunc::Path::Normalize(normalizedFilePath);
|
||||
@@ -207,7 +208,7 @@ namespace AssetBundler
|
||||
|
||||
struct StaticData
|
||||
{
|
||||
AZStd::vector<AzToolsFramework::AssetUtils::GemInfo> m_gemInfoList;
|
||||
AZStd::vector<AzFramework::GemInfo> m_gemInfoList;
|
||||
AZStd::vector<AZStd::pair<AZStd::string, bool>> m_gemSeedFilePairList;
|
||||
AZStd::unique_ptr<AzToolsFramework::ToolsApplication> m_application = {};
|
||||
AZ::IO::FileIOBase* m_priorFileIO = nullptr;
|
||||
@@ -230,7 +231,8 @@ namespace AssetBundler
|
||||
TEST_F(AssetBundlerGemsUtilTest, GetDefaultSeedFiles_AllSeedFiles_Found)
|
||||
{
|
||||
// DummyProject and fake Engine/Gem structure lives at dev/Code/Tools/AssetBundler/tests/
|
||||
auto defaultSeedList = AssetBundler::GetDefaultSeedListFiles(m_data->m_testEngineRoot.c_str(), DummyProjectFolder, m_data->m_gemInfoList, AzFramework::PlatformFlags::Platform_PC);
|
||||
auto dummyProjectPath = AZ::IO::Path(m_data->m_testEngineRoot) / DummyProjectFolder;
|
||||
auto defaultSeedList = AssetBundler::GetDefaultSeedListFiles(m_data->m_testEngineRoot.c_str(), dummyProjectPath.Native(), m_data->m_gemInfoList, AzFramework::PlatformFlags::Platform_PC);
|
||||
ASSERT_EQ(defaultSeedList.size(), 5); //adding one for the engine seed file and one for the project file
|
||||
|
||||
// Validate whether both GemA and GemB seed file are present
|
||||
@@ -246,7 +248,8 @@ namespace AssetBundler
|
||||
TEST_F(AssetBundlerGemsUtilTest, GetDefaultSeedFilesForMultiplePlatforms_AllSeedFiles_Found)
|
||||
{
|
||||
// DummyProject and fake Engine/Gem structure lives at dev/Code/Tools/AssetBundler/tests/
|
||||
auto defaultSeedList = AssetBundler::GetDefaultSeedListFiles(m_data->m_testEngineRoot.c_str(), DummyProjectFolder, m_data->m_gemInfoList, AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_IOS);
|
||||
auto dummyProjectPath = AZ::IO::Path(m_data->m_testEngineRoot) / DummyProjectFolder;
|
||||
auto defaultSeedList = AssetBundler::GetDefaultSeedListFiles(m_data->m_testEngineRoot.c_str(), dummyProjectPath.Native(), m_data->m_gemInfoList, AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_IOS);
|
||||
ASSERT_EQ(defaultSeedList.size(), 6); //adding one for the engine seed file and one for the project file
|
||||
|
||||
|
||||
@@ -264,136 +267,12 @@ namespace AssetBundler
|
||||
|
||||
TEST_F(AssetBundlerGemsUtilTest, IsSeedFileValid_Ok)
|
||||
{
|
||||
for (const auto& pair : m_data->m_gemSeedFilePairList)
|
||||
for (const auto& pair : m_data->m_gemSeedFilePairList)
|
||||
{
|
||||
bool result = IsGemSeedFilePathValid(m_data->m_testEngineRoot.c_str(), pair.first, m_data->m_gemInfoList, AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_IOS);
|
||||
EXPECT_EQ(result,pair.second);
|
||||
EXPECT_EQ(result, pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
const char TestProject[] = "TestProject";
|
||||
const char TestProjectLowerCase[] = "testproject";
|
||||
#if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
|
||||
const char TestRoot[] = "D:\\Dummy\\Test\\dev\\";
|
||||
#else
|
||||
const char TestRoot[] = "/Dummy/Test/dev/";
|
||||
#endif
|
||||
|
||||
class MockApplication
|
||||
: public AzFramework::ApplicationRequests::Bus::Handler
|
||||
{
|
||||
public:
|
||||
MockApplication()
|
||||
{
|
||||
// ensure the cached engine root from previous tests is cleared
|
||||
// so the mock application behaves properly
|
||||
g_cachedEngineRoot[0] = 0;
|
||||
|
||||
if (AZ::SettingsRegistry::Get() == nullptr)
|
||||
{
|
||||
m_settingsRegistry = AZStd::make_unique<AZ::SettingsRegistryImpl>();
|
||||
AZ::SettingsRegistry::Register(m_settingsRegistry.get());
|
||||
|
||||
auto gameProjectKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/%s",
|
||||
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, "sys_game_folder");
|
||||
m_settingsRegistry->Set(gameProjectKey, TestProject);
|
||||
}
|
||||
AzFramework::ApplicationRequests::Bus::Handler::BusConnect();
|
||||
}
|
||||
~MockApplication()
|
||||
{
|
||||
AzFramework::ApplicationRequests::Bus::Handler::BusDisconnect();
|
||||
if (m_settingsRegistry.get() == AZ::SettingsRegistry::Get())
|
||||
{
|
||||
AZ::SettingsRegistry::Unregister(m_settingsRegistry.get());
|
||||
}
|
||||
}
|
||||
// AzFramework::ApplicationRequests::Bus::Handler interface
|
||||
void NormalizePath(AZStd::string& /*path*/) override {};
|
||||
void NormalizePathKeepCase(AZStd::string& /*path*/) override {};
|
||||
void CalculateBranchTokenForAppRoot(AZStd::string& /*token*/) const override {};
|
||||
const char* GetEngineRoot() const { return TestRoot; }
|
||||
|
||||
private:
|
||||
AZStd::unique_ptr<AZ::SettingsRegistryInterface> m_settingsRegistry;
|
||||
};
|
||||
|
||||
class AssetBundlerPathUtilTest
|
||||
: public UnitTest::ScopedAllocatorSetupFixture
|
||||
{
|
||||
public:
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
UnitTest::ScopedAllocatorSetupFixture::SetUp();
|
||||
m_data = AZStd::make_unique<StaticData>();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_data.reset();
|
||||
UnitTest::ScopedAllocatorSetupFixture::TearDown();
|
||||
}
|
||||
|
||||
struct StaticData
|
||||
{
|
||||
MockApplication m_mockApplication;
|
||||
};
|
||||
|
||||
AZStd::unique_ptr<StaticData> m_data;
|
||||
};
|
||||
|
||||
|
||||
TEST_F(AssetBundlerPathUtilTest, ComputeAssetAliasAndGameName_AssetCatalogPathNotProvided_Valid)
|
||||
{
|
||||
AZStd::string platformIdentifier = "pc";
|
||||
AZStd::string assetCatalogFile;
|
||||
AZStd::string assetAlias;
|
||||
AZStd::string gameName;
|
||||
EXPECT_TRUE(ComputeAssetAliasAndGameName(platformIdentifier, assetCatalogFile, assetAlias, gameName).IsSuccess());
|
||||
EXPECT_TRUE(gameName == TestProject);
|
||||
|
||||
AZStd::string assetPath;
|
||||
bool success = AzFramework::StringFunc::Path::ConstructFull(TestRoot, "Cache", assetPath) &&
|
||||
AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), TestProject, assetPath) &&
|
||||
AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), platformIdentifier.c_str(), assetPath) &&
|
||||
AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), TestProjectLowerCase, assetPath);
|
||||
EXPECT_EQ(assetAlias, assetPath);
|
||||
}
|
||||
|
||||
TEST_F(AssetBundlerPathUtilTest, ComputeAssetAliasAndGameName_AssetCatalogPathProvided_Valid)
|
||||
{
|
||||
AZStd::string platformIdentifier = "pc";
|
||||
#if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
|
||||
AZStd::string assetCatalogFile = "D:\\Dummy\\Test\\dev\\Cache\\TestProject1\\pc\\testproject1\\assetcatalog.xml";
|
||||
#else
|
||||
AZStd::string assetCatalogFile = "/Dummy/Test/dev/Cache/TestProject1/pc/testproject1/assetcatalog.xml";
|
||||
#endif
|
||||
AZStd::string assetAlias;
|
||||
AZStd::string gameName;
|
||||
EXPECT_TRUE(ComputeAssetAliasAndGameName(platformIdentifier, assetCatalogFile, assetAlias, gameName).IsSuccess());
|
||||
EXPECT_EQ(gameName, "TestProject1");
|
||||
|
||||
AZStd::string assetPath;
|
||||
bool success = AzFramework::StringFunc::Path::ConstructFull(TestRoot, "Cache", assetPath) &&
|
||||
AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), "TestProject1", assetPath) &&
|
||||
AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), platformIdentifier.c_str(), assetPath) &&
|
||||
AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), "testproject1", assetPath);
|
||||
EXPECT_EQ(assetAlias, assetPath);
|
||||
}
|
||||
|
||||
TEST_F(AssetBundlerPathUtilTest, ComputeAssetAliasAndGameName_GameNameMismatch_Failure)
|
||||
{
|
||||
AZStd::string platformIdentifier = "pc";
|
||||
#if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
|
||||
AZStd::string assetCatalogFile = "D:\\Dummy\\Cache\\TestProject1\\pc\\testproject1\\assetcatalog.xml";
|
||||
#else
|
||||
AZStd::string assetCatalogFile = "/Dummy/Cache/TestProject1/pc/testproject1/assetcatalog.xml";
|
||||
#endif
|
||||
AZStd::string assetAlias;
|
||||
AZStd::string gameName="SomeOtherGamename";
|
||||
EXPECT_FALSE(ComputeAssetAliasAndGameName(platformIdentifier, assetCatalogFile, assetAlias, gameName).IsSuccess());
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <AzCore/UserSettings/UserSettingsComponent.h>
|
||||
#include <AzCore/Slice/SliceSystemComponent.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
#include <AzFramework/Asset/AssetCatalogComponent.h>
|
||||
#include <AzFramework/Asset/AssetSystemComponent.h>
|
||||
@@ -97,21 +98,6 @@ AssetBuilderApplication::AssetBuilderApplication(int* argc, char*** argv)
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization(
|
||||
*settingsRegistry, AssetBuilder::GetBuildTargetName());
|
||||
|
||||
// Override the /Amazon/AzCore/Bootstrap/sys_game_folder entry in the Settings Registry using the -gameName parameter
|
||||
if (m_commandLine.GetNumSwitchValues("gameName") > 0)
|
||||
{
|
||||
const AZStd::string& gameFolderOverride = m_commandLine.GetSwitchValue("gameName", 0);
|
||||
auto gameFolderCommandLineOverride = AZStd::string::format("--regset=%s/sys_game_folder=%s", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey,
|
||||
gameFolderOverride.c_str());
|
||||
|
||||
AZ::CommandLine::ParamContainer commandLineArgs;
|
||||
m_commandLine.Dump(commandLineArgs);
|
||||
commandLineArgs.emplace_back(gameFolderCommandLineOverride);
|
||||
m_commandLine.Parse(commandLineArgs);
|
||||
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*settingsRegistry, m_commandLine, false);
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry);
|
||||
}
|
||||
AZ::Interface<IBuilderApplication>::Register(this);
|
||||
}
|
||||
|
||||
@@ -123,7 +109,7 @@ AssetBuilderApplication::~AssetBuilderApplication()
|
||||
void AssetBuilderApplication::RegisterCoreComponents()
|
||||
{
|
||||
AzToolsFramework::ToolsApplication::RegisterCoreComponents();
|
||||
|
||||
|
||||
RegisterComponentDescriptor(AssetBuilderComponent::CreateDescriptor());
|
||||
RegisterComponentDescriptor(AssetProcessor::ToolsAssetCatalogComponent::CreateDescriptor());
|
||||
}
|
||||
@@ -134,32 +120,8 @@ void AssetBuilderApplication::StartCommon(AZ::Entity* systemEntity)
|
||||
|
||||
// Merge in the SettingsRegistry for the game being processed. This does not
|
||||
// necessarily correspond to the project name in the bootstrap.cfg since it
|
||||
// the AssetBuilder supports overriding the gameName on the command line
|
||||
// the AssetBuilder supports overriding the project-path on the command line
|
||||
AZ::SettingsRegistryInterface& registry = *AZ::SettingsRegistry::Get();
|
||||
AZ::SettingsRegistryInterface::FixedValueString gameName;
|
||||
if (m_commandLine.GetNumSwitchValues("gameName") > 0)
|
||||
{
|
||||
gameName = AZStd::string_view(m_commandLine.GetSwitchValue("gameName", 0));
|
||||
}
|
||||
|
||||
// Add the supplied gameName to the specialization key in the registry
|
||||
if (!gameName.empty())
|
||||
{
|
||||
auto gameNameSpecialization = AZ::SettingsRegistryInterface::FixedValueString::format("%s/%.*s",
|
||||
AZ::SettingsRegistryMergeUtils::SpecializationsRootKey, aznumeric_cast<int>(gameName.size()), gameName.data());
|
||||
registry.Set(gameNameSpecialization, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add the project name as a registry specialization
|
||||
auto projectKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/sys_game_folder", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey);
|
||||
if (AZ::SettingsRegistryInterface::FixedValueString bootstrapGameName; registry.Get(bootstrapGameName, projectKey) && !bootstrapGameName.empty())
|
||||
{
|
||||
registry.Set(AZ::SettingsRegistryInterface::FixedValueString::format("%s/%s",
|
||||
AZ::SettingsRegistryMergeUtils::SpecializationsRootKey, bootstrapGameName.c_str()),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve specializations from the Settings Registry and ComponentApplication derived classes
|
||||
AZ::SettingsRegistryInterface::Specializations specializations;
|
||||
@@ -177,41 +139,25 @@ void AssetBuilderApplication::StartCommon(AZ::Entity* systemEntity)
|
||||
// the executable, we need to set the PATH environment variable.
|
||||
AZStd::string exeFolder;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(exeFolder, &AZ::ComponentApplicationBus::Events::GetExecutableFolder);
|
||||
|
||||
|
||||
setenv("PATH", exeFolder.c_str(), 1);
|
||||
#endif // AZ_PLATFORM_MAC
|
||||
|
||||
AZStd::string gameRoot;
|
||||
|
||||
if (m_commandLine.GetNumSwitchValues("gameRoot") > 0)
|
||||
{
|
||||
gameRoot = m_commandLine.GetSwitchValue("gameRoot", 0);
|
||||
}
|
||||
|
||||
if (gameRoot.empty())
|
||||
// Make sure a project path was set to settings registry and error/warn if not.
|
||||
auto projectPath = AZ::Utils::GetProjectPath();
|
||||
if (projectPath.empty())
|
||||
{
|
||||
if (IsInDebugMode())
|
||||
{
|
||||
if (!AZ::SettingsRegistry::Get()->Get(gameRoot, AZ::SettingsRegistryMergeUtils::FilePathKey_SourceGameFolder))
|
||||
{
|
||||
AZ_Error("AssetBuilder", false, "Unable to determine the game root automatically. "
|
||||
"Make sure a default project has been set or provide a default option on the command line. (See -help for more info.)");
|
||||
return;
|
||||
}
|
||||
AZ_Error("AssetBuilder", false, "Unable to determine the project path automatically. "
|
||||
"Make sure a default project path has been set or provide a --project-path option on the command line. "
|
||||
"(See -help for more info.)");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Printf(AssetBuilderSDK::InfoWindow, "gameRoot not specified on the command line, assuming current directory.\n");
|
||||
AZ_Printf(AssetBuilderSDK::InfoWindow, "gameRoot is best specified as the full path to the game's asset folder.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!gameRoot.empty())
|
||||
{
|
||||
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
|
||||
if (fileIO)
|
||||
{
|
||||
fileIO->SetAlias("@devassets@", gameRoot.c_str());
|
||||
AZ_Printf(AssetBuilderSDK::InfoWindow, "project-path not specified on the command line, assuming current directory.\n");
|
||||
AZ_Printf(AssetBuilderSDK::InfoWindow, "project-path is best specified as the full path to the project's folder.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +173,7 @@ void AssetBuilderApplication::StartCommon(AZ::Entity* systemEntity)
|
||||
// Disable parallel dependency loads since the builders can't count on all other assets and their info being ready.
|
||||
// Specifically, asset builders can trigger asset loads during the building process. The ToolsAssetCatalog doesn't
|
||||
// implement the dependency APIs, so the asset loads will fail to load any dependent assets.
|
||||
//
|
||||
//
|
||||
// NOTE: The ToolsAssetCatalog could *potentially* implement the dependency APIs by querying the live Asset Processor instance,
|
||||
// but this will return incomplete dependency information based on the subset of assets that have already processed.
|
||||
// In theory, if the Asset Builder dependencies are set up correctly, the needed subset should always be processed first,
|
||||
@@ -242,61 +188,6 @@ bool AssetBuilderApplication::IsInDebugMode() const
|
||||
return AssetBuilderComponent::IsInDebugMode(m_commandLine);
|
||||
}
|
||||
|
||||
bool AssetBuilderApplication::GetOptionalAppRootArg(char destinationRootArgBuffer[], size_t destinationRootArgBufferSize) const
|
||||
{
|
||||
// Only continue if the application received any arguments from the command line
|
||||
if ((!this->m_argC) || (!this->m_argV))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int argc = this->m_argC;
|
||||
char** argv = this->m_argV;
|
||||
|
||||
// Search for the app root argument (-approot=<PATH>) where <PATH> is the app root path to set for the application
|
||||
const static char* appRootArgPrefix = "-approot=";
|
||||
size_t appRootArgPrefixLen = strlen(appRootArgPrefix);
|
||||
|
||||
const char* appRootArg = nullptr;
|
||||
for (int index = 0; index < argc; index++)
|
||||
{
|
||||
if (strncmp(appRootArgPrefix, argv[index], appRootArgPrefixLen) == 0)
|
||||
{
|
||||
appRootArg = &argv[index][appRootArgPrefixLen];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (appRootArg)
|
||||
{
|
||||
AZStd::string_view appRootArgView = appRootArg;
|
||||
size_t afterStartQuotes = appRootArgView.find_first_not_of(R"(")");
|
||||
if (afterStartQuotes != AZStd::string_view::npos)
|
||||
{
|
||||
appRootArgView.remove_prefix(afterStartQuotes);
|
||||
}
|
||||
size_t beforeEndQuotes = appRootArgView.find_last_not_of(R"(")");
|
||||
if (beforeEndQuotes != AZStd::string_view::npos)
|
||||
{
|
||||
appRootArgView.remove_suffix(appRootArgView.size() - (beforeEndQuotes + 1));
|
||||
}
|
||||
appRootArgView.copy(destinationRootArgBuffer, destinationRootArgBufferSize);
|
||||
destinationRootArgBuffer[appRootArgView.size()] = '\0';
|
||||
|
||||
const char lastChar = destinationRootArgBuffer[strlen(destinationRootArgBuffer) - 1];
|
||||
bool needsTrailingPathDelim = (lastChar != AZ_CORRECT_FILESYSTEM_SEPARATOR) && (lastChar != AZ_WRONG_FILESYSTEM_SEPARATOR);
|
||||
if (needsTrailingPathDelim)
|
||||
{
|
||||
azstrncat(destinationRootArgBuffer, destinationRootArgBufferSize, AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING, 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBuilderApplication::InitializeBuilderComponents()
|
||||
{
|
||||
CreateAndAddEntityFromComponentTags(AZStd::vector<AZ::Crc32>({ AssetBuilderSDK::ComponentTags::AssetBuilder }), "AssetBuilders Entity");
|
||||
|
||||
@@ -45,8 +45,6 @@ public:
|
||||
|
||||
bool IsInDebugMode() const;
|
||||
|
||||
bool GetOptionalAppRootArg(char destinationRootArgBuffer[], size_t destinationRootArgBufferSize) const;
|
||||
|
||||
void InitializeBuilderComponents() override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -19,13 +19,15 @@
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/Serialization/Utils.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/Asset/AssetProcessorMessages.h>
|
||||
#include <AzFramework/Asset/AssetSystemBus.h>
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzFramework/Network/AssetProcessorConnection.h>
|
||||
#include <AzFramework/Platform/PlatformDefaults.h>
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
#include <AssetBuilderSDK/AssetBuilderSDK.h>
|
||||
@@ -40,8 +42,8 @@
|
||||
// Command-line parameter options:
|
||||
static const char* const s_paramHelp = "help"; // Print help information.
|
||||
static const char* const s_paramTask = "task"; // Task to run.
|
||||
static const char* const s_paramGameName = "gamename"; // Name of the current project.
|
||||
static const char* const s_paramGameCache = "gamecache"; // Full path to the project cache folder.
|
||||
static const char* const s_paramProjectName = "project-name"; // Name of the current project.
|
||||
static const char* const s_paramProjectCacheRoot = "project-cache-path"; // Full path to the project cache folder.
|
||||
static const char* const s_paramModule = "module"; // For resident mode, the path to the builder dll folder, otherwise the full path to a single builder dll to use.
|
||||
static const char* const s_paramPort = "port"; // Optional, port number to use to connect to the AP.
|
||||
static const char* const s_paramIp = "remoteip"; // optional, IP address to use to connect to the AP
|
||||
@@ -63,6 +65,78 @@ static const char* const s_taskDebug = "debug"; // runs a one shot job in a fake
|
||||
static const char* const s_taskDebugCreate = "debug_create"; // runs a one shot job in a fake environment for a specified file.
|
||||
static const char* const s_taskDebugProcess = "debug_process"; // runs a one shot job in a fake environment for a specified file.
|
||||
|
||||
//! Scoped Setters for the SettingsRegistry to its previous value on destruction
|
||||
struct ScopedSettingsRegistrySetter
|
||||
{
|
||||
using SettingsRegistrySetterTypes = AZStd::variant<bool, AZ::s64, AZ::u64, double, AZStd::string_view>;
|
||||
using SettingsRegistryGetterTypes = AZStd::variant<bool, AZ::s64, AZ::u64, double, AZStd::string>;
|
||||
|
||||
ScopedSettingsRegistrySetter(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view jsonPointer,
|
||||
SettingsRegistrySetterTypes newValue)
|
||||
: m_settingsRegistry(settingsRegistry)
|
||||
, m_jsonPointer(jsonPointer)
|
||||
{
|
||||
AZStd::string oldValue;
|
||||
if (m_settingsRegistry.Get(oldValue, jsonPointer))
|
||||
{
|
||||
m_oldValue = AZStd::move(oldValue);
|
||||
}
|
||||
|
||||
AZStd::visit([this](auto&& value) {m_settingsRegistry.Set(m_jsonPointer, AZStd::move(value)); }, AZStd::move(newValue));
|
||||
}
|
||||
|
||||
~ScopedSettingsRegistrySetter()
|
||||
{
|
||||
// Reset the old value within the Settings Registry if it was set
|
||||
// Or remove it if not
|
||||
if (m_oldValue)
|
||||
{
|
||||
AZStd::visit([this](auto&& value) {m_settingsRegistry.Set(m_jsonPointer, AZStd::move(value)); }, AZStd::move(*m_oldValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_settingsRegistry.Remove(m_jsonPointer);
|
||||
}
|
||||
}
|
||||
AZ::SettingsRegistryInterface& m_settingsRegistry;
|
||||
AZStd::string_view m_jsonPointer;
|
||||
AZStd::optional<SettingsRegistryGetterTypes> m_oldValue;
|
||||
};
|
||||
|
||||
//! FileIO classes which resets the set key to its previous value on destruction
|
||||
struct ScopedAliasSetter
|
||||
{
|
||||
ScopedAliasSetter(AZ::IO::FileIOBase& fileIoBase, const char* alias,
|
||||
const char* newValue)
|
||||
: m_fileIoBase(fileIoBase)
|
||||
, m_alias(alias)
|
||||
{
|
||||
|
||||
if (const char* oldValue = m_fileIoBase.GetAlias(m_alias); oldValue != nullptr)
|
||||
{
|
||||
m_oldValue = oldValue;
|
||||
}
|
||||
|
||||
m_fileIoBase.SetAlias(alias, newValue);
|
||||
}
|
||||
|
||||
~ScopedAliasSetter()
|
||||
{
|
||||
// Reset the old alias if it was set or clear it if not
|
||||
if (m_oldValue)
|
||||
{
|
||||
m_fileIoBase.SetAlias(m_alias, m_oldValue->c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_fileIoBase.ClearAlias(m_alias);
|
||||
}
|
||||
}
|
||||
AZ::IO::FileIOBase& m_fileIoBase;
|
||||
const char* m_alias;
|
||||
AZStd::optional<AZStd::string> m_oldValue;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void AssetBuilderComponent::PrintHelp()
|
||||
@@ -71,8 +145,8 @@ void AssetBuilderComponent::PrintHelp()
|
||||
AZ_TracePrintf("Help", "The following command line options are available for the AssetBuilder.\n");
|
||||
AZ_TracePrintf("Help", "%s - Print help information.\n", s_paramHelp);
|
||||
AZ_TracePrintf("Help", "%s - Task to run.\n", s_paramTask);
|
||||
AZ_TracePrintf("Help", "%s - Name of the current project.\n", s_paramGameName);
|
||||
AZ_TracePrintf("Help", "%s - Full path to the project cache folder.\n", s_paramGameCache);
|
||||
AZ_TracePrintf("Help", "%s - Name of the current project.\n", s_paramProjectName);
|
||||
AZ_TracePrintf("Help", "%s - Full path to the project cache folder.\n", s_paramProjectCacheRoot);
|
||||
AZ_TracePrintf("Help", "%s - For resident mode, the path to the builder dll folder, otherwise the full path to a single builder dll to use.\n", s_paramModule);
|
||||
AZ_TracePrintf("Help", "%s - Optional, port number to use to connect to the AP.\n", s_paramPort);
|
||||
AZ_TracePrintf("Help", "%s - UUID string that identifies the builder. Only used for resident mode when the AP directly starts up the AssetBuilder.\n", s_paramId);
|
||||
@@ -106,6 +180,7 @@ bool AssetBuilderComponent::IsInDebugMode(const AzFramework::CommandLine& comman
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void AssetBuilderComponent::Activate()
|
||||
{
|
||||
BuilderBus::Handler::BusConnect();
|
||||
@@ -168,17 +243,12 @@ bool AssetBuilderComponent::Run()
|
||||
}
|
||||
|
||||
bool isDebugTask = (task == s_taskDebug || task == s_taskDebugCreate || task == s_taskDebugProcess);
|
||||
if (!GetParameter(s_paramGameName, m_gameName, !isDebugTask))
|
||||
if (!GetParameter(s_paramProjectName, m_gameName, !isDebugTask))
|
||||
{
|
||||
auto gameNameKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/%s",
|
||||
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, AzFramework::AssetSystem::ProjectName);
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
settingsRegistry->Get(m_gameName, gameNameKey);
|
||||
}
|
||||
m_gameName = AZ::Utils::GetProjectName();
|
||||
}
|
||||
|
||||
if (!GetParameter(s_paramGameCache, m_gameCache, !isDebugTask))
|
||||
if (!GetParameter(s_paramProjectCacheRoot, m_gameCache, !isDebugTask))
|
||||
{
|
||||
if (!isDebugTask)
|
||||
{
|
||||
@@ -283,7 +353,7 @@ bool AssetBuilderComponent::ConnectToAssetProcessor()
|
||||
|
||||
//the asset builder may have been given an optional project name to use
|
||||
AZStd::string overrideProjectName;
|
||||
if (GetParameter(s_paramGameName, overrideProjectName, false))
|
||||
if (GetParameter(s_paramProjectName, overrideProjectName, false))
|
||||
{
|
||||
connectionSettings.m_projectName = overrideProjectName;
|
||||
}
|
||||
@@ -380,21 +450,15 @@ bool AssetBuilderComponent::RunDebugTask(AZStd::string&& debugFile, bool runCrea
|
||||
return false;
|
||||
}
|
||||
}
|
||||
AzFramework::StringFunc::Path::Normalize(debugFile);
|
||||
AZ::StringFunc::Path::Normalize(debugFile);
|
||||
|
||||
if (!GetParameter(s_paramGameCache, m_gameCache, false))
|
||||
if (!GetParameter(s_paramProjectCacheRoot, m_gameCache, false))
|
||||
{
|
||||
if (m_gameCache.empty())
|
||||
{
|
||||
// Setup the cache path
|
||||
AZStd::string assetRoot;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(assetRoot, &AzFramework::ApplicationRequests::GetAssetRoot);
|
||||
if (!assetRoot.empty())
|
||||
{
|
||||
AZStd::string tempString = AZStd::string::format("Cache/%s", m_gameName.c_str());
|
||||
AzFramework::StringFunc::Path::Join(assetRoot.c_str(), tempString.c_str(), m_gameCache);
|
||||
}
|
||||
else
|
||||
// Query the project cache root path from the Settings Registry
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
if (!settingsRegistry || !settingsRegistry->Get(m_gameCache, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder))
|
||||
{
|
||||
m_gameCache = ".";
|
||||
}
|
||||
@@ -415,7 +479,7 @@ bool AssetBuilderComponent::RunDebugTask(AZStd::string&& debugFile, bool runCrea
|
||||
AZStd::string module;
|
||||
if (GetParameter(s_paramModule, module, false))
|
||||
{
|
||||
AzFramework::StringFunc::Path::GetFullPath(module.c_str(), binDir);
|
||||
AZ::StringFunc::Path::GetFullPath(module.c_str(), binDir);
|
||||
if (!LoadBuilder(module))
|
||||
{
|
||||
AZ_Error("AssetBuilder", false, "Failed to load module '%s'.", module.c_str());
|
||||
@@ -432,7 +496,7 @@ bool AssetBuilderComponent::RunDebugTask(AZStd::string&& debugFile, bool runCrea
|
||||
return false;
|
||||
}
|
||||
|
||||
AzFramework::StringFunc::Path::Join(executableFolder, "Builders", binDir);
|
||||
AZ::StringFunc::Path::Join(executableFolder, "Builders", binDir);
|
||||
|
||||
if (!LoadBuilders(binDir))
|
||||
{
|
||||
@@ -445,11 +509,11 @@ bool AssetBuilderComponent::RunDebugTask(AZStd::string&& debugFile, bool runCrea
|
||||
if (!GetParameter(s_paramOutput, baseTempDirPath, false))
|
||||
{
|
||||
AZStd::string fileName;
|
||||
AzFramework::StringFunc::Path::GetFullFileName(debugFile.c_str(), fileName);
|
||||
AZ::StringFunc::Path::GetFullFileName(debugFile.c_str(), fileName);
|
||||
AZStd::replace(fileName.begin(), fileName.end(), '.', '_');
|
||||
|
||||
AzFramework::StringFunc::Path::Join(binDir.c_str(), "Debug", baseTempDirPath);
|
||||
AzFramework::StringFunc::Path::Join(baseTempDirPath.c_str(), fileName.c_str(), baseTempDirPath);
|
||||
AZ::StringFunc::Path::Join(binDir.c_str(), "Debug", baseTempDirPath);
|
||||
AZ::StringFunc::Path::Join(baseTempDirPath.c_str(), fileName.c_str(), baseTempDirPath);
|
||||
}
|
||||
|
||||
// Default tags for the debug task are "tools" and "debug"
|
||||
@@ -489,7 +553,7 @@ bool AssetBuilderComponent::RunDebugTask(AZStd::string&& debugFile, bool runCrea
|
||||
AZ_TracePrintf(AssetBuilderSDK::InfoWindow, "Debugging builder '%s'.\n", builder->m_name.c_str());
|
||||
|
||||
AZStd::string tempDirPath;
|
||||
AzFramework::StringFunc::Path::Join(baseTempDirPath.c_str(), builder->m_name.c_str(), tempDirPath);
|
||||
AZ::StringFunc::Path::Join(baseTempDirPath.c_str(), builder->m_name.c_str(), tempDirPath);
|
||||
|
||||
AZStd::vector<AssetBuilderSDK::PlatformInfo> enabledDebugPlatformInfos =
|
||||
{
|
||||
@@ -502,7 +566,7 @@ bool AssetBuilderComponent::RunDebugTask(AZStd::string&& debugFile, bool runCrea
|
||||
if (runCreateJobs)
|
||||
{
|
||||
AZStd::string createJobsTempDirPath;
|
||||
AzFramework::StringFunc::Path::Join(tempDirPath.c_str(), "CreateJobs", createJobsTempDirPath);
|
||||
AZ::StringFunc::Path::Join(tempDirPath.c_str(), "CreateJobs", createJobsTempDirPath);
|
||||
AZ::IO::Result fileResult = fileIO->CreatePath(createJobsTempDirPath.c_str());
|
||||
if (!fileResult)
|
||||
{
|
||||
@@ -520,7 +584,7 @@ bool AssetBuilderComponent::RunDebugTask(AZStd::string&& debugFile, bool runCrea
|
||||
builder->m_createJobFunction(createRequest, createResponse);
|
||||
|
||||
AZStd::string responseFile;
|
||||
AzFramework::StringFunc::Path::Join(createJobsTempDirPath.c_str(), "CreateJobsResponse.xml", responseFile);
|
||||
AZ::StringFunc::Path::Join(createJobsTempDirPath.c_str(), "CreateJobsResponse.xml", responseFile);
|
||||
if (!AZ::Utils::SaveObjectToFile(responseFile, AZ::DataStream::ST_XML, &createResponse))
|
||||
{
|
||||
AZ_Error("AssetBuilder", false, "Failed to serialize response to file: %s", responseFile.c_str());
|
||||
@@ -538,7 +602,7 @@ bool AssetBuilderComponent::RunDebugTask(AZStd::string&& debugFile, bool runCrea
|
||||
if (runProcessJob)
|
||||
{
|
||||
AZStd::string processJobTempDirPath;
|
||||
AzFramework::StringFunc::Path::Join(tempDirPath.c_str(), "ProcessJobs", processJobTempDirPath);
|
||||
AZ::StringFunc::Path::Join(tempDirPath.c_str(), "ProcessJobs", processJobTempDirPath);
|
||||
AZ::IO::Result fileResult = fileIO->CreatePath(processJobTempDirPath.c_str());
|
||||
if (!fileResult)
|
||||
{
|
||||
@@ -555,7 +619,7 @@ bool AssetBuilderComponent::RunDebugTask(AZStd::string&& debugFile, bool runCrea
|
||||
processRequest.m_sourceFile = info.m_relativePath;
|
||||
processRequest.m_platformInfo = enabledDebugPlatformInfo;
|
||||
processRequest.m_sourceFileUUID = info.m_assetId.m_guid;
|
||||
AzFramework::StringFunc::AssetDatabasePath::Join(processRequest.m_watchFolder.c_str(), processRequest.m_sourceFile.c_str(), processRequest.m_fullPath);
|
||||
AZ::StringFunc::AssetDatabasePath::Join(processRequest.m_watchFolder.c_str(), processRequest.m_sourceFile.c_str(), processRequest.m_fullPath);
|
||||
processRequest.m_tempDirPath = processJobTempDirPath;
|
||||
processRequest.m_jobId = 0;
|
||||
processRequest.m_builderGuid = builder->m_busId;
|
||||
@@ -585,7 +649,7 @@ bool AssetBuilderComponent::RunDebugTask(AZStd::string&& debugFile, bool runCrea
|
||||
ProcessJob(builder->m_processJobFunction, processRequest, processResponse);
|
||||
|
||||
AZStd::string responseFile;
|
||||
AzFramework::StringFunc::Path::Join(processJobTempDirPath.c_str(),
|
||||
AZ::StringFunc::Path::Join(processJobTempDirPath.c_str(),
|
||||
AZStd::string::format("%zu_%s", i, AssetBuilderSDK::s_processJobResponseFileName).c_str(), responseFile);
|
||||
if (!AZ::Utils::SaveObjectToFile(responseFile, AZ::DataStream::ST_XML, &processResponse))
|
||||
{
|
||||
@@ -619,36 +683,39 @@ void AssetBuilderComponent::ProcessJob(const AssetBuilderSDK::ProcessJobFunction
|
||||
auto ioBase = AZ::IO::FileIOBase::GetInstance();
|
||||
AZ_Assert(ioBase != nullptr, "AZ::IO::FileIOBase must be ready for use.");
|
||||
|
||||
// Save out the prior paths.
|
||||
const char* priorAlias = AZ::IO::FileIOBase::GetInstance()->GetAlias("@assets@");
|
||||
AZStd::string priorAssets = priorAlias ? priorAlias : AZStd::string();
|
||||
|
||||
priorAlias = AZ::IO::FileIOBase::GetInstance()->GetAlias("@root@");
|
||||
AZStd::string priorRoot = priorAlias ? priorAlias : AZStd::string();
|
||||
|
||||
priorAlias = AZ::IO::FileIOBase::GetInstance()->GetAlias("@log@");
|
||||
AZStd::string priorLog = priorAlias ? priorAlias : AZStd::string();
|
||||
|
||||
// The game name needs to be lower case within the cache area itself.
|
||||
AZStd::string gameName = m_gameName;
|
||||
AZStd::to_lower(gameName.begin(), gameName.end());
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
AZ_Assert(settingsRegistry != nullptr, "SettingsRegistry must be ready for use in the AssetBuilder.");
|
||||
|
||||
// The root path is the cache plus the platform name.
|
||||
AZ::IO::FixedMaxPath newRoot(m_gameCache);
|
||||
newRoot /= request.m_platformInfo.m_identifier;
|
||||
// Check if the platform identifier is a valid "asset platform"
|
||||
// If so, use it, other wise use the OS default platform as a fail safe
|
||||
// This is to make sure the "debug platform" isn't added as a path segment
|
||||
// the Cache Root folder
|
||||
if (AzFramework::PlatformHelper::GetPlatformIdFromName(request.m_platformInfo.m_identifier) != AzFramework::PlatformId::Invalid)
|
||||
{
|
||||
newRoot /= request.m_platformInfo.m_identifier;
|
||||
}
|
||||
else
|
||||
{
|
||||
newRoot /= AzFramework::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME);
|
||||
}
|
||||
|
||||
// The asset path is root and the lower case game name.
|
||||
AZ::IO::FixedMaxPath newAssets = newRoot / gameName;
|
||||
|
||||
// The log path is the asset
|
||||
AZ::IO::FixedMaxPath newLog = newRoot / "user" / "log";
|
||||
AZ::IO::FixedMaxPath newAssets = newRoot;
|
||||
|
||||
// Now set the paths and run the job.
|
||||
ioBase->SetAlias("@assets@", newAssets.c_str());
|
||||
ioBase->SetAlias("@root@", newRoot.c_str());
|
||||
ioBase->SetAlias("@log@", newLog.c_str());
|
||||
{
|
||||
// Save out the prior paths.
|
||||
ScopedAliasSetter assetAliasScope(*ioBase, "@assets@", newAssets.c_str());
|
||||
ScopedAliasSetter rootAliasScope(*ioBase, "@root@", newRoot.c_str());
|
||||
ScopedAliasSetter projectplatformCacheAliasScope(*ioBase, "@projectplatformcache@", newRoot.c_str());
|
||||
ScopedSettingsRegistrySetter cacheRootFolderScope(*settingsRegistry,
|
||||
AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder, newRoot.Native());
|
||||
|
||||
job(request, outResponse);
|
||||
// Invoke the Process Job function
|
||||
job(request, outResponse);
|
||||
}
|
||||
|
||||
// The asset building ProcessJob method might read any number of source files while processing the asset.
|
||||
// Ensure that any exclusive file handle locks caused by this are cleared so that other AssetBuilder processes
|
||||
@@ -656,37 +723,6 @@ void AssetBuilderComponent::ProcessJob(const AssetBuilderSDK::ProcessJobFunction
|
||||
// This needs to occur after the ProcessJob call, but before the file aliases get cleared.
|
||||
FlushFileStreamerCache();
|
||||
|
||||
// Clean up the paths.
|
||||
// Restore previous @assets@ alias
|
||||
if (priorAssets.empty())
|
||||
{
|
||||
ioBase->ClearAlias("@assets");
|
||||
}
|
||||
else
|
||||
{
|
||||
ioBase->SetAlias("@assets@", priorAssets.c_str());
|
||||
}
|
||||
|
||||
// Restore previous @root@ alias
|
||||
if (priorRoot.empty())
|
||||
{
|
||||
ioBase->ClearAlias("@root@");
|
||||
}
|
||||
else
|
||||
{
|
||||
ioBase->SetAlias("@root@", priorRoot.c_str());
|
||||
}
|
||||
|
||||
// Restore previous @log@ alias
|
||||
if (priorLog.empty())
|
||||
{
|
||||
ioBase->ClearAlias("@log@");
|
||||
}
|
||||
else
|
||||
{
|
||||
ioBase->SetAlias("@log@", priorLog.c_str());
|
||||
}
|
||||
|
||||
UpdateResultCode(request, outResponse);
|
||||
}
|
||||
|
||||
@@ -707,8 +743,8 @@ bool AssetBuilderComponent::RunOneShotTask(const AZStd::string& task)
|
||||
return false;
|
||||
}
|
||||
|
||||
AzFramework::StringFunc::Path::Normalize(inputFilePath);
|
||||
AzFramework::StringFunc::Path::Normalize(outputFilePath);
|
||||
AZ::StringFunc::Path::Normalize(inputFilePath);
|
||||
AZ::StringFunc::Path::Normalize(outputFilePath);
|
||||
if (task == s_taskRegisterBuilder)
|
||||
{
|
||||
return HandleRegisterBuilder(inputFilePath, outputFilePath);
|
||||
@@ -1021,7 +1057,11 @@ bool AssetBuilderComponent::GetParameter(const char* paramName, AZStd::string& o
|
||||
const AzFramework::CommandLine* commandLine = nullptr;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(commandLine, &AzFramework::ApplicationRequests::GetCommandLine);
|
||||
|
||||
outValue = commandLine->GetSwitchValue(paramName, 0);
|
||||
size_t optionCount = commandLine->GetNumSwitchValues(paramName);
|
||||
if (optionCount > 0)
|
||||
{
|
||||
outValue = commandLine->GetSwitchValue(paramName, optionCount - 1);
|
||||
}
|
||||
|
||||
if (outValue.empty())
|
||||
{
|
||||
|
||||
@@ -165,5 +165,6 @@ protected:
|
||||
AZStd::unique_ptr<Job> m_queuedJob;
|
||||
|
||||
AZStd::string m_gameName;
|
||||
AZStd::string m_projectPath;
|
||||
AZStd::string m_gameCache;
|
||||
};
|
||||
|
||||
@@ -26,62 +26,6 @@ namespace AssetBuilder
|
||||
|
||||
using AssetBuilderAppTest = AllocatorsFixture;
|
||||
|
||||
TEST_F(AssetBuilderAppTest, GetAppRootArg_AssetBuilderAppNoArgs_NoExtraction)
|
||||
{
|
||||
AssetBuilderApplication app(nullptr, nullptr);
|
||||
|
||||
char appRootBuffer[AZ_MAX_PATH_LEN];
|
||||
ASSERT_FALSE(app.GetOptionalAppRootArg(appRootBuffer, AZ_MAX_PATH_LEN));
|
||||
}
|
||||
|
||||
TEST_F(AssetBuilderAppTest, GetAppRootArg_AssetBuilderAppQuotedAppRoot_Success)
|
||||
{
|
||||
#if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
|
||||
const char* appRootArg = R"str(-approot="C:\path\to\app\root\")str";
|
||||
const char* expectedResult = R"str(C:\path\to\app\root\)str";
|
||||
#else
|
||||
const char* appRootArg = R"str(-approot="/path/to/app/root")str";
|
||||
const char* expectedResult = R"str(/path/to/app/root/)str";
|
||||
#endif
|
||||
|
||||
const char* argArray[] = {
|
||||
appRootArg
|
||||
};
|
||||
int argc = AZ_ARRAY_SIZE(argArray);
|
||||
char** argv = const_cast<char**>(argArray); // this is unfortunately necessary to get around osx's strict non-const string literal stance
|
||||
|
||||
AssetBuilderApplication app(&argc, &argv);
|
||||
|
||||
char appRootBuffer[AZ_MAX_PATH_LEN] = { 0 };
|
||||
|
||||
ASSERT_TRUE(app.GetOptionalAppRootArg(appRootBuffer, AZ_ARRAY_SIZE(appRootBuffer)));
|
||||
ASSERT_STREQ(appRootBuffer, expectedResult);
|
||||
}
|
||||
|
||||
TEST_F(AssetBuilderAppTest, GetAppRootArg_AssetBuilderAppNoQuotedAppRoot_Success)
|
||||
{
|
||||
#if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
|
||||
const char* appRootArg = R"str(-approot=C:\path\to\app\root\)str";
|
||||
const char* expectedResult = R"str(C:\path\to\app\root\)str";
|
||||
#else
|
||||
const char* appRootArg = R"str(-approot=/path/to/app/root)str";
|
||||
const char* expectedResult = R"str(/path/to/app/root/)str";
|
||||
#endif
|
||||
|
||||
const char* argArray[] = {
|
||||
appRootArg
|
||||
};
|
||||
int argc = AZ_ARRAY_SIZE(argArray);
|
||||
char** argv = const_cast<char**>(argArray); // this is unfortunately necessary to get around osx's strict non-const string literal stance
|
||||
|
||||
AssetBuilderApplication app(&argc, &argv);
|
||||
|
||||
char appRootBuffer[AZ_MAX_PATH_LEN] = { 0 };
|
||||
|
||||
ASSERT_TRUE(app.GetOptionalAppRootArg(appRootBuffer, AZ_ARRAY_SIZE(appRootBuffer)));
|
||||
ASSERT_STREQ(appRootBuffer, expectedResult);
|
||||
}
|
||||
|
||||
TEST_F(AssetBuilderAppTest, AssetBuilder_EditorScriptingComponents_Exists)
|
||||
{
|
||||
AssetBuilderApplication app(nullptr, nullptr);
|
||||
|
||||
@@ -20,13 +20,7 @@ int main(int argc, char** argv)
|
||||
traceMessageHook.EnableTraceContext(true);
|
||||
AZ::Debug::Trace::HandleExceptions(true);
|
||||
|
||||
// Perform an additional check for an override app root argument, and set it in the startup params if appropriate
|
||||
char destinationRootArgBuffer[AZ_MAX_PATH_LEN];
|
||||
AZ::ComponentApplication::StartupParameters startupParams;
|
||||
if (app.GetOptionalAppRootArg(destinationRootArgBuffer, AZ_MAX_PATH_LEN))
|
||||
{
|
||||
startupParams.m_appRootOverride = destinationRootArgBuffer;
|
||||
}
|
||||
startupParams.m_loadDynamicModules = false;
|
||||
|
||||
app.Start(AzFramework::Application::Descriptor(), startupParams);
|
||||
|
||||
@@ -494,8 +494,8 @@ namespace AssetBuilderSDK
|
||||
}
|
||||
#endif // defined(ENABLE_LEGACY_PLATFORMFLAGS_SUPPORT)
|
||||
|
||||
PlatformInfo::PlatformInfo(const char* identifier, const AZStd::unordered_set<AZStd::string>& tags)
|
||||
: m_identifier(identifier)
|
||||
PlatformInfo::PlatformInfo(AZStd::string identifier, const AZStd::unordered_set<AZStd::string>& tags)
|
||||
: m_identifier(AZStd::move(identifier))
|
||||
, m_tags(tags)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ namespace AssetBuilderSDK
|
||||
};
|
||||
|
||||
AZStd::string m_pattern;
|
||||
PatternType m_type;
|
||||
PatternType m_type{};
|
||||
|
||||
AssetBuilderPattern() = default;
|
||||
AssetBuilderPattern(const AssetBuilderPattern& src) = default;
|
||||
@@ -221,8 +221,8 @@ namespace AssetBuilderSDK
|
||||
AssetBuilderSDK::AssetBuilderPattern m_pattern;
|
||||
RegexType m_regex;
|
||||
AZStd::string m_errorString;
|
||||
bool m_isRegex;
|
||||
bool m_isValid;
|
||||
bool m_isRegex{};
|
||||
bool m_isValid{};
|
||||
};
|
||||
|
||||
//!Information that builders will send to the assetprocessor
|
||||
@@ -503,7 +503,7 @@ namespace AssetBuilderSDK
|
||||
AZStd::unordered_set<AZStd::string> m_tags; ///< The tags like "console" or "tools" on that platform
|
||||
|
||||
PlatformInfo() = default;
|
||||
PlatformInfo(const char* identifier, const AZStd::unordered_set<AZStd::string>& tags);
|
||||
PlatformInfo(AZStd::string identifier, const AZStd::unordered_set<AZStd::string>& tags);
|
||||
bool operator==(const PlatformInfo& other);
|
||||
|
||||
///! utility function. It just searches the set for you:
|
||||
|
||||
@@ -176,6 +176,71 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
VALUES LY_CMAKE_BINARY_DIR="${CMAKE_BINARY_DIR}"
|
||||
)
|
||||
|
||||
ly_add_target_files(
|
||||
TARGETS
|
||||
AssetProcessor.Tests
|
||||
FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testdata/config_broken_badplatform/AssetProcessorPlatformConfig.ini
|
||||
OUTPUT_SUBDIRECTORY
|
||||
testdata/config_broken_badplatform
|
||||
)
|
||||
ly_add_target_files(
|
||||
TARGETS
|
||||
AssetProcessor.Tests
|
||||
FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testdata/config_broken_noscans/AssetProcessorPlatformConfig.ini
|
||||
OUTPUT_SUBDIRECTORY
|
||||
testdata/config_broken_noscans
|
||||
)
|
||||
ly_add_target_files(
|
||||
TARGETS
|
||||
AssetProcessor.Tests
|
||||
FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testdata/config_broken_recognizers/AssetProcessorPlatformConfig.ini
|
||||
OUTPUT_SUBDIRECTORY
|
||||
testdata/config_broken_recognizers
|
||||
)
|
||||
ly_add_target_files(
|
||||
TARGETS
|
||||
AssetProcessor.Tests
|
||||
FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testdata/config_broken_noplatform/AssetProcessorPlatformConfig.ini
|
||||
OUTPUT_SUBDIRECTORY
|
||||
testdata/config_broken_noplatform
|
||||
)
|
||||
ly_add_target_files(
|
||||
TARGETS
|
||||
AssetProcessor.Tests
|
||||
FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testdata/config_regular/AssetProcessorPlatformConfig.ini
|
||||
OUTPUT_SUBDIRECTORY
|
||||
testdata/config_regular
|
||||
)
|
||||
ly_add_target_files(
|
||||
TARGETS
|
||||
AssetProcessor.Tests
|
||||
FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testdata/config_regular_platform_scanfolder/AssetProcessorPlatformConfig.ini
|
||||
OUTPUT_SUBDIRECTORY
|
||||
testdata/config_regular_platform_scanfolder
|
||||
)
|
||||
ly_add_target_files(
|
||||
TARGETS
|
||||
AssetProcessor.Tests
|
||||
FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testdata/EmptyDummyProject/AssetProcessorGamePlatformConfig.ini
|
||||
OUTPUT_SUBDIRECTORY
|
||||
testdata/EmptyDummyProject
|
||||
)
|
||||
ly_add_target_files(
|
||||
TARGETS
|
||||
AssetProcessor.Tests
|
||||
FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testdata/DummyProject/AssetProcessorGamePlatformConfig.ini
|
||||
OUTPUT_SUBDIRECTORY
|
||||
testdata/DummyProject
|
||||
)
|
||||
|
||||
# Have the AssetProcessorTest use the LY_CMAKE_TARGET define of AssetProcessorBatch for the purpose
|
||||
# of looking up the generated cmake build dependencies settings registry .setreg file
|
||||
# It is tied to the UnitTestRunner.cpp file
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
testdata/unittests.qrc
|
||||
testdata/config_broken_badplatform/AssetProcessorPlatformConfig.ini
|
||||
testdata/config_broken_noplatform/AssetProcessorPlatformConfig.ini
|
||||
testdata/config_broken_noscans/AssetProcessorPlatformConfig.ini
|
||||
|
||||
@@ -12,11 +12,12 @@
|
||||
|
||||
#include "native/AssetManager/AssetCatalog.h"
|
||||
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/std/string/wildcard.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzToolsFramework/API/AssetDatabaseBus.h>
|
||||
#include <AzFramework/FileTag/FileTagBus.h>
|
||||
#include <AzFramework/FileTag/FileTag.h>
|
||||
#include <AzToolsFramework/API/AssetDatabaseBus.h>
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include "PathDependencyManager.h"
|
||||
@@ -45,14 +46,12 @@ namespace AssetProcessor
|
||||
m_absoluteDevFolderPath[0] = 0;
|
||||
m_absoluteDevGameFolderPath[0] = 0;
|
||||
|
||||
AZStd::string appRoot;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(appRoot, &AzFramework::ApplicationRequests::GetAppRoot);
|
||||
azstrcpy(m_absoluteDevFolderPath, AZ_MAX_PATH_LEN, appRoot.c_str());
|
||||
|
||||
AZStd::string gameFolderPath;
|
||||
AzFramework::StringFunc::Path::Join(appRoot.c_str(), AssetUtilities::ComputeGameName().toUtf8().constData(), gameFolderPath);
|
||||
azstrcpy(m_absoluteDevGameFolderPath, AZ_MAX_PATH_LEN, gameFolderPath.c_str());
|
||||
AZStd::string engineRoot;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot);
|
||||
azstrcpy(m_absoluteDevFolderPath, AZ_MAX_PATH_LEN, engineRoot.c_str());
|
||||
|
||||
AZStd::string gameFolderPath{AssetUtilities::ComputeProjectPath().toUtf8().constData()};
|
||||
azstrcpy(m_absoluteDevGameFolderPath, AZ_MAX_PATH_LEN, gameFolderPath.c_str());
|
||||
|
||||
AssetUtilities::ComputeProjectCacheRoot(m_cacheRootDir);
|
||||
|
||||
@@ -340,9 +339,13 @@ namespace AssetProcessor
|
||||
}
|
||||
else
|
||||
{
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
AZ::SettingsRegistryInterface::FixedValueString cacheRootFolder;
|
||||
settingsRegistry->Get(cacheRootFolder, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder);
|
||||
|
||||
QString tempRegistryFile = QString("%1/%2").arg(workSpace).arg("assetcatalog.xml.tmp");
|
||||
QString platformGameDir = QString("%1/%2/").arg(m_cacheRoot.absoluteFilePath(platform)).arg(AssetUtilities::ComputeGameName().toLower());
|
||||
QString actualRegistryFile = QString("%1%2").arg(platformGameDir).arg("assetcatalog.xml");
|
||||
QString platformCacheDir = QString::fromUtf8(cacheRootFolder.c_str(), aznumeric_cast<int>(cacheRootFolder.size()));
|
||||
QString actualRegistryFile = QString("%1/%2").arg(platformCacheDir).arg("assetcatalog.xml");
|
||||
|
||||
AZ_TracePrintf(AssetProcessor::DebugChannel, "Creating asset catalog: %s --> %s\n", tempRegistryFile.toUtf8().constData(), actualRegistryFile.toUtf8().constData());
|
||||
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
|
||||
@@ -352,12 +355,12 @@ namespace AssetProcessor
|
||||
AZ::IO::FileIOBase::GetInstance()->Close(fileHandle);
|
||||
|
||||
// Make sure that the destination folder of the registry file exists
|
||||
QDir registryDir(platformGameDir);
|
||||
QDir registryDir(platformCacheDir);
|
||||
if (!registryDir.exists())
|
||||
{
|
||||
QString absPath = registryDir.absolutePath();
|
||||
bool makeDirResult = AZ::IO::SystemFile::CreateDir(absPath.toUtf8().constData());
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, makeDirResult, "Failed create folder %s", platformGameDir.toUtf8().constData());
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, makeDirResult, "Failed create folder %s", platformCacheDir.toUtf8().constData());
|
||||
}
|
||||
|
||||
// if we succeeded in doing this, then use "rename" to move the file over the previous copy.
|
||||
@@ -488,9 +491,7 @@ namespace AssetProcessor
|
||||
AZ::Data::AssetId assetId(combined.m_sourceGuid, combined.m_subID);
|
||||
|
||||
// relative file path is gotten by removing the platform and game from the product name
|
||||
QString relativeProductPath = combined.m_productName.c_str();
|
||||
relativeProductPath = relativeProductPath.right(relativeProductPath.length() - relativeProductPath.indexOf('/') - 1); // remove PLATFORM and an extra slash
|
||||
relativeProductPath = relativeProductPath.right(relativeProductPath.length() - relativeProductPath.indexOf('/') - 1); // remove GAMENAME and an extra slash
|
||||
QString relativeProductPath = AssetUtilities::StripAssetPlatform(combined.m_productName);
|
||||
QString fullProductPath = m_cacheRoot.absoluteFilePath(combined.m_productName.c_str());
|
||||
|
||||
AZ::Data::AssetInfo info;
|
||||
@@ -1133,31 +1134,17 @@ namespace AssetProcessor
|
||||
return assetInfo;
|
||||
}
|
||||
|
||||
bool ConvertDatabaseProductPathToProductFileName(QString dbPath, QString& productFileName)
|
||||
bool ConvertDatabaseProductPathToProductFilename(AZStd::string_view dbPath, QString& productFileName)
|
||||
{
|
||||
QString gameName = AssetUtilities::ComputeGameName();
|
||||
bool result = false;
|
||||
int gameNameIndex = dbPath.indexOf(gameName, 0, Qt::CaseInsensitive);
|
||||
if (gameNameIndex != -1)
|
||||
// Always strip the leading directory from the product path
|
||||
// The leading directory can be either an asset platform path or a subfolder
|
||||
AZ::StringFunc::TokenizeNext(dbPath, AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR);
|
||||
if (!dbPath.empty())
|
||||
{
|
||||
//we will now remove the gameName and the native separator to get the assetId
|
||||
dbPath.remove(0, gameNameIndex + gameName.length() + 1); // adding one for the native separator also
|
||||
result = true;
|
||||
productFileName = QString::fromUtf8(dbPath.data(), aznumeric_cast<int>(dbPath.size()));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//we will just remove the platform and the native separator to get the assetId
|
||||
int separatorIndex = dbPath.indexOf("/");
|
||||
if (separatorIndex != -1)
|
||||
{
|
||||
dbPath.remove(0, separatorIndex + 1); // adding one for the native separator
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
productFileName = dbPath;
|
||||
|
||||
return result;
|
||||
return false;
|
||||
}
|
||||
|
||||
void AssetCatalog::ProcessGetRelativeProductPathFromFullSourceOrProductPathRequest(const AZStd::string& fullPath, AZStd::string& relativeProductPath)
|
||||
@@ -1166,7 +1153,7 @@ namespace AssetProcessor
|
||||
QString normalizedSourceOrProductPath = AssetUtilities::NormalizeFilePath(sourceOrProductPath);
|
||||
|
||||
QString productFileName;
|
||||
int resultCode = 0;
|
||||
bool resultCode = false;
|
||||
QDir inputPath(normalizedSourceOrProductPath);
|
||||
|
||||
AZ_TracePrintf(AssetProcessor::DebugChannel, "ProcessGetRelativeProductPath: %s...\n", sourceOrProductPath.toUtf8().constData());
|
||||
@@ -1175,7 +1162,7 @@ namespace AssetProcessor
|
||||
{
|
||||
//if the path coming in is already a relative path,we just send it back
|
||||
productFileName = sourceOrProductPath;
|
||||
resultCode = 1;
|
||||
resultCode = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1196,7 +1183,7 @@ namespace AssetProcessor
|
||||
// Now after removing the cache root,normalizedInputAssetPath can either be $Platform/$Game/xxx/yyy or something like $Platform/zzz
|
||||
// and the corresponding assetId have to be either xxx/yyy or zzz
|
||||
|
||||
resultCode = ConvertDatabaseProductPathToProductFileName(normalizedSourceOrProductPath, productFileName);
|
||||
resultCode = ConvertDatabaseProductPathToProductFilename(normalizedSourceOrProductPath.toUtf8().data(), productFileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1224,12 +1211,12 @@ namespace AssetProcessor
|
||||
|
||||
if (m_db->GetProductsBySourceName(relativeName, products))
|
||||
{
|
||||
resultCode = ConvertDatabaseProductPathToProductFileName(products[0].m_productName.c_str(), productFileName);
|
||||
resultCode = ConvertDatabaseProductPathToProductFilename(products[0].m_productName, productFileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
productFileName = relativeName;
|
||||
resultCode = 1;
|
||||
resultCode = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1242,7 +1229,6 @@ namespace AssetProcessor
|
||||
}
|
||||
|
||||
relativeProductPath = productFileName.toUtf8().data();
|
||||
|
||||
}
|
||||
|
||||
void AssetCatalog::ProcessGetFullSourcePathFromRelativeProductPathRequest(const AZStd::string& relPath, AZStd::string& fullSourcePath)
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace AssetProcessor
|
||||
PathDependencyManager::PathDependencyManager(AZStd::shared_ptr<AssetDatabaseConnection> stateData, PlatformConfiguration* platformConfig)
|
||||
: m_stateData(stateData), m_platformConfig(platformConfig)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
void PathDependencyManager::SaveUnresolvedDependenciesToDatabase(AssetBuilderSDK::ProductPathDependencySet& unresolvedDependencies, const AzToolsFramework::AssetDatabase::ProductDatabaseEntry& productEntry, const AZStd::string& platform)
|
||||
@@ -55,7 +55,7 @@ namespace AssetProcessor
|
||||
// other problems. This string says that something went wrong in this function.
|
||||
AZStd::string("INVALID_PATH"),
|
||||
dependencyType);
|
||||
|
||||
|
||||
AZStd::string path = AssetUtilities::NormalizeFilePath(unresolvedPathDep.m_dependencyPath.c_str()).toUtf8().constData();
|
||||
bool isExactDependency = IsExactDependency(path);
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace AssetProcessor
|
||||
dependencyContainer.push_back(placeholderDependency);
|
||||
}
|
||||
|
||||
if(!m_stateData->UpdateProductDependencies(dependencyContainer))
|
||||
if (!m_stateData->UpdateProductDependencies(dependencyContainer))
|
||||
{
|
||||
AZ_Error(AssetProcessor::ConsoleChannel, false, "Failed to save unresolved dependencies to database for product %d (%s)",
|
||||
productEntry.m_productID, productEntry.m_productName.c_str());
|
||||
@@ -111,8 +111,8 @@ namespace AssetProcessor
|
||||
const DependencyProductMap& excludedPathDependencyIds = handleProductDependencies ? exclusionMaps.m_productPathDependencyIds : exclusionMaps.m_sourcePathDependencyIds;
|
||||
const DependencyProductMap& excludedWildcardPathDependencyIds = handleProductDependencies ? exclusionMaps.m_wildcardProductPathDependencyIds : exclusionMaps.m_wildcardSourcePathDependencyIds;
|
||||
|
||||
// strip path of /<platform>/<project>/
|
||||
AZStd::string strippedPath = handleProductDependencies ? StripPlatformAndProject(assetName) : sourceEntry.m_sourceName;
|
||||
// strip asset platform from path
|
||||
AZStd::string strippedPath = handleProductDependencies ? AssetUtilities::StripAssetPlatform(assetName).toUtf8().constData() : sourceEntry.m_sourceName;
|
||||
SanitizeForDatabase(strippedPath);
|
||||
|
||||
auto unresolvedIter = excludedPathDependencyIds.find(ExcludedDependenciesSymbol + strippedPath);
|
||||
@@ -137,13 +137,6 @@ namespace AssetProcessor
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string PathDependencyManager::StripPlatformAndProject(AZStd::string_view productName)
|
||||
{
|
||||
auto nextSlash = productName.find('/'); // platform/
|
||||
nextSlash = productName.find('/', nextSlash + 1) + 1; // project/
|
||||
return productName.substr(nextSlash, productName.size() - nextSlash);
|
||||
}
|
||||
|
||||
PathDependencyManager::DependencyProductMap& PathDependencyManager::SelectMap(MapSet& mapSet, bool wildcard, AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntry::DependencyType type)
|
||||
{
|
||||
const bool isSource = type == AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntry::DependencyType::ProductDep_SourceFile;
|
||||
@@ -193,7 +186,7 @@ namespace AssetProcessor
|
||||
|
||||
void PathDependencyManager::NotifyResolvedDependencies(const AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntryContainer& dependencyContainer) const
|
||||
{
|
||||
if(!m_dependencyResolvedCallback)
|
||||
if (!m_dependencyResolvedCallback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -226,7 +219,7 @@ namespace AssetProcessor
|
||||
const bool isExactDependency = IsExactDependency(productDependencyDatabaseEntry.m_unresolvedPath);
|
||||
AZ::s64 dependencyId = isExactDependency ? productDependencyDatabaseEntry.m_productDependencyID : AzToolsFramework::AssetDatabase::InvalidEntryId;
|
||||
|
||||
if(isSourceDependency && !isExactDependency && matchedPath == sourceNameWithScanFolder)
|
||||
if (isSourceDependency && !isExactDependency && matchedPath == sourceNameWithScanFolder)
|
||||
{
|
||||
// Since we did a search for the source 2 different ways, filter one out
|
||||
// Scanfolder-prefixes are only for exact dependencies
|
||||
@@ -239,14 +232,14 @@ namespace AssetProcessor
|
||||
AZStd::vector<AZStd::pair<DependencyProductIdInfo, bool>> exclusions; // bool = is exact dependency
|
||||
GetMatchedExclusions(sourceEntry, matchedProduct, exclusions, productDependencyDatabaseEntry.m_dependencyType, exclusionMaps);
|
||||
|
||||
if(!exclusions.empty())
|
||||
if (!exclusions.empty())
|
||||
{
|
||||
bool isExclusionForThisProduct = false;
|
||||
bool isExclusionExact = false;
|
||||
|
||||
for (const auto& exclusionPair : exclusions)
|
||||
{
|
||||
if(exclusionPair.first.m_productId == productDependencyDatabaseEntry.m_productPK && exclusionPair.first.m_platform == productDependencyDatabaseEntry.m_platform)
|
||||
if (exclusionPair.first.m_productId == productDependencyDatabaseEntry.m_productPK && exclusionPair.first.m_platform == productDependencyDatabaseEntry.m_platform)
|
||||
{
|
||||
isExclusionExact = exclusionPair.second;
|
||||
isExclusionForThisProduct = true;
|
||||
@@ -254,7 +247,7 @@ namespace AssetProcessor
|
||||
}
|
||||
}
|
||||
|
||||
if(isExclusionForThisProduct)
|
||||
if (isExclusionForThisProduct)
|
||||
{
|
||||
if (isExactDependency && isExclusionExact)
|
||||
{
|
||||
@@ -314,8 +307,8 @@ namespace AssetProcessor
|
||||
{
|
||||
const AZStd::string& productName = productEntry.m_productName;
|
||||
|
||||
// strip path of /<platform>/<project>/
|
||||
AZStd::string strippedPath = StripPlatformAndProject(productName);
|
||||
// strip path of the <platform>/
|
||||
AZStd::string strippedPath = AssetUtilities::StripAssetPlatform(productName).toUtf8().constData();
|
||||
SanitizeForDatabase(strippedPath);
|
||||
|
||||
searchPaths.push_back(strippedPath);
|
||||
@@ -347,7 +340,7 @@ namespace AssetProcessor
|
||||
AzToolsFramework::AssetDatabase::ProductDatabaseEntryContainer matchedProducts;
|
||||
|
||||
// Figure out the list of products to work with, for a source match, use all the products, otherwise just use the matched products
|
||||
if(isSourceDependency)
|
||||
if (isSourceDependency)
|
||||
{
|
||||
matchedProducts = products;
|
||||
}
|
||||
@@ -357,11 +350,11 @@ namespace AssetProcessor
|
||||
{
|
||||
const AZStd::string& productName = productEntry.m_productName;
|
||||
|
||||
// strip path of /<platform>/<project>/
|
||||
AZStd::string strippedPath = StripPlatformAndProject(productName);
|
||||
// strip path of the leading asset platform /<platform>
|
||||
AZStd::string strippedPath = AssetUtilities::StripAssetPlatform(productName).toUtf8().constData();
|
||||
SanitizeForDatabase(strippedPath);
|
||||
|
||||
if(strippedPath == matchedPath)
|
||||
if (strippedPath == matchedPath)
|
||||
{
|
||||
matchedProducts.push_back(productEntry);
|
||||
}
|
||||
@@ -373,7 +366,7 @@ namespace AssetProcessor
|
||||
}
|
||||
|
||||
// Save everything to the db
|
||||
if(!m_stateData->UpdateProductDependencies(dependencyContainer))
|
||||
if (!m_stateData->UpdateProductDependencies(dependencyContainer))
|
||||
{
|
||||
AZ_Error("PathDependencyManager", false, "Failed to update product dependencies");
|
||||
}
|
||||
@@ -386,7 +379,7 @@ namespace AssetProcessor
|
||||
|
||||
void CleanupPathDependency(AssetBuilderSDK::ProductPathDependency& pathDependency)
|
||||
{
|
||||
if(pathDependency.m_dependencyType == AssetBuilderSDK::ProductPathDependencyType::SourceFile)
|
||||
if (pathDependency.m_dependencyType == AssetBuilderSDK::ProductPathDependencyType::SourceFile)
|
||||
{
|
||||
// Nothing to cleanup if the dependency type was already pointing at source.
|
||||
return;
|
||||
@@ -411,7 +404,7 @@ namespace AssetProcessor
|
||||
{
|
||||
const AZ::Data::ProductDependencyInfo::ProductDependencyFlags productDependencyFlags =
|
||||
AZ::Data::ProductDependencyInfo::CreateFlags(AZ::Data::AssetLoadBehavior::NoLoad);
|
||||
const QString gameName = AssetUtilities::ComputeGameName();
|
||||
|
||||
AZStd::vector<AssetBuilderSDK::ProductDependency> excludedDeps;
|
||||
|
||||
// Check the path dependency set and find any conflict (include and exclude the same path dependency)
|
||||
@@ -458,9 +451,8 @@ namespace AssetProcessor
|
||||
{
|
||||
AzToolsFramework::AssetDatabase::ProductDatabaseEntryContainer productInfoContainer;
|
||||
QString productNameWithPlatform = QString("%1%2%3").arg(platform.c_str(), AZ_CORRECT_DATABASE_SEPARATOR_STRING, dependencyPathSearch.c_str());
|
||||
QString productNameWithPlatformAndGameName = QString("%1%2%3%2%4").arg(platform.c_str(), AZ_CORRECT_DATABASE_SEPARATOR_STRING, gameName, dependencyPathSearch.c_str());
|
||||
|
||||
if (AzFramework::StringFunc::Equal(productNameWithPlatformAndGameName.toUtf8().data(), productName.c_str()))
|
||||
if (AzFramework::StringFunc::Equal(productNameWithPlatform.toUtf8().data(), productName.c_str()))
|
||||
{
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, false,
|
||||
"Invalid dependency: Product Asset ( %s ) has listed itself as one of its own Product Dependencies.",
|
||||
@@ -471,18 +463,15 @@ namespace AssetProcessor
|
||||
|
||||
if (isExactDependency)
|
||||
{
|
||||
m_stateData->GetProductsByProductName(productNameWithPlatformAndGameName, productInfoContainer);
|
||||
// Not all products will be in the game subfolder.
|
||||
// Items in dev, like bootstrap.cfg, end up in just the root platform folder.
|
||||
// These two checks search for products in both location.
|
||||
// Example: If a path dependency was just "bootstrap.cfg" in SamplesProject on PC, this would search both
|
||||
// "cache/SamplesProject/pc/bootstrap.cfg" and "cache/SamplesProject/pc/SamplesProject/bootstrap.cfg".
|
||||
// Search for products in the cache platform folder
|
||||
// Example: If a path dependency is "test1.asset" in SamplesProject on PC, this would search
|
||||
// "SamplesProject/Cache/pc/test1.asset"
|
||||
m_stateData->GetProductsByProductName(productNameWithPlatform, productInfoContainer);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
m_stateData->GetProductsLikeProductName(productNameWithPlatformAndGameName, AzToolsFramework::AssetDatabase::AssetDatabaseConnection::LikeType::Raw, productInfoContainer);
|
||||
m_stateData->GetProductsLikeProductName(productNameWithPlatform, AzToolsFramework::AssetDatabase::AssetDatabaseConnection::LikeType::Raw, productInfoContainer);
|
||||
}
|
||||
|
||||
// See if path matches any product files
|
||||
|
||||
@@ -76,9 +76,6 @@ namespace AssetProcessor
|
||||
/// Returns false if a path contains wildcards, true otherwise
|
||||
static bool IsExactDependency(AZStd::string_view path);
|
||||
|
||||
/// Removes /platform/project/ from the start of a product path
|
||||
static AZStd::string StripPlatformAndProject(AZStd::string_view relativeProductPath);
|
||||
|
||||
/// Prefixes the scanFolderId to the relativePath
|
||||
AZStd::string ToScanFolderPrefixedPath(int scanFolderId, const char* relativePath) const;
|
||||
|
||||
|
||||
@@ -67,8 +67,7 @@ namespace AssetProcessor
|
||||
if (AssetUtilities::ComputeAssetRoot(assetRoot))
|
||||
{
|
||||
azstrcpy(m_absoluteDevFolderPath, AZ_MAX_PATH_LEN, assetRoot.absolutePath().toUtf8().constData());
|
||||
QString absoluteDevGameFolderPath = assetRoot.absoluteFilePath(AssetUtilities::ComputeGameName());
|
||||
azstrcpy(m_absoluteDevGameFolderPath, AZ_MAX_PATH_LEN, absoluteDevGameFolderPath.toUtf8().constData());
|
||||
azstrcpy(m_absoluteDevGameFolderPath, AZ_MAX_PATH_LEN, AssetUtilities::ComputeProjectPath().toUtf8().constData());
|
||||
}
|
||||
|
||||
using namespace AZStd::placeholders;
|
||||
@@ -1067,7 +1066,7 @@ namespace AssetProcessor
|
||||
AZStd::vector<AZStd::vector<AZ::u32> > newLegacySubIDs; // each product has a vector of legacy subids;
|
||||
for (const AssetBuilderSDK::JobProduct& product : processedAsset.m_response.m_outputProducts)
|
||||
{
|
||||
// prior products, if present, will be in the form "platform/game/subfolders/productfile", convert
|
||||
// prior products, if present, will be in the form "platform/subfolders/productfile", convert
|
||||
// our new products to the same thing by removing the cache root
|
||||
QString newProductName = product.m_productFileName.c_str();
|
||||
newProductName = AssetUtilities::NormalizeFilePath(newProductName);
|
||||
@@ -1093,7 +1092,8 @@ namespace AssetProcessor
|
||||
//This is the legacy product guid, its only use is for backward compatibility as before the asset id's guid was created off of the relative product name.
|
||||
// Right now when we query for an asset guid we first match on the source guid which is correct and secondarily match on the product guid. Eventually this will go away.
|
||||
newProductName = newProductName.right(newProductName.length() - newProductName.indexOf('/') - 1); // remove PLATFORM and an extra slash
|
||||
newProductName = newProductName.right(newProductName.length() - newProductName.indexOf('/') - 1); // remove GAMENAME and an extra slash
|
||||
// Strip the <asset_platform> from the front of a relative product path
|
||||
newProductName = AssetUtilities::StripAssetPlatform(newProductName.toUtf8().constData());
|
||||
newProduct.m_legacyGuid = AZ::Uuid::CreateName(newProductName.toUtf8().constData());
|
||||
|
||||
//push back the new product into the new products list
|
||||
@@ -1115,7 +1115,7 @@ namespace AssetProcessor
|
||||
// we need to delete these product files from the disk as they no longer exist and inform everyone we did so
|
||||
for (const auto& priorProduct : priorProducts)
|
||||
{
|
||||
// product name will be in the form "platform/game/relativeProductPath"
|
||||
// product name will be in the form "platform/relativeProductPath"
|
||||
// and will always already be a lowercase string, because its relative to the cache.
|
||||
QString productName = priorProduct.m_productName.c_str();
|
||||
|
||||
@@ -1123,10 +1123,8 @@ namespace AssetProcessor
|
||||
// this is case sensitive since it refers to a real location on disk.
|
||||
QString fullProductPath = m_cacheRootDir.absoluteFilePath(productName);
|
||||
|
||||
// relative file path is gotten by removing the platform and game from the product name
|
||||
QString relativeProductPath = productName;
|
||||
relativeProductPath = relativeProductPath.right(relativeProductPath.length() - relativeProductPath.indexOf('/') - 1); // remove PLATFORM and an extra slash
|
||||
relativeProductPath = relativeProductPath.right(relativeProductPath.length() - relativeProductPath.indexOf('/') - 1); // remove GAMENAME and an extra slash
|
||||
// Strip the <asset_platform> from the front of a relative product path
|
||||
QString relativeProductPath = AssetUtilities::StripAssetPlatform(priorProduct.m_productName);
|
||||
|
||||
AZ::Data::AssetId assetId(source.m_sourceGuid, priorProduct.m_subID);
|
||||
|
||||
@@ -1302,16 +1300,15 @@ namespace AssetProcessor
|
||||
AzToolsFramework::AssetDatabase::ProductDatabaseEntry& newProduct = pair.first;
|
||||
AZStd::vector<AZ::u32>& subIds = newLegacySubIDs[productIdx];
|
||||
|
||||
// product name will be in the form "platform/game/relativeProductPath"
|
||||
// product name will be in the form "platform/relativeProductPath"
|
||||
QString productName = QString::fromUtf8(newProduct.m_productName.c_str());
|
||||
|
||||
// the full file path is gotten by adding the product name to the cache root
|
||||
QString fullProductPath = m_cacheRootDir.absoluteFilePath(productName);
|
||||
|
||||
// relative file path is gotten by removing the platform and game from the product name
|
||||
QString relativeProductPath = productName;
|
||||
relativeProductPath = relativeProductPath.right(relativeProductPath.length() - relativeProductPath.indexOf('/') - 1); // remove PLATFORM and an extra slash
|
||||
relativeProductPath = relativeProductPath.right(relativeProductPath.length() - relativeProductPath.indexOf('/') - 1); // remove GAMENAME and an extra slash
|
||||
// Strip the <asset_platform> from the front of a relative product path
|
||||
QString relativeProductPath = AssetUtilities::StripAssetPlatform(productName.toUtf8().constData());
|
||||
|
||||
AssetNotificationMessage message(relativeProductPath.toUtf8().constData(), AssetNotificationMessage::AssetChanged, newProduct.m_assetType, processedAsset.m_entry.m_platformInfo.m_identifier.c_str());
|
||||
AZ::Data::AssetId assetId(source.m_sourceGuid, newProduct.m_subID);
|
||||
@@ -1572,10 +1569,10 @@ namespace AssetProcessor
|
||||
// this might be interesting, but only if its a known product!
|
||||
// the dictionary in statedata stores only the relative path, not the platform.
|
||||
// which means right now we have, for example
|
||||
// d:/game/root/Cache/SamplesProject/IOS/SamplesProject/textures/favorite.tga
|
||||
// ^^^^^^^^^^^^ engine root
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cache root
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ platform root
|
||||
// d:/SamplesProject/Cache/ios/textures/favorite.tga
|
||||
// ^^^^^^^^^ projectroot
|
||||
// ^^^^^^^^^^^^^^^^^^^^^ cache root
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^ platform root
|
||||
{
|
||||
QMutexLocker locker(&m_processingJobMutex);
|
||||
auto found = m_processingProductInfoList.find(fullProductFile.toUtf8().constData());
|
||||
@@ -1596,7 +1593,7 @@ namespace AssetProcessor
|
||||
QString relativeProductFile = m_cacheRootDir.relativeFilePath(fullProductFile);
|
||||
|
||||
//platform
|
||||
QString platform = relativeProductFile;// currently <platform>/<gamename>/<relative_asset_path>
|
||||
QString platform = relativeProductFile;// currently <platform>/<relative_asset_path>
|
||||
platform = platform.left(platform.indexOf('/')); // also consume the extra slash - remove PLATFORM
|
||||
|
||||
//we are going to force the processor to re process the source file associated with this product
|
||||
@@ -1652,11 +1649,8 @@ namespace AssetProcessor
|
||||
}
|
||||
}
|
||||
|
||||
// currently <platform>/<gamename>/<relative_asset_path>
|
||||
// remove PLATFORM and GAMENAME so that we only have the relative asset path which should match the db
|
||||
QString relativePath(relativeProductFile);
|
||||
relativePath = relativePath.right(relativePath.length() - relativePath.indexOf('/') - 1); // also consume the extra slash - remove PLATFORM
|
||||
relativePath = relativePath.right(relativePath.length() - relativePath.indexOf('/') - 1); // also consume the extra slash - remove GAMENAME
|
||||
// Strip the <asset_platform> from the front of a relative product path
|
||||
QString relativePath = AssetUtilities::StripAssetPlatform(relativeProductFile.toUtf8().constData());
|
||||
|
||||
//set the fingerprint on the job that made this product
|
||||
for (auto& job : jobs)
|
||||
@@ -1687,9 +1681,9 @@ namespace AssetProcessor
|
||||
{
|
||||
bool successfullyRemoved = true;
|
||||
// delete the products.
|
||||
// products have names like "pc/SamplesProject/textures/blah.dds" and do include platform roots!
|
||||
// products have names like "pc/textures/blah.dds" and do include platform roots!
|
||||
// this means the actual full path is something like
|
||||
// [cache root] / [platform] / [product name]
|
||||
// [cache root] / [platform]
|
||||
for (const auto& product : products)
|
||||
{
|
||||
//get the source for this product
|
||||
@@ -1700,9 +1694,7 @@ namespace AssetProcessor
|
||||
}
|
||||
|
||||
QString fullProductPath = m_cacheRootDir.absoluteFilePath(product.m_productName.c_str());
|
||||
QString relativeProductPath(product.m_productName.c_str());
|
||||
relativeProductPath = relativeProductPath.right(relativeProductPath.length() - relativeProductPath.indexOf('/') - 1); // also consume the extra slash - remove PLATFORM
|
||||
relativeProductPath = relativeProductPath.right(relativeProductPath.length() - relativeProductPath.indexOf('/') - 1); // also consume the extra slash - remove GAMENAME
|
||||
QString relativeProductPath(AssetUtilities::StripAssetPlatform(product.m_productName));
|
||||
QFileInfo productFileInfo(fullProductPath);
|
||||
if (productFileInfo.exists())
|
||||
{
|
||||
@@ -2144,15 +2136,7 @@ namespace AssetProcessor
|
||||
pathRel = QString();
|
||||
}
|
||||
|
||||
if (jobDetails.m_scanFolder->IsRoot())
|
||||
{
|
||||
// stuff which is found in the root continues to go to the root, rather than GAMENAME folder...
|
||||
lowerCasePath += pathRel;
|
||||
}
|
||||
else
|
||||
{
|
||||
lowerCasePath += "/" + AssetUtilities::ComputeGameName() + pathRel;
|
||||
}
|
||||
lowerCasePath += pathRel;
|
||||
|
||||
lowerCasePath = lowerCasePath.toLower();
|
||||
jobDetails.m_destinationPath = m_cacheRootDir.absoluteFilePath(lowerCasePath);
|
||||
@@ -3471,8 +3455,16 @@ namespace AssetProcessor
|
||||
AssetProcessor::BuilderConfigurationRequestBus::Broadcast(&AssetProcessor::BuilderConfigurationRequests::UpdateJobDescriptor, jobDescriptor.m_jobKey, jobDescriptor);
|
||||
|
||||
const AssetBuilderSDK::PlatformInfo* const infoForPlatform = m_platformConfig->GetPlatformByIdentifier(jobDescriptor.GetPlatformIdentifier().c_str());
|
||||
AZ_Assert(infoForPlatform, "Somehow, a platform for a job was created in createjobs which cannot be found in the list of enabled platforms.");
|
||||
if (infoForPlatform)
|
||||
|
||||
if (!infoForPlatform)
|
||||
{
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, infoForPlatform,
|
||||
"CODE BUG: Builder %s emitted jobs for a platform that isn't enabled (%s). This job will be "
|
||||
"discarded. Builders should check the input list of platforms and only emit jobs for platforms "
|
||||
"in that list", builderInfo.m_name.c_str(), jobDescriptor.GetPlatformIdentifier().c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
JobDetails newJob;
|
||||
newJob.m_assetBuilderDesc = builderInfo;
|
||||
@@ -4614,7 +4606,7 @@ namespace AssetProcessor
|
||||
QString metaDataFileName;
|
||||
QDir assetRoot;
|
||||
AssetUtilities::ComputeAssetRoot(assetRoot);
|
||||
QString gameName = AssetUtilities::ComputeGameName();
|
||||
QString projectPath = AssetUtilities::ComputeProjectPath();
|
||||
QString fullPathToFile(absolutePathToFileToCheck);
|
||||
|
||||
if (!m_cachedMetaFilesExistMap)
|
||||
@@ -4624,7 +4616,7 @@ namespace AssetProcessor
|
||||
for (int idx = 0; idx < m_platformConfig->MetaDataFileTypesCount(); idx++)
|
||||
{
|
||||
QPair<QString, QString> metaDataFileType = m_platformConfig->GetMetaDataFileTypeAt(idx);
|
||||
QString fullMetaPath = assetRoot.filePath(gameName + "/" + metaDataFileType.first);
|
||||
QString fullMetaPath = QDir(projectPath).filePath(metaDataFileType.first);
|
||||
if (QFileInfo::exists(fullMetaPath))
|
||||
{
|
||||
m_metaFilesWhichActuallyExistOnDisk.insert(metaDataFileType.first);
|
||||
@@ -4644,7 +4636,7 @@ namespace AssetProcessor
|
||||
|
||||
if (m_metaFilesWhichActuallyExistOnDisk.find(metaDataFileType.first) != m_metaFilesWhichActuallyExistOnDisk.end())
|
||||
{
|
||||
QString fullMetaPath = assetRoot.filePath(gameName + "/" + metaDataFileType.first);
|
||||
QString fullMetaPath = QDir(projectPath).filePath(metaDataFileType.first);
|
||||
metaDataFileName = fullMetaPath;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
|
||||
#include "native/utilities/assetUtils.h"
|
||||
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
|
||||
using namespace AZ::IO;
|
||||
@@ -114,20 +117,20 @@ void FileServer::ConnectionAdded(unsigned int connId, Connection* connection)
|
||||
{
|
||||
projectCacheRoot = QDir(projectCacheRoot.absoluteFilePath(assetPlatform));
|
||||
}
|
||||
fileIO->SetAlias("@root@", projectCacheRoot.absolutePath().toUtf8().data());
|
||||
const char* projectCachePath = projectCacheRoot.absolutePath().toUtf8().data();
|
||||
fileIO->SetAlias("@assets@", projectCachePath);
|
||||
fileIO->SetAlias("@root@", projectCachePath);
|
||||
|
||||
QString userDir = projectCacheRoot.absoluteFilePath("user");
|
||||
userDir = QDir::toNativeSeparators(userDir);
|
||||
fileIO->SetAlias("@user@", userDir.toUtf8().data());
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
AZ::IO::Path projectUserPath;
|
||||
settingsRegistry->Get(projectUserPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectUserPath);
|
||||
fileIO->SetAlias("@user@", projectUserPath.c_str());
|
||||
|
||||
QString logDir = QDir(userDir).absoluteFilePath("log");
|
||||
logDir = QDir::toNativeSeparators(logDir);
|
||||
fileIO->SetAlias("@log@", logDir.toUtf8().data());
|
||||
AZ::IO::Path logUserPath = projectUserPath / "log";
|
||||
fileIO->SetAlias("@log@", logUserPath.c_str());
|
||||
}
|
||||
|
||||
QString gameName = AssetUtilities::ComputeGameName();
|
||||
QString gameDir = projectCacheRoot.absoluteFilePath(gameName);
|
||||
gameDir = QDir::toNativeSeparators(gameDir);
|
||||
fileIO->SetAlias("@assets@", gameDir.toUtf8().data());
|
||||
|
||||
// note that the cache folder is auto-created only upon first use of VFS.
|
||||
}
|
||||
@@ -145,13 +148,20 @@ void FileServer::EnsureCacheFolderExists(int connId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (fileIO->GetAlias("@cache@"))
|
||||
if (fileIO->GetAlias("@usercache@"))
|
||||
{
|
||||
// already created.
|
||||
return;
|
||||
}
|
||||
|
||||
QString cacheDir = QDir(fileIO->GetAlias("@user@")).absoluteFilePath("cache");
|
||||
AZ::IO::FixedMaxPath cacheUserPath;
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
if (settingsRegistry->Get(cacheUserPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectUserPath))
|
||||
{
|
||||
cacheUserPath /= "Cache";
|
||||
}
|
||||
|
||||
auto cacheDir = QString::fromUtf8(cacheUserPath.c_str(), aznumeric_cast<int>(cacheUserPath.Native().size()));
|
||||
cacheDir = QDir::toNativeSeparators(cacheDir);
|
||||
// the Cache-dir is special in that we don't allow sharing of cache dirs for multiple running
|
||||
// apps of the same platform at the same time.
|
||||
@@ -207,7 +217,7 @@ void FileServer::EnsureCacheFolderExists(int connId)
|
||||
}
|
||||
#endif
|
||||
|
||||
fileIO->SetAlias("@cache@", cacheDir.toUtf8().data());
|
||||
fileIO->SetAlias("@usercache@", cacheDir.toUtf8().data());
|
||||
}
|
||||
|
||||
void FileServer::ConnectionRemoved(unsigned int connId)
|
||||
@@ -944,7 +954,7 @@ void FileServer::ProcessFileTreeRequest(unsigned int connId, unsigned int, unsig
|
||||
}
|
||||
|
||||
auto fileIO = m_fileIOs[connId];
|
||||
|
||||
|
||||
FileTreeResponse::FileList files;
|
||||
FileTreeResponse::FolderList folders;
|
||||
|
||||
@@ -954,10 +964,10 @@ void FileServer::ProcessFileTreeRequest(unsigned int connId, unsigned int, unsig
|
||||
folders.push_back("@assets@");
|
||||
untestedFolders.push_back("@assets@");
|
||||
}
|
||||
if (fileIO->IsDirectory("@cache@"))
|
||||
if (fileIO->IsDirectory("@usercache@"))
|
||||
{
|
||||
folders.push_back("@cache@");
|
||||
untestedFolders.push_back("@cache@");
|
||||
folders.push_back("@usercache@");
|
||||
untestedFolders.push_back("@usercache@");
|
||||
}
|
||||
if (fileIO->IsDirectory("@user@"))
|
||||
{
|
||||
@@ -976,7 +986,7 @@ void FileServer::ProcessFileTreeRequest(unsigned int connId, unsigned int, unsig
|
||||
}
|
||||
|
||||
AZ::IO::Result res = ResultCode::Success;
|
||||
|
||||
|
||||
while (untestedFolders.size() && res == ResultCode::Success)
|
||||
{
|
||||
AZ::OSString folderName = untestedFolders.back();
|
||||
@@ -1006,9 +1016,9 @@ void FileServer::ProcessFileTreeRequest(unsigned int connId, unsigned int, unsig
|
||||
}
|
||||
|
||||
uint32_t resultCode = static_cast<uint32_t>(res.GetResultCode());
|
||||
|
||||
|
||||
FileTreeResponse response(resultCode, files, folders);
|
||||
|
||||
|
||||
Send(connId, serial, response);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
#include <AssetBuilderSDK/AssetBuilderSDK.h>
|
||||
#include <AzCore/Settings/SettingsRegistryImpl.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/Platform/PlatformDefaults.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <native/InternalBuilders/SettingsRegistryBuilder.h>
|
||||
#include <native/utilities/PlatformConfiguration.h>
|
||||
|
||||
namespace AssetProcessor
|
||||
{
|
||||
@@ -210,6 +212,8 @@ namespace AssetProcessor
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
|
||||
AZStd::vector<AZStd::string> excludes = ReadExcludesFromRegistry();
|
||||
// Exclude the AssetProcessor settings from the game regsitry
|
||||
excludes.emplace_back(AssetProcessor::AssetProcessorSettingsKey);
|
||||
|
||||
AZStd::vector<char> scratchBuffer;
|
||||
scratchBuffer.reserve(512 * 1024); // Reserve 512kb to avoid repeatedly resizing the buffer;
|
||||
@@ -228,19 +232,16 @@ namespace AssetProcessor
|
||||
};
|
||||
|
||||
// Add the project specific specializations
|
||||
if (auto settingsRegistry = AZ::Interface<AZ::SettingsRegistryInterface>::Get(); settingsRegistry)
|
||||
auto projectName = AZ::Utils::GetProjectName();
|
||||
if (!projectName.empty())
|
||||
{
|
||||
auto projectKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/sys_game_folder", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey);
|
||||
if (AZ::SettingsRegistryInterface::FixedValueString projectName; settingsRegistry->Get(projectName, projectKey))
|
||||
for (AZ::SettingsRegistryInterface::Specializations& specialization : specializations)
|
||||
{
|
||||
for (AZ::SettingsRegistryInterface::Specializations& specialization : specializations)
|
||||
{
|
||||
specialization.Append(projectName);
|
||||
// The Game Launcher normally has a build target name of <ProjectName>Launcher
|
||||
// Add that as a specialization to pick up the gem dependencies files that are specialized
|
||||
// on a the Game Launcher target if the asset platform isn't "server"
|
||||
specialization.Append(projectName + launcherType);
|
||||
}
|
||||
specialization.Append(projectName);
|
||||
// The Game Launcher normally has a build target name of <ProjectName>Launcher
|
||||
// Add that as a specialization to pick up the gem dependencies files that are specialized
|
||||
// on a the Game Launcher target if the asset platform isn't "server"
|
||||
specialization.Append(projectName + launcherType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,8 +256,9 @@ namespace AssetProcessor
|
||||
for (AZStd::string_view platform : platformCodes)
|
||||
{
|
||||
AZ::u32 productSubID = static_cast<AZ::u32>(AZStd::hash<AZStd::string_view>{}(platform)); // Deliberately ignoring half the bits.
|
||||
for (size_t i = 0; i < AZ_ARRAY_SIZE(specializations); ++i)
|
||||
for (size_t i = 0; i < AZStd::size(specializations); ++i)
|
||||
{
|
||||
const AZ::SettingsRegistryInterface::Specializations& specialization = specializations[i];
|
||||
if (m_isShuttingDown)
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
|
||||
@@ -269,12 +271,12 @@ namespace AssetProcessor
|
||||
if (auto settingsRegistry = AZ::Interface<AZ::SettingsRegistryInterface>::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
AZStd::array settingsToCopy{
|
||||
AZStd::string::format("%s/sys_game_folder", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey),
|
||||
AZStd::string::format("%s/project_path", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey),
|
||||
AZStd::string{AZ::SettingsRegistryMergeUtils::FilePathKey_BinaryFolder},
|
||||
AZStd::string{AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder},
|
||||
AZStd::string{AZ::SettingsRegistryMergeUtils::FilePathKey_SourceGameFolder},
|
||||
AZStd::string{AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath},
|
||||
AZStd::string{AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder},
|
||||
AZStd::string{AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder},
|
||||
AZStd::string{AZ::SettingsRegistryMergeUtils::FilePathKey_CacheGameFolder}
|
||||
};
|
||||
|
||||
for (const auto& settingsKey : settingsToCopy)
|
||||
@@ -288,14 +290,14 @@ namespace AssetProcessor
|
||||
}
|
||||
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(registry);
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_EngineRegistry(registry, platform, specializations[i], &scratchBuffer);
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_GemRegistries(registry, platform, specializations[i], &scratchBuffer);
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectRegistry(registry, platform, specializations[i], &scratchBuffer);
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_EngineRegistry(registry, platform, specialization, &scratchBuffer);
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_GemRegistries(registry, platform, specialization, &scratchBuffer);
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectRegistry(registry, platform, specialization, &scratchBuffer);
|
||||
|
||||
// Merge the Developer User settings registry only in non-release builds
|
||||
if (!specializations->Contains("release"))
|
||||
if (!specialization.Contains("release"))
|
||||
{
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_DevRegistry(registry, platform, specializations[i], &scratchBuffer);
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_UserRegistry(registry, platform, specialization, &scratchBuffer);
|
||||
}
|
||||
|
||||
AZ::ComponentApplicationBus::Broadcast([®istry](AZ::ComponentApplicationRequests* appRequests)
|
||||
@@ -314,7 +316,7 @@ namespace AssetProcessor
|
||||
return;
|
||||
}
|
||||
|
||||
outputPath += specializations[i].GetSpecialization(0); // Append configuration
|
||||
outputPath += specialization.GetSpecialization(0); // Append configuration
|
||||
outputPath += '.';
|
||||
outputPath += platform;
|
||||
outputPath += ".setreg";
|
||||
|
||||
@@ -506,7 +506,7 @@ void ConnectionManager::AddAddressToAllowedList(QString address)
|
||||
UpdateAllowedListFromBootStrap();
|
||||
while (m_allowedListAddresses.removeOne(address)) {}
|
||||
m_allowedListAddresses << address;
|
||||
AssetUtilities::WriteAllowedlistToBootstrap(m_allowedListAddresses);
|
||||
AssetUtilities::WriteAllowedlistToSettingsRegistry(m_allowedListAddresses);
|
||||
Q_EMIT SyncAllowedListAndRejectedList(m_allowedListAddresses, m_rejectedAddresses);
|
||||
}
|
||||
|
||||
@@ -514,7 +514,7 @@ void ConnectionManager::RemoveAddressFromAllowedList(QString address)
|
||||
{
|
||||
UpdateAllowedListFromBootStrap();
|
||||
while (m_allowedListAddresses.removeOne(address)) {}
|
||||
AssetUtilities::WriteAllowedlistToBootstrap(m_allowedListAddresses);
|
||||
AssetUtilities::WriteAllowedlistToSettingsRegistry(m_allowedListAddresses);
|
||||
Q_EMIT SyncAllowedListAndRejectedList(m_allowedListAddresses, m_rejectedAddresses);
|
||||
}
|
||||
|
||||
|
||||
@@ -235,9 +235,9 @@ bool ConnectionWorker::NegotiateDirect(bool initiate)
|
||||
using namespace AzFramework::AssetSystem;
|
||||
|
||||
AZStd::string azBranchToken;
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForAppRoot, azBranchToken);
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForEngineRoot, azBranchToken);
|
||||
QString branchToken(azBranchToken.c_str());
|
||||
QString projectName = AssetUtilities::ComputeGameName();
|
||||
QString projectName = AssetUtilities::ComputeProjectName();
|
||||
|
||||
NegotiationMessage myInfo;
|
||||
|
||||
@@ -316,7 +316,7 @@ bool ConnectionWorker::NegotiateDirect(bool initiate)
|
||||
QString incomingBranchToken(engineInfo.m_negotiationInfoMap[NegotiationInfo_BranchIndentifier].c_str());
|
||||
if (QString::compare(incomingBranchToken, branchToken, Qt::CaseInsensitive) != 0)
|
||||
{
|
||||
//if we are here it means that the editor/game which is negotiating is running on a different branch
|
||||
// if we are here it means that the editor/game which is negotiating is running on a different branch
|
||||
// note that it could have just read nothing from the engine or a repeat packet, in that case, discard it silently and try again.
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "ConnectionWorker::NegotiateDirect: branch token mismatch from %s - %p - %s vs %s\n", engineInfo.m_identifier.c_str(), this, incomingBranchToken.toUtf8().data(), branchToken.toUtf8().data());
|
||||
AssetProcessor::MessageInfoBus::Broadcast(&AssetProcessor::MessageInfoBus::Events::NegotiationFailed);
|
||||
@@ -325,7 +325,7 @@ bool ConnectionWorker::NegotiateDirect(bool initiate)
|
||||
}
|
||||
|
||||
QString incomingProjectName(engineInfo.m_negotiationInfoMap[NegotiationInfo_ProjectName].c_str());
|
||||
// Do a case-insensitive compare for the project name because some (case-sensitive) platforms will blower-case the incoming project name
|
||||
// Do a case-insensitive compare for the project name because some (case-sensitive) platforms will lower-case the incoming project name
|
||||
if(QString::compare(incomingProjectName, projectName, Qt::CaseInsensitive) != 0)
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "ConnectionWorker::NegotiateDirect: project name mismatch from %s - %p - %s vs %s\n", engineInfo.m_identifier.c_str(), this, incomingProjectName.toUtf8().constData(), projectName.toUtf8().constData());
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzFramework/Application/Application.h>
|
||||
|
||||
#include <AzToolsFramework/Process/ProcessCommunicator.h>
|
||||
#include <AzToolsFramework/Process/ProcessWatcher.h>
|
||||
#include <AzFramework/Process/ProcessCommunicator.h>
|
||||
#include <AzFramework/Process/ProcessWatcher.h>
|
||||
#include <AzToolsFramework/Application/ToolsApplication.h>
|
||||
|
||||
#include <AssetBuilderSDK/AssetBuilderSDK.h>
|
||||
@@ -164,7 +164,7 @@ namespace AssetProcessor
|
||||
// build the command line:
|
||||
QString commandString = NativeLegacyRCCompiler::BuildCommand(inputFile, watchFolder, platformIdentifier, params, dest);
|
||||
|
||||
AzToolsFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
|
||||
// while it might be tempting to set the executable in processLaunchInfo.m_processExecutableString, it turns out that RC.EXE
|
||||
// won't work if you do that because it assumes the first command line param is the exe name, which is not the case if you do it that way...
|
||||
@@ -173,12 +173,12 @@ namespace AssetProcessor
|
||||
processLaunchInfo.m_commandlineParameters = QString(formatter).arg(m_rcExecutableFullPath).arg(commandString).toUtf8().data();
|
||||
processLaunchInfo.m_showWindow = false;
|
||||
processLaunchInfo.m_workingDirectory = m_systemRoot.absolutePath().toUtf8().data();
|
||||
processLaunchInfo.m_processPriority = AzToolsFramework::PROCESSPRIORITY_IDLE;
|
||||
processLaunchInfo.m_processPriority = AzFramework::ProcessPriority::PROCESSPRIORITY_IDLE;
|
||||
|
||||
AZ_TracePrintf("RC Builder", "Executing RC.EXE: '%s' ...\n", processLaunchInfo.m_commandlineParameters.c_str());
|
||||
AZ_TracePrintf("Rc Builder", "Executing RC.EXE with working directory: '%s' ...\n", processLaunchInfo.m_workingDirectory.c_str());
|
||||
|
||||
AzToolsFramework::ProcessWatcher* watcher = AzToolsFramework::ProcessWatcher::LaunchProcess(processLaunchInfo, AzToolsFramework::COMMUNICATOR_TYPE_STDINOUT);
|
||||
AzFramework::ProcessWatcher* watcher = AzFramework::ProcessWatcher::LaunchProcess(processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_STDINOUT);
|
||||
|
||||
if (!watcher)
|
||||
{
|
||||
@@ -256,18 +256,16 @@ namespace AssetProcessor
|
||||
QString cmdLine;
|
||||
if (!dest.isEmpty())
|
||||
{
|
||||
QString gameName = AssetUtilities::ComputeGameName();
|
||||
QString projectName = AssetUtilities::ComputeProjectName();
|
||||
QString projectPath = AssetUtilities::ComputeProjectPath();
|
||||
|
||||
int portNumber = 0;
|
||||
ApplicationServerBus::BroadcastResult(portNumber, &ApplicationServerBus::Events::GetServerListeningPort);
|
||||
|
||||
QDir assetRoot;
|
||||
AssetUtilities::ComputeAssetRoot(assetRoot);
|
||||
QString gameRoot = assetRoot.absoluteFilePath(AssetUtilities::ComputeGameName());
|
||||
AZStd::string appBranchToken;
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForAppRoot, appBranchToken);
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForEngineRoot, appBranchToken);
|
||||
cmdLine = QString("\"%1\" /p=%2 %3 /unattended=true /gameroot=\"%4\" /watchfolder=\"%6\" /targetroot=\"%5\" /logprefix=\"%5/\" /port=%7 /gamesubdirectory=\"%8\" /branchtoken=\"%9\"");
|
||||
cmdLine = cmdLine.arg(inputFile, platformIdentifier, params, gameRoot, dest, watchFolder).arg(portNumber).arg(gameName).arg(appBranchToken.c_str());
|
||||
cmdLine = cmdLine.arg(inputFile, platformIdentifier, params, projectPath, dest, watchFolder).arg(portNumber).arg(projectName).arg(appBranchToken.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/Component/ComponentApplication.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzToolsFramework/API/AssetDatabaseBus.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
@@ -44,7 +45,7 @@ namespace AssetProcessor
|
||||
public:
|
||||
AssetCatalogForUnitTest(QObject* parent, AssetProcessor::PlatformConfiguration* platformConfiguration)
|
||||
: AssetCatalog(parent, platformConfiguration) {}
|
||||
|
||||
|
||||
// prevent automatic save on shutdown, no point in doing that in unit test mode, just wastes time.
|
||||
virtual ~AssetCatalogForUnitTest()
|
||||
{
|
||||
@@ -68,7 +69,7 @@ namespace AssetProcessor
|
||||
: public ScopedAllocatorSetupFixture
|
||||
{
|
||||
protected:
|
||||
|
||||
|
||||
// store all data we create here so that it can be destroyed on shutdown before we remove allocators
|
||||
struct DataMembers
|
||||
{
|
||||
@@ -89,7 +90,7 @@ namespace AssetProcessor
|
||||
int argc = 0;
|
||||
DataMembers() : coreApp(argc, nullptr)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@@ -108,23 +109,28 @@ namespace AssetProcessor
|
||||
m_systemEntity = m_app->Create(desc);
|
||||
|
||||
m_data = azcreate(DataMembers, ());
|
||||
|
||||
|
||||
AssetUtilities::ComputeAssetRoot(m_data->m_priorAssetRoot);
|
||||
AssetUtilities::ResetAssetRoot();
|
||||
|
||||
// the canonicalization of the path here is to get around the fact that on some platforms
|
||||
// the "temporary" folder location could be junctioned into some other folder and getting "QDir::current()"
|
||||
// and other similar functions may actually return a different string but still be referring to the same folder
|
||||
// and other similar functions may actually return a different string but still be referring to the same folder
|
||||
m_data->m_temporarySourceDir = QDir(m_data->m_temporaryDir.path());
|
||||
QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(m_data->m_temporarySourceDir.canonicalPath());
|
||||
m_data->m_temporarySourceDir = QDir(canonicalTempDirPath);
|
||||
m_data->m_scopedDir.Setup(m_data->m_temporarySourceDir.path());
|
||||
m_data->m_gameName = AssetUtilities::ComputeGameName("SamplesProject"); // uses the above file.
|
||||
m_data->m_gameName = AssetUtilities::ComputeProjectName("SamplesProject"); // uses the above file.
|
||||
|
||||
AssetUtilities::ResetAssetRoot();
|
||||
QDir newRoot; // throwaway dummy var - we just want to invoke the below function
|
||||
AssetUtilities::ComputeAssetRoot(newRoot, &m_data->m_temporarySourceDir);
|
||||
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
auto cacheRootKey =
|
||||
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_cache_path";
|
||||
settingsRegistry->Set(cacheRootKey, m_data->m_temporarySourceDir.absoluteFilePath("Cache").toUtf8().constData());
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry);
|
||||
AssetUtilities::ComputeProjectCacheRoot(m_data->m_cacheRootDir);
|
||||
QString normalizedCacheRoot = AssetUtilities::NormalizeDirectoryPath(m_data->m_cacheRootDir.absolutePath());
|
||||
m_data->m_cacheRootDir = QDir(normalizedCacheRoot);
|
||||
@@ -181,7 +187,7 @@ namespace AssetProcessor
|
||||
|
||||
m_data->m_databaseLocationListener.BusConnect();
|
||||
m_data->m_dbConn.OpenDatabase();
|
||||
|
||||
|
||||
BuildConfig(m_data->m_temporarySourceDir, &(m_data->m_dbConn), m_data->m_config);
|
||||
m_data->m_assetCatalog.reset(new AssetCatalogForUnitTest(nullptr, &(m_data->m_config)));
|
||||
}
|
||||
@@ -331,7 +337,7 @@ namespace AssetProcessor
|
||||
{
|
||||
bool relPathfound = false;
|
||||
AZStd::string relPath;
|
||||
AZStd::string fullPath(fileToCheck.toStdString().c_str());
|
||||
AZStd::string fullPath(fileToCheck.toUtf8().constData());
|
||||
|
||||
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(relPathfound, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetRelativeProductPathFromFullSourceOrProductPath, fullPath, relPath);
|
||||
|
||||
@@ -356,7 +362,7 @@ namespace AssetProcessor
|
||||
{
|
||||
bool fullPathfound = false;
|
||||
AZStd::string fullPath;
|
||||
AZStd::string relPath(fileToCheck.toStdString().c_str());
|
||||
AZStd::string relPath(fileToCheck.toUtf8().constData());
|
||||
|
||||
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(fullPathfound, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetFullSourcePathFromRelativeProductPath, relPath, fullPath);
|
||||
|
||||
@@ -419,7 +425,7 @@ namespace AssetProcessor
|
||||
ASSERT_EQ(m_data->m_absorber.m_numAssertsAbsorbed, 2);
|
||||
// reset the absorber before we leave this assert-test, so that it doesn't cause failure of the test itself
|
||||
m_data->m_absorber.Clear();
|
||||
|
||||
|
||||
ASSERT_TRUE(TestGetRelativeProductPath("", false, { "" }));
|
||||
ASSERT_TRUE(TestGetFullSourcePath("", m_data->m_temporarySourceDir, false, ""));
|
||||
}
|
||||
@@ -444,75 +450,76 @@ namespace AssetProcessor
|
||||
|
||||
TEST_F(AssetCatalogTestWithProducts, GetRelativeProductPathFromFullSourceOrProductPath_WithGameName_ReturnsFileInGameFolder)
|
||||
{
|
||||
// feed it a product path with gamename and a platform name, returns it without gamename
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath("pc/" + m_data->m_gameName + "/aaa/basefile.txt");
|
||||
// feed it a product path with a platform name, returns it
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath(QString("pc") + "/aaa/basefile.txt");
|
||||
ASSERT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "aaa/basefile.txt" }));
|
||||
}
|
||||
|
||||
TEST_F(AssetCatalogTestWithProducts, GetRelativeProductPathFromFullSourceOrProductPath_WithoutGameName_ReturnsFileInRootFolder)
|
||||
{
|
||||
// feed it a product path without gamename, just the file name since its supposed to be a root file
|
||||
// feed it a product path, just the file name since its supposed to be a root file
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath("pc/basefile.txt");
|
||||
ASSERT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "basefile.txt" }));
|
||||
}
|
||||
|
||||
TEST_F(AssetCatalogTestWithProducts, GetRelativeProductPathFromFullSourceOrProductPath_BadCasingInPlatform_ReturnsRelativePath)
|
||||
{
|
||||
// feed it a product path with gamename but poor casing (test 1: the pc platform is not matching case)
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath("Pc/" + m_data->m_gameName + "/aaa/basefile.txt");
|
||||
// feed it a product path but with poor casing (test 1: the pc platform is not matching case)
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath(QString("Pc") + "/aaa/basefile.txt");
|
||||
ASSERT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "aaa/basefile.txt" }));
|
||||
}
|
||||
|
||||
TEST_F(AssetCatalogTestWithProducts, GetRelativeProductPathFromFullSourceOrProductPath_BadCasingInGameName_ReturnsRelativePath)
|
||||
{
|
||||
//feed it a product path with gamename but poor casing (test 2: the gameName is not matching case)
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath("pc/" + m_data->m_gameName.toUpper() + "/aaa/basefile.txt");
|
||||
//feed it a product path but with poor casing (test 2: the gameName is not matching case)
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath(QString("pc") + "/aaa/basefile.txt");
|
||||
ASSERT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "aaa/basefile.txt" }));
|
||||
}
|
||||
|
||||
TEST_F(AssetCatalogTestWithProducts, GetRelativeProductPathFromFullSourceOrProductPath_FolderName_ReturnsFolderNameOnly)
|
||||
{
|
||||
// feed it a product path that resolves to a directory name instead of a file. GameName is 'incorrect' (upper)
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath("pc/" + m_data->m_gameName.toUpper() + "/aaa");
|
||||
// feed it a product path that resolves to a directory name instead of a file.
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath(QString("pc") + "/aaa");
|
||||
ASSERT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "aaa" }));
|
||||
}
|
||||
|
||||
TEST_F(AssetCatalogTestWithProducts, GetRelativeProductPathFromFullSourceOrProductPath_FolderNameExtraSlash_ReturnsFolderNameOnlyNoExtraSlash)
|
||||
{
|
||||
// make sure it doesn't keep any trailing slashes. GameName is 'incorrect' (upper)
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath("pc/" + m_data->m_gameName.toUpper() + "/aaa/"); // extra trailing slash
|
||||
// make sure it doesn't keep any trailing slashes.
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath(QString("pc") + "/aaa/"); // extra trailing slash
|
||||
ASSERT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "aaa" })); // the API should never result in a trailing slash
|
||||
}
|
||||
|
||||
TEST_F(AssetCatalogTestWithProducts, GetRelativeProductPathFromFullSourceOrProductPath_FolderNameExtraWrongWaySlash_ReturnsFolderNameOnlyNoExtraWrongSlash)
|
||||
{
|
||||
// make sure it doesn't keep any trailing slashes. GameName is 'incorrect' (upper)
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath("pc/" + m_data->m_gameName.toUpper() + "/aaa\\"); // extra trailing wrongway slash
|
||||
// make sure it doesn't keep any trailing slashes.
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath(QString("pc") + "/aaa\\"); // extra trailing wrongway slash
|
||||
ASSERT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "aaa" })); // the API should never result in a trailing slash
|
||||
}
|
||||
|
||||
TEST_F(AssetCatalogTestWithProducts, GetRelativeProductPathFromFullSourceOrProductPath_RelativeDirectoryNameWhichDoesNotExist_ReturnsFolderNameOnly)
|
||||
{
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath("pc/" + m_data->m_gameName.toLower() + "/nonexistantfolder"); // extra trailing wrongway slash
|
||||
ASSERT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "nonexistantfolder" }));
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath(QString("pc") + "/nonexistantfolder"); // extra trailing wrongway slash
|
||||
ASSERT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "nonexistantfolder" }));
|
||||
}
|
||||
|
||||
TEST_F(AssetCatalogTestWithProducts, GetRelativeProductPathFromFullSourceOrProductPath_RelativeDirectoryNameWhichDoesNotExistWithExtraSlash_ReturnsFolderNameOnly)
|
||||
{
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath("pc/" + m_data->m_gameName.toLower() + "/nonexistantfolder/"); // extra trailing wrongway slash
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath(QString("pc") + "/nonexistantfolder/"); // extra trailing wrongway slash
|
||||
ASSERT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "nonexistantfolder" }));
|
||||
}
|
||||
|
||||
TEST_F(AssetCatalogTestWithProducts, GetRelativeProductPathFromFullSourceOrProductPath_RelativeDirectoryNameWhichDoesNotExistWithExtraWrongWaySlash_ReturnsFolderNameOnly)
|
||||
{
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath("pc\\" + m_data->m_gameName.toLower() + "\\nonexistantfolder\\"); // extra trailing wrongway slash
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath(QString("pc") + "\\nonexistantfolder\\"); // extra trailing wrongway slash
|
||||
ASSERT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "nonexistantfolder" }));
|
||||
}
|
||||
|
||||
TEST_F(AssetCatalogTestWithProducts, GetRelativeProductPathFromFullSourceOrProductPath_RelativePathToSourceFile_ReturnsProductFilePath)
|
||||
{
|
||||
QString fileToCheck = m_data->m_temporarySourceDir.absoluteFilePath("subfolder3/BaseFile.txt");
|
||||
ASSERT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "basefilez.arc2", "basefileaz.azm2", "basefile.arc2", "basefile.azm2" }));
|
||||
ASSERT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "basefilez.arc2", "basefileaz.azm2",
|
||||
"basefile.arc2", "basefile.azm2" }));
|
||||
}
|
||||
|
||||
TEST_F(AssetCatalogTestWithProducts, GetRelativeProductPathFromFullSourceOrProductPath_RelativePathToSourceFile_BadCasing_ReturnsProductFilePath)
|
||||
@@ -533,9 +540,9 @@ namespace AssetProcessor
|
||||
// ----- Test the ProcessGetFullAssetPath function on product files
|
||||
{
|
||||
QStringList pcouts;
|
||||
pcouts.push_back(m_data->m_cacheRootDir.filePath(QString("pc/") + m_data->m_gameName + "/subfolder3/randomfileoutput.random"));
|
||||
pcouts.push_back(m_data->m_cacheRootDir.filePath(QString("pc/") + m_data->m_gameName + "/subfolder3/randomfileoutput.random1"));
|
||||
pcouts.push_back(m_data->m_cacheRootDir.filePath(QString("pc/") + m_data->m_gameName + "/subfolder3/randomfileoutput.random2"));
|
||||
pcouts.push_back(m_data->m_cacheRootDir.filePath(QString("pc") + "/subfolder3/randomfileoutput.random"));
|
||||
pcouts.push_back(m_data->m_cacheRootDir.filePath(QString("pc") + "/subfolder3/randomfileoutput.random1"));
|
||||
pcouts.push_back(m_data->m_cacheRootDir.filePath(QString("pc") + "/subfolder3/randomfileoutput.random2"));
|
||||
|
||||
AZ::s64 jobId;
|
||||
ASSERT_TRUE(AddSourceAndJob("subfolder3", "somerandomfile.random", &(m_data->m_dbConn), jobId));
|
||||
@@ -591,32 +598,32 @@ namespace AssetProcessor
|
||||
QString fileToCheck = "@somerandomalias@/subfolder3/randomfileoutput.random1";
|
||||
EXPECT_TRUE(TestGetFullSourcePath(fileToCheck, m_data->m_temporarySourceDir, true, "subfolder3/somerandomfile.random"));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(AssetCatalogTest_GetFullSourcePath, InvalidAliasMissingSeperator_ReturnsAbsolutePathToSource)
|
||||
{
|
||||
//feed it a path with some random alias and asset id but no separator
|
||||
QString fileToCheck = "@somerandomalias@subfolder3/randomfileoutput.random1";
|
||||
EXPECT_TRUE(TestGetFullSourcePath(fileToCheck, m_data->m_temporarySourceDir, true, "subfolder3/somerandomfile.random"));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(AssetCatalogTest_GetFullSourcePath, InvalidSourcePathContainingCacheAlias_ReturnsAbsolutePathToSource)
|
||||
{
|
||||
//feed it a path with alias and input name
|
||||
QString fileToCheck = "@assets@/somerandomfile.random";
|
||||
EXPECT_TRUE(TestGetFullSourcePath(fileToCheck, m_data->m_temporarySourceDir, true, "subfolder3/somerandomfile.random"));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(AssetCatalogTest_GetFullSourcePath, AbsolutePathToCache_ReturnsAbsolutePathToSource)
|
||||
{
|
||||
//feed it an absolute path with cacheroot
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath("pc/" + m_data->m_gameName.toLower() + "/subfolder3/randomfileoutput.random1");
|
||||
QString fileToCheck = m_data->m_cacheRootDir.filePath(QString("pc") + "/subfolder3/randomfileoutput.random1");
|
||||
EXPECT_TRUE(TestGetFullSourcePath(fileToCheck, m_data->m_temporarySourceDir, true, "subfolder3/somerandomfile.random"));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(AssetCatalogTest_GetFullSourcePath, ProductNameIncludingPlatformAndGameName_ReturnsAbsolutePathToSource)
|
||||
{
|
||||
//feed it a productName directly
|
||||
QString fileToCheck = "pc/" + m_data->m_gameName + "/subfolder3/randomfileoutput.random1";
|
||||
QString fileToCheck = QString("pc") + "/subfolder3/randomfileoutput.random1";
|
||||
EXPECT_TRUE(TestGetFullSourcePath(fileToCheck, m_data->m_temporarySourceDir, true, "subfolder3/somerandomfile.random"));
|
||||
}
|
||||
|
||||
@@ -624,7 +631,7 @@ namespace AssetProcessor
|
||||
: public AssetCatalogTest
|
||||
{
|
||||
public:
|
||||
|
||||
|
||||
struct AssetCatalogTest_AssetInfo_DataMembers
|
||||
{
|
||||
AssetId m_assetA = AssetId(Uuid::CreateRandom(), 0);
|
||||
@@ -650,7 +657,7 @@ namespace AssetProcessor
|
||||
|
||||
AzFramework::StringFunc::Path::Join(m_customDataMembers->m_subfolder1AbsolutePath.c_str(), m_customDataMembers->m_assetASourceRelPath.c_str(), m_customDataMembers->m_assetAFullPath);
|
||||
CreateDummyFile(QString::fromUtf8(m_customDataMembers->m_assetAFullPath.c_str()), m_customDataMembers->m_assetTestString.c_str());
|
||||
|
||||
|
||||
AzFramework::StringFunc::Path::Join(m_data->m_cacheRootDir.absolutePath().toUtf8().constData(), m_customDataMembers->m_assetAProductRelPath.c_str(), m_customDataMembers->m_assetAProductFullPath);
|
||||
CreateDummyFile(QString::fromUtf8(m_customDataMembers->m_assetAProductFullPath.c_str()), m_customDataMembers->m_productTestString.c_str());
|
||||
}
|
||||
@@ -724,7 +731,7 @@ namespace AssetProcessor
|
||||
return true;
|
||||
};
|
||||
|
||||
void TearDown() override
|
||||
void TearDown() override
|
||||
{
|
||||
azdestroy(m_customDataMembers);
|
||||
AssetCatalogTest::TearDown();
|
||||
@@ -848,7 +855,7 @@ namespace AssetProcessor
|
||||
using namespace AzFramework::AssetSystem;
|
||||
|
||||
PlatformConfiguration config;
|
||||
|
||||
|
||||
config.EnablePlatform(AssetBuilderSDK::PlatformInfo("pc", { "test" }));
|
||||
|
||||
{
|
||||
@@ -1060,7 +1067,7 @@ namespace AssetProcessor
|
||||
productDependency.m_dependencySourceGuid = m_sourceFileWithDifferentProductsPerPlatform;
|
||||
productDependency.m_unresolvedPath = AZStd::string();
|
||||
|
||||
QString platformGameDir = QDir(cacheRoot.absoluteFilePath(productDependency.m_platform.c_str())).filePath(AssetUtilities::ComputeGameName().toLower());
|
||||
QString platformGameDir = QDir(cacheRoot.absoluteFilePath(productDependency.m_platform.c_str())).filePath(AssetUtilities::ComputeProjectName().toLower());
|
||||
QString assetCatalogFile = QDir(platformGameDir).filePath("assetcatalog.xml");
|
||||
QFileInfo fileInfo(assetCatalogFile);
|
||||
|
||||
@@ -1071,7 +1078,7 @@ namespace AssetProcessor
|
||||
// process all events
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents);
|
||||
|
||||
// This ensures that no save catalog event was queued when we resolve dependency
|
||||
// This ensures that no save catalog event was queued when we resolve dependency
|
||||
EXPECT_FALSE(fileInfo.exists());
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace AssetProcessorMessagesTests
|
||||
m_batchApplicationManager->BeforeRun();
|
||||
|
||||
// Override Game Name to be "SamplesProject"
|
||||
AssetUtilities::ComputeGameName("SamplesProject", true);
|
||||
AssetUtilities::ComputeProjectName("SamplesProject", true);
|
||||
|
||||
m_batchApplicationManager->m_platformConfiguration = new PlatformConfiguration();
|
||||
m_batchApplicationManager->InitAssetProcessorManager();
|
||||
@@ -144,7 +144,7 @@ namespace AssetProcessorMessagesTests
|
||||
RunNetworkRequest([]()
|
||||
{
|
||||
AZStd::string appBranchToken;
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForAppRoot, appBranchToken);
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForEngineRoot, appBranchToken);
|
||||
|
||||
AzFramework::AssetSystem::ConnectionSettings connectionSettings;
|
||||
connectionSettings.m_assetProcessorIp = "127.0.0.1";
|
||||
|
||||
@@ -181,9 +181,11 @@ namespace UnitTests
|
||||
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("dev/testfolder/file.foo")));
|
||||
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("dev/testfolder/File.bar")));
|
||||
|
||||
|
||||
AZ::IO::FileIOBase::SetInstance(nullptr); // The API requires the old instance to be destroyed first
|
||||
AZ::IO::FileIOBase::SetInstance(new AZ::IO::LocalFileIO());
|
||||
if (AZ::IO::FileIOBase::GetInstance() == nullptr)
|
||||
{
|
||||
m_localFileIo = AZStd::make_unique<AZ::IO::LocalFileIO>();
|
||||
AZ::IO::FileIOBase::SetInstance(m_localFileIo.get());
|
||||
}
|
||||
|
||||
m_data->m_reporter = AZStd::make_unique<SourceFileRelocator>(m_data->m_connection, &m_data->m_platformConfig);
|
||||
|
||||
@@ -204,6 +206,12 @@ namespace UnitTests
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
if (AZ::IO::FileIOBase::GetInstance() == m_localFileIo.get())
|
||||
{
|
||||
AZ::IO::FileIOBase::SetInstance(nullptr);
|
||||
}
|
||||
m_localFileIo.reset();
|
||||
|
||||
AZ::JobContext::SetGlobalContext(nullptr);
|
||||
delete m_data->m_jobContext;
|
||||
delete m_data->m_jobManager;
|
||||
@@ -405,6 +413,7 @@ namespace UnitTests
|
||||
// we store the above data in a unique_ptr so that its memory can be cleared during TearDown() in one call, before we destroy the memory
|
||||
// allocator, reducing the chance of missing or forgetting to destroy one in the future.
|
||||
AZStd::unique_ptr<StaticData> m_data;
|
||||
AZStd::unique_ptr<AZ::IO::LocalFileIO> m_localFileIo;
|
||||
};
|
||||
|
||||
TEST_F(SourceFileRelocatorTest, GetSources_SingleFile_Succeeds)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "AssetProcessorManagerTest.h"
|
||||
#include "native/AssetManager/PathDependencyManager.h"
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzToolsFramework/ToolsFileUtils/ToolsFileUtils.h>
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
@@ -41,7 +42,7 @@ public:
|
||||
friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, QueryAbsolutePathDependenciesRecursive_WithDifferentTypes_BasicTest);
|
||||
friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, QueryAbsolutePathDependenciesRecursive_Reverse_BasicTest);
|
||||
friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, QueryAbsolutePathDependenciesRecursive_MissingFiles_ReturnsNoPathWithPlaceholders);
|
||||
|
||||
|
||||
friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, BuilderDirtiness_BeforeComputingDirtiness_AllDirty);
|
||||
friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, BuilderDirtiness_EmptyDatabase_AllDirty);
|
||||
friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, BuilderDirtiness_SameAsLastTime_NoneDirty);
|
||||
@@ -206,6 +207,12 @@ void AssetProcessorManagerTest::SetUp()
|
||||
m_scopeDir->Setup(m_tempDir.path());
|
||||
QDir tempPath(m_tempDir.path());
|
||||
|
||||
auto registry = AZ::SettingsRegistry::Get();
|
||||
auto cacheRootKey =
|
||||
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_cache_path";
|
||||
registry->Set(cacheRootKey, tempPath.absoluteFilePath("Cache").toUtf8().constData());
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
|
||||
|
||||
m_data->m_databaseLocationListener.BusConnect();
|
||||
|
||||
// in other unit tests we may open the database called ":memory:" to use an in-memory database instead of one on disk.
|
||||
@@ -220,7 +227,7 @@ void AssetProcessorManagerTest::SetUp()
|
||||
SetArgReferee<0>(m_data->m_databaseLocation),
|
||||
Return(true)));
|
||||
|
||||
m_gameName = AssetUtilities::ComputeGameName("SamplesProject", true);
|
||||
m_gameName = AssetUtilities::ComputeProjectName("SamplesProject", true);
|
||||
|
||||
AssetUtilities::ResetAssetRoot();
|
||||
QDir newRoot;
|
||||
@@ -230,7 +237,6 @@ void AssetProcessorManagerTest::SetUp()
|
||||
AssetUtilities::ComputeProjectCacheRoot(cacheRoot);
|
||||
QString normalizedCacheRoot = AssetUtilities::NormalizeDirectoryPath(cacheRoot.absolutePath());
|
||||
|
||||
QString normalizedDirPathCheck = AssetUtilities::NormalizeDirectoryPath(QDir::current().absoluteFilePath("Cache/" + m_gameName));
|
||||
m_normalizedCacheRootDir.setPath(normalizedCacheRoot);
|
||||
|
||||
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder1/assetProcessorManagerTest.txt"));
|
||||
@@ -357,7 +363,7 @@ TEST_F(AssetProcessorManagerTest, UnitTestForGettingJobInfoBySourceUUIDSuccess)
|
||||
TEST_F(AssetProcessorManagerTest, WarningsAndErrorsReported_SuccessfullySavedToDatabase)
|
||||
{
|
||||
// This tests the JobDiagnosticTracker: Warnings/errors reported to it should be recorded in the database when AssetProcessed is fired and able to be retrieved when querying job status
|
||||
|
||||
|
||||
using namespace AzToolsFramework::AssetSystem;
|
||||
using namespace AssetProcessor;
|
||||
|
||||
@@ -471,7 +477,7 @@ TEST_F(AssetProcessorManagerTest, UnitTestForOutPutPrefix)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
m_isIdling = false;
|
||||
// tell the APM about the files:
|
||||
QMetaObject::invokeMethod(m_assetProcessorManager.get(), "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, tempPath.absoluteFilePath("subfolder2/test.txt")));
|
||||
@@ -626,10 +632,10 @@ TEST_F(AssetProcessorManagerTest, BuilderDirtiness_EmptyDatabase_AllDirty)
|
||||
|
||||
MockBuilderResponder mockBuilderResponder;
|
||||
mockBuilderResponder.BusConnect();
|
||||
|
||||
|
||||
mockBuilderResponder.AddBuilder("builder1", { AssetBuilderSDK::AssetBuilderPattern("*.egg", AssetBuilderPattern::Wildcard) }, AZ::Uuid::CreateRandom(), 1, "fingerprint1");
|
||||
mockBuilderResponder.AddBuilder("builder2", { AssetBuilderSDK::AssetBuilderPattern("*.foo", AssetBuilderPattern::Wildcard) }, AZ::Uuid::CreateRandom(), 1, "fingerprint2");
|
||||
|
||||
|
||||
m_assetProcessorManager->ComputeBuilderDirty();
|
||||
|
||||
EXPECT_TRUE(m_assetProcessorManager->m_anyBuilderChange);
|
||||
@@ -666,7 +672,7 @@ TEST_F(AssetProcessorManagerTest, BuilderDirtiness_SameAsLastTime_NoneDirty)
|
||||
mockBuilderResponder.BusDisconnect();
|
||||
}
|
||||
|
||||
// when a new builder appears, the new builder should be dirty,
|
||||
// when a new builder appears, the new builder should be dirty,
|
||||
TEST_F(AssetProcessorManagerTest, BuilderDirtiness_MoreThanLastTime_NewOneIsDirty)
|
||||
{
|
||||
using namespace AzToolsFramework::AssetSystem;
|
||||
@@ -677,13 +683,13 @@ TEST_F(AssetProcessorManagerTest, BuilderDirtiness_MoreThanLastTime_NewOneIsDirt
|
||||
mockBuilderResponder.BusConnect();
|
||||
|
||||
mockBuilderResponder.AddBuilder("builder1", { AssetBuilderSDK::AssetBuilderPattern("*.egg", AssetBuilderPattern::Wildcard) }, AZ::Uuid::CreateRandom(), 1, "fingerprint1");
|
||||
|
||||
|
||||
m_assetProcessorManager->ComputeBuilderDirty();
|
||||
|
||||
mockBuilderResponder.AddBuilder("builder2", { AssetBuilderSDK::AssetBuilderPattern("*.foo", AssetBuilderPattern::Wildcard) }, AZ::Uuid::CreateRandom(), 1, "fingerprint2");
|
||||
|
||||
m_assetProcessorManager->ComputeBuilderDirty();
|
||||
|
||||
|
||||
// one new builder should have been dirty:
|
||||
EXPECT_TRUE(m_assetProcessorManager->m_anyBuilderChange);
|
||||
EXPECT_TRUE(m_assetProcessorManager->m_buildersAddedOrRemoved);
|
||||
@@ -892,7 +898,7 @@ TEST_F(AssetProcessorManagerTest, BuilderDirtiness_NewAnalysisFingerprint_IsNotA
|
||||
TEST_F(AssetProcessorManagerTest, QueryAbsolutePathDependenciesRecursive_BasicTest)
|
||||
{
|
||||
using SourceFileDependencyEntry = AzToolsFramework::AssetDatabase::SourceFileDependencyEntry;
|
||||
|
||||
|
||||
// A depends on B, which depends on both C and D
|
||||
|
||||
QDir tempPath(m_tempDir.path());
|
||||
@@ -941,7 +947,7 @@ TEST_F(AssetProcessorManagerTest, QueryAbsolutePathDependenciesRecursive_BasicTe
|
||||
|
||||
dependencies.clear();
|
||||
m_assetProcessorManager->QueryAbsolutePathDependenciesRecursive("b.txt", dependencies, SourceFileDependencyEntry::DEP_SourceToSource, false);
|
||||
EXPECT_EQ(dependencies.size(), 3); // b depends on c, and d
|
||||
EXPECT_EQ(dependencies.size(), 3); // b depends on c, and d
|
||||
EXPECT_NE(dependencies.find(tempPath.absoluteFilePath("subfolder1/b.txt").toUtf8().constData()), dependencies.end());
|
||||
EXPECT_NE(dependencies.find(tempPath.absoluteFilePath("subfolder1/c.txt").toUtf8().constData()), dependencies.end());
|
||||
EXPECT_NE(dependencies.find(tempPath.absoluteFilePath("subfolder1/d.txt").toUtf8().constData()), dependencies.end());
|
||||
@@ -961,7 +967,7 @@ TEST_F(AssetProcessorManagerTest, QueryAbsolutePathDependenciesRecursive_WithDif
|
||||
{
|
||||
// test to make sure that different TYPES of dependencies work as expected.
|
||||
using SourceFileDependencyEntry = AzToolsFramework::AssetDatabase::SourceFileDependencyEntry;
|
||||
|
||||
|
||||
QDir tempPath(m_tempDir.path());
|
||||
|
||||
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder1/a.txt"), QString("tempdata\n"));
|
||||
@@ -998,7 +1004,7 @@ TEST_F(AssetProcessorManagerTest, QueryAbsolutePathDependenciesRecursive_WithDif
|
||||
m_assetProcessorManager->QueryAbsolutePathDependenciesRecursive("a.txt", dependencies, SourceFileDependencyEntry::DEP_SourceToSource, false);
|
||||
// note that a depends on b, c, and d - with the latter two being indirect.
|
||||
// however, since b's dependency on C is via JOB, and we're asking for SOURCE only, we should not see C.
|
||||
EXPECT_EQ(dependencies.size(), 3);
|
||||
EXPECT_EQ(dependencies.size(), 3);
|
||||
|
||||
EXPECT_NE(dependencies.find(tempPath.absoluteFilePath("subfolder1/a.txt").toUtf8().constData()), dependencies.end());
|
||||
EXPECT_NE(dependencies.find(tempPath.absoluteFilePath("subfolder1/b.txt").toUtf8().constData()), dependencies.end());
|
||||
@@ -1007,7 +1013,7 @@ TEST_F(AssetProcessorManagerTest, QueryAbsolutePathDependenciesRecursive_WithDif
|
||||
dependencies.clear();
|
||||
m_assetProcessorManager->QueryAbsolutePathDependenciesRecursive("b.txt", dependencies, SourceFileDependencyEntry::DEP_JobToJob, false);
|
||||
// b depends on c, and d - but we're asking for job dependencies only, so we should not get anything except C and B
|
||||
EXPECT_EQ(dependencies.size(), 2);
|
||||
EXPECT_EQ(dependencies.size(), 2);
|
||||
EXPECT_NE(dependencies.find(tempPath.absoluteFilePath("subfolder1/b.txt").toUtf8().constData()), dependencies.end());
|
||||
EXPECT_NE(dependencies.find(tempPath.absoluteFilePath("subfolder1/c.txt").toUtf8().constData()), dependencies.end());
|
||||
|
||||
@@ -1063,13 +1069,13 @@ TEST_F(AssetProcessorManagerTest, QueryAbsolutePathDependenciesRecursive_Reverse
|
||||
AssetProcessor::SourceFilesForFingerprintingContainer dependencies;
|
||||
// sanity: what Depends on a? the only result should be a itself.
|
||||
m_assetProcessorManager->QueryAbsolutePathDependenciesRecursive("a.txt", dependencies, SourceFileDependencyEntry::DEP_SourceToSource, true /*reverse*/);
|
||||
EXPECT_EQ(dependencies.size(), 1);
|
||||
EXPECT_EQ(dependencies.size(), 1);
|
||||
EXPECT_NE(dependencies.find(tempPath.absoluteFilePath("subfolder1/a.txt").toUtf8().constData()), dependencies.end());
|
||||
|
||||
dependencies.clear();
|
||||
// what depends on d? b and a should (indirectly)
|
||||
m_assetProcessorManager->QueryAbsolutePathDependenciesRecursive("d.txt", dependencies, SourceFileDependencyEntry::DEP_SourceToSource, true);
|
||||
EXPECT_EQ(dependencies.size(), 3);
|
||||
EXPECT_EQ(dependencies.size(), 3);
|
||||
EXPECT_NE(dependencies.find(tempPath.absoluteFilePath("subfolder1/a.txt").toUtf8().constData()), dependencies.end());
|
||||
EXPECT_NE(dependencies.find(tempPath.absoluteFilePath("subfolder1/b.txt").toUtf8().constData()), dependencies.end());
|
||||
EXPECT_NE(dependencies.find(tempPath.absoluteFilePath("subfolder1/d.txt").toUtf8().constData()), dependencies.end());
|
||||
@@ -1077,7 +1083,7 @@ TEST_F(AssetProcessorManagerTest, QueryAbsolutePathDependenciesRecursive_Reverse
|
||||
// what depends on c? b and a should.
|
||||
dependencies.clear();
|
||||
m_assetProcessorManager->QueryAbsolutePathDependenciesRecursive("c.txt", dependencies, SourceFileDependencyEntry::DEP_SourceToSource, true);
|
||||
EXPECT_EQ(dependencies.size(), 3); // b depends on c, and d
|
||||
EXPECT_EQ(dependencies.size(), 3); // b depends on c, and d
|
||||
EXPECT_NE(dependencies.find(tempPath.absoluteFilePath("subfolder1/c.txt").toUtf8().constData()), dependencies.end());
|
||||
EXPECT_NE(dependencies.find(tempPath.absoluteFilePath("subfolder1/b.txt").toUtf8().constData()), dependencies.end());
|
||||
EXPECT_NE(dependencies.find(tempPath.absoluteFilePath("subfolder1/a.txt").toUtf8().constData()), dependencies.end());
|
||||
@@ -1092,7 +1098,7 @@ TEST_F(AssetProcessorManagerTest, QueryAbsolutePathDependenciesRecursive_Reverse
|
||||
EXPECT_NE(dependencies.find(tempPath.absoluteFilePath("subfolder1/c.txt").toUtf8().constData()), dependencies.end());
|
||||
}
|
||||
|
||||
// since we need these files to still produce a 0-based fingerprint, we need them to
|
||||
// since we need these files to still produce a 0-based fingerprint, we need them to
|
||||
// still do a best guess at absolute path, when they are missing.
|
||||
TEST_F(AssetProcessorManagerTest, QueryAbsolutePathDependenciesRecursive_MissingFiles_ReturnsNoPathWithPlaceholders)
|
||||
{
|
||||
@@ -1131,13 +1137,13 @@ TEST_F(AssetProcessorManagerTest, QueryAbsolutePathDependenciesRecursive_Missing
|
||||
AssetProcessor::SourceFilesForFingerprintingContainer dependencies;
|
||||
m_assetProcessorManager->QueryAbsolutePathDependenciesRecursive("a.txt", dependencies, SourceFileDependencyEntry::DEP_SourceToSource, false);
|
||||
EXPECT_EQ(dependencies.size(), 2); // a depends on b, c, and d - with the latter two being indirect.
|
||||
|
||||
|
||||
EXPECT_NE(dependencies.find(tempPath.absoluteFilePath("subfolder1/a.txt").toUtf8().constData()), dependencies.end());
|
||||
EXPECT_NE(dependencies.find(tempPath.absoluteFilePath("subfolder1/d.txt").toUtf8().constData()), dependencies.end());
|
||||
|
||||
dependencies.clear();
|
||||
m_assetProcessorManager->QueryAbsolutePathDependenciesRecursive("b.txt", dependencies, SourceFileDependencyEntry::DEP_SourceToSource, false);
|
||||
EXPECT_EQ(dependencies.size(), 1); // b depends on c, and d
|
||||
EXPECT_EQ(dependencies.size(), 1); // b depends on c, and d
|
||||
EXPECT_NE(dependencies.find(tempPath.absoluteFilePath("subfolder1/d.txt").toUtf8().constData()), dependencies.end());
|
||||
|
||||
// eliminate b --> c
|
||||
@@ -1161,7 +1167,7 @@ TEST_F(AssetProcessorManagerTest, BuilderSDK_API_CreateJobs_HasValidParameters_W
|
||||
|
||||
m_isIdling = false;
|
||||
QMetaObject::invokeMethod(m_assetProcessorManager.get(), "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, absPath));
|
||||
|
||||
|
||||
// wait for AP to become idle.
|
||||
ASSERT_TRUE(BlockUntilIdle(5000));
|
||||
|
||||
@@ -1189,10 +1195,10 @@ TEST_F(AssetProcessorManagerTest, BuilderSDK_API_CreateJobs_HasValidParameters_W
|
||||
UnitTestUtils::CreateDummyFile(absPath);
|
||||
|
||||
m_mockApplicationManager->ResetMockBuilderCreateJobCalls();
|
||||
|
||||
|
||||
m_isIdling = false;
|
||||
QMetaObject::invokeMethod(m_assetProcessorManager.get(), "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, absPath));
|
||||
|
||||
|
||||
ASSERT_TRUE(BlockUntilIdle(5000));
|
||||
|
||||
ASSERT_EQ(m_mockApplicationManager->GetMockBuilderCreateJobCalls(), 1);
|
||||
@@ -1461,14 +1467,14 @@ bool PathDependencyTest::ProcessAsset(TestAsset& asset, const OutputAssetSet& ou
|
||||
{
|
||||
ProcessJobResponse processJobResponse;
|
||||
processJobResponse.m_resultCode = ProcessJobResult_Success;
|
||||
|
||||
|
||||
for (const char* outputExtension : outputSet)
|
||||
{
|
||||
if(jobSet >= capturedDetails.size() || capturedDetails[jobSet].m_destinationPath.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
QString outputAssetPath = QDir(capturedDetails[jobSet].m_destinationPath).absoluteFilePath(QString(asset.m_name.c_str()) + outputExtension);
|
||||
|
||||
UnitTestUtils::CreateDummyFile(outputAssetPath, "this is a test output asset");
|
||||
@@ -1508,7 +1514,7 @@ bool SearchDependencies(AzToolsFramework::AssetDatabase::ProductDependencyDataba
|
||||
void VerifyDependencies(AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntryContainer dependencyContainer, AZStd::initializer_list<AZ::Data::AssetId> assetIds, AZStd::initializer_list<const char*> unresolvedPaths = {})
|
||||
{
|
||||
EXPECT_EQ(dependencyContainer.size(), assetIds.size() + unresolvedPaths.size());
|
||||
|
||||
|
||||
for (const AZ::Data::AssetId& assetId : assetIds)
|
||||
{
|
||||
bool found = false;
|
||||
@@ -1635,19 +1641,19 @@ TEST_F(PathDependencyTest, NoLongerProcessedFile_IsRemoved)
|
||||
{
|
||||
details = message;
|
||||
});
|
||||
|
||||
|
||||
QDir tempPath(m_tempDir.path());
|
||||
QString absPath(tempPath.absoluteFilePath("subfolder1/test1.txt"));
|
||||
|
||||
TestAsset testAsset("test1");
|
||||
|
||||
|
||||
ASSERT_TRUE(ProcessAsset(testAsset, { {".asset1"} }));
|
||||
|
||||
AzToolsFramework::AssetDatabase::ProductDatabaseEntryContainer products;
|
||||
m_sharedConnection->GetProductsBySourceName("test1.txt", products);
|
||||
|
||||
ASSERT_EQ(products.size(), 1);
|
||||
ASSERT_TRUE(QFile::exists(m_normalizedCacheRootDir.absoluteFilePath("pc/samplesproject/test1.asset1").toUtf8().constData()));
|
||||
ASSERT_TRUE(QFile::exists(m_normalizedCacheRootDir.absoluteFilePath("pc/test1.asset1").toUtf8().constData()));
|
||||
|
||||
m_mockApplicationManager->UnRegisterAllBuilders();
|
||||
|
||||
@@ -1723,15 +1729,15 @@ TEST_F(PathDependencyTest, AssetProcessed_Impl_DeferredPathResolution)
|
||||
AZStd::vector<TestAsset> dependencySources = { "dep1", "dep2" };
|
||||
// Start with mixed casing.
|
||||
ProductPathDependencySet dependencies = { {"Dep1.txt", AssetBuilderSDK::ProductPathDependencyType::SourceFile}, {"DEP2.asset2", AssetBuilderSDK::ProductPathDependencyType::ProductFile}, {"dep2.asset3", AssetBuilderSDK::ProductPathDependencyType::ProductFile} }; // Test depending on a source asset, and on a subset of product assets
|
||||
|
||||
|
||||
TestAsset mainFile("test_text");
|
||||
ASSERT_TRUE(ProcessAsset(mainFile, { { ".asset" }, {} }, dependencies));
|
||||
|
||||
|
||||
// ---------- Verify that we have unresolved path in ProductDependencies table ----------
|
||||
AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntryContainer dependencyContainer;
|
||||
ASSERT_TRUE(m_sharedConnection->GetProductDependencies(dependencyContainer));
|
||||
ASSERT_EQ(dependencyContainer.size(), dependencies.size());
|
||||
|
||||
|
||||
// All dependencies are stored lowercase in the database. Make the expected dependencies lowercase here to match that.
|
||||
for(auto& dependency : dependencies)
|
||||
{
|
||||
@@ -1752,12 +1758,12 @@ TEST_F(PathDependencyTest, AssetProcessed_Impl_DeferredPathResolution)
|
||||
{
|
||||
ASSERT_TRUE(ProcessAsset(dependency, { { ".asset1", ".asset2" }, { ".asset3" } }, {}));
|
||||
}
|
||||
|
||||
|
||||
// ---------- Verify that path has been found and resolved ----------
|
||||
dependencyContainer.clear();
|
||||
ASSERT_TRUE(m_sharedConnection->GetProductDependencies(dependencyContainer));
|
||||
|
||||
VerifyDependencies(dependencyContainer,
|
||||
VerifyDependencies(dependencyContainer,
|
||||
{
|
||||
dependencySources[0].m_products[0],
|
||||
dependencySources[0].m_products[1],
|
||||
@@ -1780,19 +1786,19 @@ TEST_F(PathDependencyTest, AssetProcessed_Impl_DeferredPathResolutionAlreadyReso
|
||||
// create dependees
|
||||
TestAsset dep1("dep1");
|
||||
TestAsset dep2("deP2"); // random casing to make sure the search is case-insensitive
|
||||
|
||||
|
||||
ASSERT_TRUE(ProcessAsset(dep1, { {".asset1"}, {".asset2"} }));
|
||||
ASSERT_TRUE(ProcessAsset(dep2, { {".asset1", ".asset2"}, {".asset3"} }));
|
||||
|
||||
|
||||
// -------- Make main test asset, with dependencies on products we just created -----
|
||||
TestAsset primaryFile("test_text");
|
||||
ASSERT_TRUE(ProcessAsset(primaryFile, { { ".asset" }, {} }, { {"dep1.txt", ProductPathDependencyType::SourceFile}, {"DEP2.asset2", ProductPathDependencyType::ProductFile}, {"Dep2.asset3", ProductPathDependencyType::ProductFile} }));
|
||||
|
||||
|
||||
// ---------- Verify that the dependency was recorded, and did not keep the path after resolution ----------
|
||||
AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntryContainer dependencyContainer;
|
||||
ASSERT_TRUE(m_sharedConnection->GetProductDependencies(dependencyContainer));
|
||||
|
||||
VerifyDependencies(dependencyContainer,
|
||||
VerifyDependencies(dependencyContainer,
|
||||
{
|
||||
dep1.m_products[0],
|
||||
dep1.m_products[1],
|
||||
@@ -1887,13 +1893,13 @@ TEST_F(PathDependencyTest, WildcardDependencies_Existing_ResolveCorrectly)
|
||||
|
||||
bool result = ProcessAsset(dep1, { {".asset1"}, {".asset2"} });
|
||||
ASSERT_TRUE(result) << "Failed to Process Assets";
|
||||
|
||||
|
||||
result = ProcessAsset(dep2, { {".asset1", ".asset2"}, {".asset3"} });
|
||||
ASSERT_TRUE(result) << "Failed to Process Assets";
|
||||
|
||||
|
||||
result = ProcessAsset(dep3, { {".asset1", ".asset2"}, {".asset3"} });
|
||||
ASSERT_TRUE(result) << "Failed to Process Assets";
|
||||
|
||||
|
||||
result = ProcessAsset(dep4, { {".asset1"}, {".asset3"} }); // This product will match on both dependencies, this will check to make sure we don't get duplicates
|
||||
ASSERT_TRUE(result) << "Failed to Process Assets";
|
||||
|
||||
@@ -1906,7 +1912,7 @@ TEST_F(PathDependencyTest, WildcardDependencies_Existing_ResolveCorrectly)
|
||||
AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntryContainer dependencyContainer;
|
||||
result = m_sharedConnection->GetProductDependencies(dependencyContainer);
|
||||
ASSERT_TRUE(result)<< "Failed to Get Product Dependencies";
|
||||
|
||||
|
||||
VerifyDependencies(dependencyContainer,
|
||||
{
|
||||
dep1.m_products[0],
|
||||
@@ -2054,14 +2060,14 @@ TEST_F(PathDependencyTest, WildcardDependencies_Deferred_ResolveCorrectly)
|
||||
AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntryContainer dependencyContainer;
|
||||
ASSERT_TRUE(m_sharedConnection->GetProductDependencies(dependencyContainer));
|
||||
|
||||
VerifyDependencies(dependencyContainer,
|
||||
VerifyDependencies(dependencyContainer,
|
||||
{
|
||||
dep1.m_products[0],
|
||||
dep1.m_products[1],
|
||||
dep2.m_products[2],
|
||||
dep3.m_products[2],
|
||||
dep4.m_products[0]
|
||||
},
|
||||
},
|
||||
{ "*p1.txt", "*.asset3" }
|
||||
);
|
||||
}
|
||||
@@ -2204,14 +2210,14 @@ void PathDependencyTest::RunWildcardTest(bool useCorrectDatabaseSeparator, Asset
|
||||
ASSERT_TRUE(ProcessAsset(matchingDepDeeperFolderMixedSlashes, { {".asset"}, {} })) << "Failed to Process " << matchingDepDeeperFolderMixedSlashes.m_name.c_str();
|
||||
ASSERT_TRUE(ProcessAsset(notMatchingDepInSubfolder, { {".asset"}, {} })) << "Failed to Process " << notMatchingDepInSubfolder.m_name.c_str();
|
||||
}
|
||||
|
||||
|
||||
// -------- Make main test asset, with dependencies on products we just created -----
|
||||
TestAsset primaryFile("test_text");
|
||||
const char* databaseSeparator = useCorrectDatabaseSeparator ? AZ_CORRECT_DATABASE_SEPARATOR_STRING : AZ_WRONG_DATABASE_SEPARATOR_STRING;
|
||||
|
||||
|
||||
AZStd::string extension = (pathDependencyType == ProductPathDependencyType::SourceFile) ? "txt" : "asset";
|
||||
AZStd::string wildcardString = AZStd::string::format("*testFolder%s*.%s", databaseSeparator, extension.c_str());
|
||||
|
||||
|
||||
ASSERT_TRUE(ProcessAsset(primaryFile, { { ".asset" }, {} }, { {wildcardString.c_str(), pathDependencyType}, })) << "Failed to Process " << primaryFile.m_name.c_str();
|
||||
|
||||
if (!buildDependenciesFirst)
|
||||
@@ -2354,7 +2360,7 @@ TEST_F(PathDependencyTest, AbsoluteDependencies_Deferred_ResolveCorrectly)
|
||||
// -------- Make main test asset, with dependencies on products that don't exist yet -----
|
||||
TestAsset primaryFile("test_text");
|
||||
ASSERT_TRUE(ProcessAsset(primaryFile, { { ".asset" }, {} },
|
||||
{
|
||||
{
|
||||
{absPathDep1.toUtf8().constData(), ProductPathDependencyType::SourceFile},
|
||||
{absPathDep2.toUtf8().constData(), ProductPathDependencyType::SourceFile},
|
||||
{absPathDep3.toUtf8().constData(), ProductPathDependencyType::SourceFile},
|
||||
@@ -2418,7 +2424,7 @@ TEST_F(PathDependencyTest, ChangeDependencies_Existing_ResolveCorrectly)
|
||||
|
||||
// Update again with different dependencies
|
||||
ASSERT_TRUE(ProcessAsset(primaryFile, { {".asset"} , {} }, { {absPath.toUtf8().constData(), ProductPathDependencyType::SourceFile} }));
|
||||
|
||||
|
||||
// ---------- Verify that the dependency was recorded, and did not keep the path after resolution ----------
|
||||
dependencyContainer.clear();
|
||||
ASSERT_TRUE(m_sharedConnection->GetProductDependencies(dependencyContainer));
|
||||
@@ -2582,7 +2588,7 @@ TEST_F(PathDependencyTest, SourceFileDependencyWithPrefix_Deferred_ResolvesCorre
|
||||
AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntryContainer dependencyContainer;
|
||||
ASSERT_TRUE(m_sharedConnection->GetProductDependencies(dependencyContainer));
|
||||
|
||||
VerifyDependencies(dependencyContainer,
|
||||
VerifyDependencies(dependencyContainer,
|
||||
{
|
||||
dep2.m_products[0],
|
||||
dep2.m_products[1],
|
||||
@@ -2834,7 +2840,7 @@ TEST_F(AssetProcessorManagerTest, AssetProcessedImpl_DifferentProductDependencie
|
||||
// and correctly stored the results into the dependency table.
|
||||
|
||||
//-------------------------------- EVALUATION PHASE -------------------------
|
||||
// at this point, the AP will have filed the asset away in its database and we can now validate that it actually
|
||||
// at this point, the AP will have filed the asset away in its database and we can now validate that it actually
|
||||
// did it correctly.
|
||||
// We expect to see two dependencies in the dependency table, each with the correct dependency, no duplicates, no lost data.
|
||||
AssetDatabaseConnection* sharedConnection = m_assetProcessorManager->m_stateData.get();
|
||||
@@ -2856,7 +2862,7 @@ TEST_F(AssetProcessorManagerTest, AssetProcessedImpl_DifferentProductDependencie
|
||||
// this also asserts uniqueness.
|
||||
ASSERT_EQ(countFound, 2);
|
||||
ASSERT_EQ(capturedTableEntries.size(), countFound); // if they were not unique asset IDs, they would have collapsed on top of each other.
|
||||
|
||||
|
||||
// make sure both assetIds are present:
|
||||
ASSERT_NE(capturedTableEntries.find(expectedIdOfProductA), capturedTableEntries.end());
|
||||
ASSERT_NE(capturedTableEntries.find(expectedIdOfProductB), capturedTableEntries.end());
|
||||
@@ -2927,7 +2933,7 @@ TEST_F(AssetProcessorManagerTest, AssessDeletedFile_OnJobInFlight_IsIgnored)
|
||||
m_isIdling = false;
|
||||
m_assetProcessorManager->AssetProcessed(capturedDetails.m_jobEntry, response);
|
||||
ASSERT_TRUE(BlockUntilIdle(5000));
|
||||
|
||||
|
||||
// at this point, everything should be up to date and ready for the test - there should be one source in the database
|
||||
// with numOutputsToSimulate products.
|
||||
// now, we simulate a job running to process the asset again, by modifying the timestamp on the file to be at least one second later.
|
||||
@@ -2959,7 +2965,7 @@ TEST_F(AssetProcessorManagerTest, AssessDeletedFile_OnJobInFlight_IsIgnored)
|
||||
ASSERT_FALSE(capturedDetails.m_destinationPath.isEmpty());
|
||||
// ----------------------------- TEST BEGINS HERE -----------------------------
|
||||
// simulte a very slow computer processing the file one output at a time and feeding file change notifies:
|
||||
|
||||
|
||||
// FROM THIS POINT ON we should see no new job create / cancellation or anything since we're just going to be messing with the cache.
|
||||
bool gotUnexpectedAssetToProcess = false;
|
||||
connection = QObject::connect(m_assetProcessorManager.get(), &AssetProcessorManager::AssetToProcess, [&gotUnexpectedAssetToProcess](JobDetails /*jobDetails*/)
|
||||
@@ -2994,7 +3000,7 @@ TEST_F(AssetProcessorManagerTest, AssessDeletedFile_OnJobInFlight_IsIgnored)
|
||||
|
||||
QString fileNameToGenerate = QString("test%1.txt").arg(outputIdx);
|
||||
QString filePathToGenerate = QDir(capturedDetails.m_destinationPath).absoluteFilePath(fileNameToGenerate);
|
||||
|
||||
|
||||
JobProduct product(filePathToGenerate.toUtf8().constData(), AZ::Uuid::CreateRandom(), static_cast<AZ::u32>(outputIdx));
|
||||
response.m_outputProducts.push_back(product);
|
||||
|
||||
@@ -3005,7 +3011,7 @@ TEST_F(AssetProcessorManagerTest, AssessDeletedFile_OnJobInFlight_IsIgnored)
|
||||
// simulate the file watcher showing the deletion occuring:
|
||||
notifyAPM("AssessDeletedFile", filePathToGenerate, shouldBlockAndWaitThisTime);
|
||||
UnitTestUtils::CreateDummyFile(filePathToGenerate, "an output");
|
||||
|
||||
|
||||
// let the APM go for a significant amount of time so that it simulates a slow thread copying a large file with lots of events about it pouring in.
|
||||
for (int repeatLoop = 0; repeatLoop < 100; ++repeatLoop)
|
||||
{
|
||||
@@ -3020,7 +3026,7 @@ TEST_F(AssetProcessorManagerTest, AssessDeletedFile_OnJobInFlight_IsIgnored)
|
||||
QMetaObject::invokeMethod(m_assetProcessorManager.get(), "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, QString(filePathToGenerate)));
|
||||
QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents, 1);
|
||||
ASSERT_FALSE(gotUnexpectedAssetToProcess);
|
||||
|
||||
|
||||
// now tell it to stop ignoring the cache delete and let it do the next one.
|
||||
EBUS_EVENT(AssetProcessor::ProcessingJobInfoBus, EndCacheFileUpdate, filePathToGenerate.toUtf8().data(), false);
|
||||
|
||||
@@ -3075,7 +3081,7 @@ TEST_F(AssetProcessorManagerTest, UpdateSourceFileDependenciesDatabase_BasicTest
|
||||
// each file we will take a different approach to publishing: rel path, and UUID:
|
||||
job.m_sourceFileDependencies.push_back(AZStd::make_pair<AZ::Uuid, AssetBuilderSDK::SourceFileDependency>(dummyBuilderUUID, { "a.txt", AZ::Uuid::CreateNull() }));
|
||||
job.m_sourceFileDependencies.push_back(AZStd::make_pair<AZ::Uuid, AssetBuilderSDK::SourceFileDependency>(dummyBuilderUUID, { "", uuidOfB }));
|
||||
|
||||
|
||||
// it is currently assumed that the only fields that we care about in JobDetails is the builder busId and the job dependencies themselves:
|
||||
JobDetails newDetails;
|
||||
newDetails.m_assetBuilderDesc.m_busId = dummyBuilderUUID;
|
||||
@@ -3267,7 +3273,7 @@ TEST_F(AssetProcessorManagerTest, UpdateSourceFileDependenciesDatabase_MissingFi
|
||||
// this indirectly verifies the QueryAbsolutePathDependenciesRecursive function also but it has its own dedicated tests, above.
|
||||
AssetProcessor::SourceFilesForFingerprintingContainer deps;
|
||||
m_assetProcessorManager.get()->QueryAbsolutePathDependenciesRecursive(QString::fromUtf8("assetProcessorManagerTest.txt"), deps, AzToolsFramework::AssetDatabase::SourceFileDependencyEntry::DEP_SourceToSource, false);
|
||||
|
||||
|
||||
// we should find all of the deps, but not the placeholders.
|
||||
|
||||
EXPECT_EQ(deps.size(), 2);
|
||||
@@ -3453,7 +3459,7 @@ TEST_F(AssetProcessorManagerTest, UpdateSourceFileDependenciesDatabase_MissingFi
|
||||
EXPECT_EQ(deps.size(), 2);
|
||||
EXPECT_NE(deps.find(absPath.toUtf8().constData()), deps.end());
|
||||
EXPECT_NE(deps.find(dependsOnFile1_Job.toUtf8().constData()), deps.end()); // c
|
||||
|
||||
|
||||
// in addition, we expect to have the original file that depends on B appear in the analysis queue, since something it depends on appeared:
|
||||
QString normalizedSourcePath = AssetUtilities::NormalizeFilePath(absPath);
|
||||
EXPECT_TRUE(m_assetProcessorManager->m_alreadyActiveFiles.contains(normalizedSourcePath));
|
||||
@@ -4407,7 +4413,7 @@ TEST_F(AssetProcessorManagerTest, SourceFileProcessFailure_ClearsFingerprint)
|
||||
|
||||
ASSERT_TRUE(found);
|
||||
ASSERT_NE(source.m_analysisFingerprint, "");
|
||||
|
||||
|
||||
// Modify the file and run it through AP again, but this time signal a failure
|
||||
|
||||
{
|
||||
@@ -4655,12 +4661,12 @@ TEST_F(ModtimeScanningTest, ModtimeSkipping_EnablePlatform_ShouldProcessFilesFor
|
||||
// Enable the features we're testing
|
||||
m_assetProcessorManager->m_allowModtimeSkippingFeature = true;
|
||||
AssetUtilities::SetUseFileHashOverride(true, true);
|
||||
|
||||
|
||||
// Enable es3 platform after the initial SetUp has already processed the files for pc
|
||||
QDir tempPath(m_tempDir.path());
|
||||
AssetBuilderSDK::PlatformInfo es3Platform("es3", { "host", "renderer" });
|
||||
m_config->EnablePlatform(es3Platform, true);
|
||||
|
||||
|
||||
// There's no way to remove scanfolders and adding a new one after enabling the platform will cause the pc assets to build as well, which we don't want
|
||||
// Instead we'll just const cast the vector and modify the enabled platforms for the scanfolder
|
||||
auto& platforms = const_cast<AZStd::vector<AssetBuilderSDK::PlatformInfo>&>(m_config->GetScanFolderAt(0).GetPlatforms());
|
||||
@@ -4743,7 +4749,7 @@ TEST_F(ModtimeScanningTest, ModtimeSkipping_ModifyFile)
|
||||
using namespace AzToolsFramework::AssetSystem;
|
||||
|
||||
SetFileContents(m_data->m_absolutePath[1].toUtf8().constData(), "hello world");
|
||||
|
||||
|
||||
// Enable the features we're testing
|
||||
m_assetProcessorManager->m_allowModtimeSkippingFeature = true;
|
||||
AssetUtilities::SetUseFileHashOverride(true, true);
|
||||
@@ -4861,7 +4867,7 @@ TEST_F(ModtimeScanningTest, ModtimeSkipping_DeleteFile)
|
||||
{
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents, 10);
|
||||
} while (m_data->m_deletedSources.size() < m_data->m_relativePathFromWatchFolder[0].size() && timer.elapsed() < 5000);
|
||||
|
||||
|
||||
ASSERT_EQ(m_data->m_mockBuilderInfoHandler.m_createJobsCount, 0);
|
||||
ASSERT_EQ(m_data->m_processResults.size(), 0);
|
||||
ASSERT_THAT(m_data->m_deletedSources, testing::ElementsAre(m_data->m_relativePathFromWatchFolder[0]));
|
||||
@@ -4885,7 +4891,7 @@ TEST_F(ModtimeScanningTest, ReprocessRequest_SourceWithDependency_BothWillProces
|
||||
|
||||
using SourceFileDependencyEntry = AzToolsFramework::AssetDatabase::SourceFileDependencyEntry;
|
||||
|
||||
SourceFileDependencyEntry newEntry1;
|
||||
SourceFileDependencyEntry newEntry1;
|
||||
newEntry1.m_sourceDependencyID = AzToolsFramework::AssetDatabase::InvalidEntryId;
|
||||
newEntry1.m_builderGuid = AZ::Uuid::CreateRandom();
|
||||
newEntry1.m_source = m_data->m_absolutePath[0].toUtf8().constData();
|
||||
@@ -4953,7 +4959,7 @@ void MockBuilderInfoHandler::CreateJobs(const AssetBuilderSDK::CreateJobsRequest
|
||||
|
||||
if (!m_jobDependencyFilePath.isEmpty())
|
||||
{
|
||||
jobDescriptor.m_jobDependencyList.push_back(AssetBuilderSDK::JobDependency("Mock Job", "pc", AssetBuilderSDK::JobDependencyType::Order,
|
||||
jobDescriptor.m_jobDependencyList.push_back(AssetBuilderSDK::JobDependency("Mock Job", "pc", AssetBuilderSDK::JobDependencyType::Order,
|
||||
AssetBuilderSDK::SourceFileDependency(m_jobDependencyFilePath.toUtf8().constData(), AZ::Uuid::CreateNull())));
|
||||
}
|
||||
|
||||
@@ -5127,7 +5133,7 @@ TEST_F(AssetProcessorManagerTest, UpdateSourceFileDependenciesDatabase_WildcardM
|
||||
m_assetProcessorManager.get()->m_stateData->QueryDependsOnSourceBySourceDependency("wildcardTest.txt", nullptr, AzToolsFramework::AssetDatabase::SourceFileDependencyEntry::DEP_SourceLikeMatch, callbackFunction);
|
||||
EXPECT_EQ(wildcardDeps.size(), 2);
|
||||
|
||||
// The database should have the wildcard record and the individual dependency on b and c at this point, now we add new files
|
||||
// The database should have the wildcard record and the individual dependency on b and c at this point, now we add new files
|
||||
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(dependsOnFileb1_Source, QString("tempdata\n")));
|
||||
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(dependsOnFilec1_Job, QString("tempdata\n")));
|
||||
|
||||
@@ -5307,7 +5313,7 @@ TEST_F(DuplicateProductsTest, SameSource_MultipleBuilder_DuplicateProductJobs_Em
|
||||
QDir tempPath(m_tempDir.path());
|
||||
QString sourceFile;
|
||||
AZStd::vector<JobDetails> jobDetails;
|
||||
|
||||
|
||||
ProcessJobResponse response;
|
||||
SetupDuplicateProductsTest(sourceFile, tempPath, productFile, jobDetails, response, false, "txt");
|
||||
|
||||
@@ -5397,7 +5403,7 @@ void JobDependencyTest::SetUp()
|
||||
m_data->m_mockBuilderInfoHandler.BusConnect();
|
||||
|
||||
QDir tempPath(m_tempDir.path());
|
||||
|
||||
|
||||
QString watchFolderPath = tempPath.absoluteFilePath("subfolder1");
|
||||
const ScanFolderInfo* scanFolder = m_config->GetScanFolderByPath(watchFolderPath);
|
||||
|
||||
@@ -5460,7 +5466,7 @@ TEST_F(JobDependencyTest, JobDependency_ThatWasJustRun_IsFound)
|
||||
TEST_F(JobDependencyTest, JobDependency_ThatHasNotRun_IsNotFound)
|
||||
{
|
||||
AZStd::vector<JobDetails> capturedDetails;
|
||||
|
||||
|
||||
capturedDetails.clear();
|
||||
m_data->m_mockBuilderInfoHandler.m_jobDependencyFilePath = "c.txt";
|
||||
CaptureJobs(capturedDetails, "subfolder1/b.txt");
|
||||
@@ -5654,7 +5660,7 @@ TEST_F(ChainJobDependencyTest, TestChainDependency_Multi)
|
||||
{
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents, 10);
|
||||
} while (finishedJobs.size() < capturedDetails.size() && timer.elapsed() < 5000);
|
||||
|
||||
|
||||
ASSERT_EQ(finishedJobs.size(), capturedDetails.size());
|
||||
|
||||
// Test that the jobs completed in the correct order (captureDetails has the correct ordering)
|
||||
@@ -5775,7 +5781,7 @@ TEST_F(DeleteTest, DeleteFolderSharedAcrossTwoScanFolders_CorrectFileAndFolderAr
|
||||
QDir tempPath(m_tempDir.path());
|
||||
QString absPath(tempPath.absoluteFilePath("subfolder1/textures"));
|
||||
QDir(absPath).removeRecursively();
|
||||
|
||||
|
||||
AZStd::vector<AZStd::string> deletedFolders;
|
||||
QObject::connect(m_assetProcessorManager.get(), &AssetProcessorManager::SourceFolderDeleted, [&deletedFolders](QString file)
|
||||
{
|
||||
|
||||
+95
-67
@@ -15,7 +15,7 @@
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
|
||||
const char TestAppRoot[] = ":/testdata";
|
||||
const char TestAppRoot[] = "@exefolder@/testdata";
|
||||
const char EmptyDummyProjectName[] = "EmptyDummyProject";
|
||||
const char DummyProjectName[] = "DummyProject";
|
||||
|
||||
@@ -54,10 +54,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_BadPlatform)
|
||||
using namespace AzToolsFramework::AssetSystem;
|
||||
using namespace AssetProcessor;
|
||||
|
||||
const char* configRoot = ":/testdata/config_broken_badplatform";
|
||||
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
|
||||
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_broken_badplatform");
|
||||
ASSERT_TRUE(configRoot);
|
||||
UnitTestPlatformConfiguration config;
|
||||
m_absorber.Clear();
|
||||
ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot, TestAppRoot, EmptyDummyProjectName, false, false));
|
||||
ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false));
|
||||
ASSERT_GT(m_absorber.m_numErrorsAbsorbed, 0);
|
||||
}
|
||||
|
||||
@@ -67,10 +69,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_NoPlatform)
|
||||
using namespace AzToolsFramework::AssetSystem;
|
||||
using namespace AssetProcessor;
|
||||
|
||||
const char* configRoot = ":/testdata/config_broken_noplatform";
|
||||
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
|
||||
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_broken_noplatform");
|
||||
ASSERT_TRUE(configRoot);
|
||||
UnitTestPlatformConfiguration config;
|
||||
m_absorber.Clear();
|
||||
ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot, TestAppRoot, EmptyDummyProjectName, false, false));
|
||||
ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false));
|
||||
ASSERT_GT(m_absorber.m_numErrorsAbsorbed, 0);
|
||||
}
|
||||
|
||||
@@ -79,10 +83,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_NoScanFolders)
|
||||
using namespace AzToolsFramework::AssetSystem;
|
||||
using namespace AssetProcessor;
|
||||
|
||||
const char* configRoot = ":/testdata/config_broken_noscans";
|
||||
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
|
||||
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_broken_noscans");
|
||||
ASSERT_TRUE(configRoot);
|
||||
UnitTestPlatformConfiguration config;
|
||||
m_absorber.Clear();
|
||||
ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot, TestAppRoot, EmptyDummyProjectName, false, false));
|
||||
ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false));
|
||||
ASSERT_GT(m_absorber.m_numErrorsAbsorbed, 0);
|
||||
}
|
||||
|
||||
@@ -91,10 +97,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_BrokenRecognizers)
|
||||
using namespace AzToolsFramework::AssetSystem;
|
||||
using namespace AssetProcessor;
|
||||
|
||||
const char* configRoot = ":/testdata/config_broken_recognizers";
|
||||
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
|
||||
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_broken_recognizers");
|
||||
ASSERT_TRUE(configRoot);
|
||||
UnitTestPlatformConfiguration config;
|
||||
m_absorber.Clear();
|
||||
ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot, TestAppRoot, EmptyDummyProjectName, false, false));
|
||||
ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false));
|
||||
ASSERT_GT(m_absorber.m_numErrorsAbsorbed, 0);
|
||||
}
|
||||
|
||||
@@ -103,10 +111,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Regular_Platforms)
|
||||
using namespace AzToolsFramework::AssetSystem;
|
||||
using namespace AssetProcessor;
|
||||
|
||||
const char* configRoot = ":/testdata/config_regular";
|
||||
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
|
||||
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular");
|
||||
ASSERT_TRUE(configRoot);
|
||||
UnitTestPlatformConfiguration config;
|
||||
m_absorber.Clear();
|
||||
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot, TestAppRoot, EmptyDummyProjectName, false, false));
|
||||
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false));
|
||||
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
|
||||
|
||||
// verify the data.
|
||||
@@ -127,42 +137,43 @@ TEST_F(PlatformConfigurationUnitTests, TestReadScanFolderRoot_FromSettingsRegist
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
ASSERT_NE(nullptr, settingsRegistry);
|
||||
|
||||
AZ::SettingsRegistryInterface::Specializations apSpecializations;
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_EngineRegistry(*settingsRegistry, AZ_TRAIT_OS_PLATFORM_CODENAME, apSpecializations);
|
||||
|
||||
struct ScanFolderVisitor
|
||||
: AZ::SettingsRegistryInterface::Visitor
|
||||
QTemporaryDir tempEngineRoot;
|
||||
QDir tempPath(tempEngineRoot.path());
|
||||
QString testScanFolderSetregPath = tempPath.absoluteFilePath("test.setreg");
|
||||
UnitTestUtils::CreateDummyFile(testScanFolderSetregPath, QString(R"({ "Amazon":
|
||||
{
|
||||
void Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value)
|
||||
"AssetProcessor":
|
||||
{
|
||||
if (valueName == "recursive")
|
||||
{
|
||||
m_isRecursive = value != 0;
|
||||
}
|
||||
else if (valueName == "order")
|
||||
{
|
||||
m_scanOrder = value;
|
||||
"Settings":
|
||||
{
|
||||
"ScanFolder SettingsRegistryTest":
|
||||
{
|
||||
"watch": "_TestPath",
|
||||
"recursive": false,
|
||||
"order": 20000
|
||||
}
|
||||
}
|
||||
}
|
||||
void Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value)
|
||||
{
|
||||
if (valueName == "watch")
|
||||
{
|
||||
m_watchPath = value;
|
||||
}
|
||||
}
|
||||
AZ::SettingsRegistryInterface::FixedValueString m_watchPath;
|
||||
bool m_isRecursive{};
|
||||
int m_scanOrder{};
|
||||
};
|
||||
}
|
||||
}\n)"));
|
||||
|
||||
ScanFolderVisitor scanFolderVisitor;
|
||||
EXPECT_TRUE(settingsRegistry->Visit(scanFolderVisitor, "/Amazon/AssetProcessor/Settings/ScanFolder Root"));
|
||||
EXPECT_TRUE(AssetProcessor::PlatformConfiguration::MergeConfigFileToSettingsRegistry(*settingsRegistry, testScanFolderSetregPath.toUtf8().data()));
|
||||
|
||||
AZStd::string watchPath;
|
||||
bool recurseScanFolder{ true };
|
||||
AZ::s64 scanOrder{};
|
||||
|
||||
EXPECT_TRUE(settingsRegistry->Get(watchPath, AZ::SettingsRegistryInterface::FixedValueString(AssetProcessor::AssetProcessorSettingsKey)
|
||||
+ "/ScanFolder SettingsRegistryTest/watch"));
|
||||
EXPECT_TRUE(settingsRegistry->Get(recurseScanFolder, AZ::SettingsRegistryInterface::FixedValueString(AssetProcessor::AssetProcessorSettingsKey)
|
||||
+ "/ScanFolder SettingsRegistryTest/recursive"));
|
||||
EXPECT_TRUE(settingsRegistry->Get(scanOrder, AZ::SettingsRegistryInterface::FixedValueString(AssetProcessor::AssetProcessorSettingsKey)
|
||||
+ "/ScanFolder SettingsRegistryTest/order"));
|
||||
|
||||
// These test values come from the <dev_root>/Engine/Registry/AssetProcessorPlatformConfig.setreg file
|
||||
EXPECT_STREQ("@ROOT@", scanFolderVisitor.m_watchPath.c_str());
|
||||
EXPECT_FALSE(scanFolderVisitor.m_isRecursive);
|
||||
EXPECT_EQ(10000, scanFolderVisitor.m_scanOrder);
|
||||
EXPECT_STREQ("_TestPath", watchPath.c_str());
|
||||
EXPECT_FALSE(recurseScanFolder);
|
||||
EXPECT_EQ(20000, scanOrder);
|
||||
}
|
||||
|
||||
|
||||
@@ -340,34 +351,36 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularScanfolder)
|
||||
using namespace AzToolsFramework::AssetSystem;
|
||||
using namespace AssetProcessor;
|
||||
|
||||
const char* configRoot = ":/testdata/config_regular";
|
||||
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
|
||||
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular");
|
||||
ASSERT_TRUE(configRoot);
|
||||
UnitTestPlatformConfiguration config;
|
||||
m_absorber.Clear();
|
||||
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot, TestAppRoot, EmptyDummyProjectName, false, false));
|
||||
AssetUtilities::ComputeProjectName(EmptyDummyProjectName, true);
|
||||
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false));
|
||||
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
|
||||
|
||||
ASSERT_EQ(config.GetScanFolderCount(), 3); // the two, and then the one that has the same data as prior but different identifier.
|
||||
QString scanName = AssetUtilities::ComputeGameName() + " Scan Folder";
|
||||
QString scanName = AssetUtilities::ComputeProjectPath() + " Scan Folder";
|
||||
ASSERT_EQ(config.GetScanFolderAt(0).GetDisplayName(), scanName);
|
||||
ASSERT_EQ(config.GetScanFolderAt(0).GetOutputPrefix(), QString());
|
||||
ASSERT_EQ(config.GetScanFolderAt(0).RecurseSubFolders(), true);
|
||||
ASSERT_EQ(config.GetScanFolderAt(0).GetOrder(), 0);
|
||||
// its important that this does NOT change and this makes sure the old way of doing it (case-sensitive name) persists
|
||||
ASSERT_EQ(config.GetScanFolderAt(0).GetPortableKey(), QString("from-ini-file-Game"));
|
||||
ASSERT_EQ(config.GetScanFolderAt(0).GetPortableKey(), QString("Game"));
|
||||
|
||||
ASSERT_EQ(config.GetScanFolderAt(1).GetDisplayName(), QString("FeatureTests"));
|
||||
ASSERT_EQ(config.GetScanFolderAt(1).GetOutputPrefix(), QString("featuretestsoutputfolder")); // to prove its not related to display name
|
||||
ASSERT_EQ(config.GetScanFolderAt(1).RecurseSubFolders(), false);
|
||||
ASSERT_EQ(config.GetScanFolderAt(1).GetOrder(), 5000);
|
||||
// this proves that the featuretests name is used instead of the output prefix
|
||||
ASSERT_EQ(config.GetScanFolderAt(1).GetPortableKey(), QString("from-ini-file-FeatureTests"));
|
||||
ASSERT_EQ(config.GetScanFolderAt(1).GetPortableKey(), QString("FeatureTests"));
|
||||
|
||||
ASSERT_EQ(config.GetScanFolderAt(2).GetDisplayName(), QString("FeatureTests2"));
|
||||
ASSERT_EQ(config.GetScanFolderAt(2).GetOutputPrefix(), QString("featuretestsoutputfolder")); // to prove its not related to display name
|
||||
ASSERT_EQ(config.GetScanFolderAt(2).RecurseSubFolders(), false);
|
||||
ASSERT_EQ(config.GetScanFolderAt(2).GetOrder(), 6000);
|
||||
// this proves that the featuretests name is used instead of the output prefix
|
||||
ASSERT_EQ(config.GetScanFolderAt(2).GetPortableKey(), QString("from-ini-file-FeatureTests2"));
|
||||
ASSERT_EQ(config.GetScanFolderAt(2).GetPortableKey(), QString("FeatureTests2"));
|
||||
}
|
||||
|
||||
TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularScanfolderPlatformSpecific)
|
||||
@@ -375,10 +388,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularScanfolderP
|
||||
using namespace AzToolsFramework::AssetSystem;
|
||||
using namespace AssetProcessor;
|
||||
|
||||
const char* configRoot = ":/testdata/config_regular_platform_scanfolder";
|
||||
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
|
||||
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular_platform_scanfolder");
|
||||
ASSERT_TRUE(configRoot);
|
||||
UnitTestPlatformConfiguration config;
|
||||
m_absorber.Clear();
|
||||
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot, TestAppRoot, EmptyDummyProjectName, false, false));
|
||||
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false));
|
||||
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
|
||||
|
||||
ASSERT_EQ(config.GetScanFolderCount(), 5);
|
||||
@@ -419,10 +434,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularExcludes)
|
||||
using namespace AzToolsFramework::AssetSystem;
|
||||
using namespace AssetProcessor;
|
||||
|
||||
const char* configRoot = ":/testdata/config_regular";
|
||||
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
|
||||
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular");
|
||||
ASSERT_TRUE(configRoot);
|
||||
UnitTestPlatformConfiguration config;
|
||||
m_absorber.Clear();
|
||||
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot, TestAppRoot, EmptyDummyProjectName, false, false));
|
||||
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false));
|
||||
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
|
||||
|
||||
ASSERT_TRUE(config.IsFileExcluded("blahblah/$tmp_01.test"));
|
||||
@@ -446,10 +463,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Recognizers)
|
||||
const char* platformWhichIsNotCurrentPlatform = "pc";
|
||||
#endif
|
||||
|
||||
const char* configRoot = ":/testdata/config_regular";
|
||||
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
|
||||
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular");
|
||||
ASSERT_TRUE(configRoot);
|
||||
UnitTestPlatformConfiguration config;
|
||||
m_absorber.Clear();
|
||||
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot, TestAppRoot, EmptyDummyProjectName, false, false));
|
||||
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false));
|
||||
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
|
||||
|
||||
const AssetProcessor::RecognizerContainer& recogs = config.GetAssetRecognizerContainer();
|
||||
@@ -519,11 +538,13 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Overrides)
|
||||
{
|
||||
using namespace AzToolsFramework::AssetSystem;
|
||||
using namespace AssetProcessor;
|
||||
const char* configRoot = ":/testdata/config_regular";
|
||||
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
|
||||
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular");
|
||||
ASSERT_TRUE(configRoot);
|
||||
UnitTestPlatformConfiguration config;
|
||||
m_absorber.Clear();
|
||||
|
||||
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot, TestAppRoot, DummyProjectName, false, false));
|
||||
|
||||
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), DummyProjectName, false, false));
|
||||
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
|
||||
|
||||
const AssetProcessor::RecognizerContainer& recogs = config.GetAssetRecognizerContainer();
|
||||
@@ -565,7 +586,7 @@ TEST_F(PlatformConfigurationUnitTests, Test_GemHandling)
|
||||
QTemporaryDir tempEngineRoot;
|
||||
QDir tempPath(tempEngineRoot.path());
|
||||
AssetUtilities::ResetAssetRoot();
|
||||
AssetUtilities::ComputeGameName("SamplesProject", true);
|
||||
AssetUtilities::ComputeProjectName("SamplesProject", true);
|
||||
|
||||
QDir computedEngineRoot;
|
||||
ASSERT_TRUE(AssetUtilities::ComputeAssetRoot(computedEngineRoot, &tempPath));
|
||||
@@ -576,9 +597,11 @@ TEST_F(PlatformConfigurationUnitTests, Test_GemHandling)
|
||||
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("Gems/LyShine/AssetProcessorGemConfig.ini"), ";nothing to see here"));
|
||||
|
||||
// note that it is expected that the gems system gives us absolute paths.
|
||||
AZStd::vector<AzToolsFramework::AssetUtils::GemInfo> fakeGems;
|
||||
fakeGems.push_back({ "LyShine", "Gems/LyShine", tempPath.absoluteFilePath("Gems/LyShine").toUtf8().constData(), "0fefab3f13364722b2eab3b96ce2bf20", true, false });// true = pretend this is a game gem.
|
||||
fakeGems.push_back({ "LmbrCentral", "Gems/LmbrCentral/v2", tempPath.absoluteFilePath("Gems/LmbrCentral/v2").toUtf8().constData(), "ff06785f7145416b9d46fde39098cb0c", false, false });
|
||||
AZStd::vector<AzFramework::GemInfo> fakeGems;
|
||||
fakeGems.emplace_back("LyShine");// true = pretend this is a game gem.
|
||||
fakeGems.back().m_absoluteSourcePaths.push_back(tempPath.absoluteFilePath("Gems/LyShine").toUtf8().constData());
|
||||
fakeGems.emplace_back("LmbrCentral");
|
||||
fakeGems.back().m_absoluteSourcePaths.push_back(tempPath.absoluteFilePath("Gems/LmbrCentral/v2").toUtf8().constData());
|
||||
|
||||
// reading gems via the Gems System is already to be tested in the actual Gems API tests.
|
||||
// to avoid trying to load those DLLs we avoid calling the actual ReadGems function
|
||||
@@ -593,8 +616,7 @@ TEST_F(PlatformConfigurationUnitTests, Test_GemHandling)
|
||||
EXPECT_TRUE(config.GetScanFolderAt(0).GetOutputPrefix().isEmpty());
|
||||
EXPECT_TRUE(config.GetScanFolderAt(0).RecurseSubFolders());
|
||||
// the first one is a game gem, so its order should be above 1 but below 100.
|
||||
EXPECT_GE(config.GetScanFolderAt(0).GetOrder(), 1);
|
||||
EXPECT_LE(config.GetScanFolderAt(0).GetOrder(), 100);
|
||||
EXPECT_GE(config.GetScanFolderAt(0).GetOrder(), 100);
|
||||
EXPECT_EQ(0, config.GetScanFolderAt(0).ScanPath().compare(expectedScanFolder, Qt::CaseInsensitive));
|
||||
|
||||
// for each gem, there are currently 1 scan folder, the gem assets folder, with no output prefix
|
||||
@@ -618,15 +640,17 @@ TEST_F(PlatformConfigurationUnitTests, Test_MetaFileTypes)
|
||||
ASSERT_TRUE(QString::compare(config.GetMetaDataFileTypeAt(1).second, "zzzz", Qt::CaseInsensitive) == 0);
|
||||
}
|
||||
|
||||
TEST_F(PlatformConfigurationUnitTests, ReadCheckSever_FromConfig_Valid)
|
||||
TEST_F(PlatformConfigurationUnitTests, ReadCheckServer_FromConfig_Valid)
|
||||
{
|
||||
using namespace AzToolsFramework::AssetSystem;
|
||||
using namespace AssetProcessor;
|
||||
|
||||
const char* configRoot = ":/testdata/config_regular";
|
||||
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
|
||||
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular");
|
||||
ASSERT_TRUE(configRoot);
|
||||
UnitTestPlatformConfiguration config;
|
||||
m_absorber.Clear();
|
||||
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot, TestAppRoot, EmptyDummyProjectName, false, false));
|
||||
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false));
|
||||
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
|
||||
|
||||
const AssetProcessor::RecognizerContainer& recogs = config.GetAssetRecognizerContainer();
|
||||
@@ -642,7 +666,11 @@ TEST_F(PlatformConfigurationUnitTests, PlatformConfigFile_IsPresent_Found)
|
||||
QTemporaryDir tempEngineRoot;
|
||||
QDir tempPath(tempEngineRoot.path());
|
||||
AssetUtilities::ResetAssetRoot();
|
||||
AssetUtilities::ComputeGameName("SamplesProject", true);
|
||||
AssetUtilities::ComputeProjectName("SamplesProject", true);
|
||||
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
ASSERT_NE(nullptr, settingsRegistry);
|
||||
settingsRegistry->Set(AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder, tempPath.absolutePath().toUtf8().constData());
|
||||
|
||||
QDir computedEngineRoot;
|
||||
ASSERT_TRUE(AssetUtilities::ComputeAssetRoot(computedEngineRoot, &tempPath));
|
||||
@@ -655,7 +683,7 @@ TEST_F(PlatformConfigurationUnitTests, PlatformConfigFile_IsPresent_Found)
|
||||
platformConfigPath.append("TestPlatform/");
|
||||
platformConfigPath.append(AssetProcessor::AssetProcessorPlatformConfigFileName);
|
||||
|
||||
QStringList platformConfigList;
|
||||
AZStd::vector<AZ::IO::Path> platformConfigList;
|
||||
ASSERT_FALSE(config.AddPlatformConfigFilePaths(platformConfigList));
|
||||
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath(platformConfigPath), ";nothing to see here"));
|
||||
ASSERT_TRUE(config.AddPlatformConfigFilePaths(platformConfigList));
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include <QHash>
|
||||
|
||||
#include "native/tests/AssetProcessorTest.h"
|
||||
#include <native/utilities/PlatformConfiguration.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/std/parallel/thread.h>
|
||||
#include <AzTest/AzTest.h>
|
||||
|
||||
@@ -519,11 +521,18 @@ TEST_F(AssetUtilitiesTest, GetServerAddress_ReadFromConfig_Valid)
|
||||
QTemporaryDir tempDir;
|
||||
QDir tempPath(tempDir.path());
|
||||
QString assetServerAddress("T:/AssetServerCacheDummyFolder");
|
||||
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("AssetProcessorPlatformConfig.ini"), QString("[Server]\ncacheServerAddress=%1\n").arg(assetServerAddress));
|
||||
QString assetProcesorPlatformConfigPath = tempPath.absoluteFilePath("AssetProcessorPlatformConfig.ini");
|
||||
UnitTestUtils::CreateDummyFile(assetProcesorPlatformConfigPath, QString("[Server]\ncacheServerAddress=%1\n").arg(assetServerAddress));
|
||||
|
||||
AssetUtilities::ResetAssetRoot();
|
||||
QDir newRoot;
|
||||
AssetUtilities::ComputeEngineRoot(newRoot, &tempPath);
|
||||
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
AZ::SettingsRegistryMergeUtils::ConfigParserSettings configParserSettings;
|
||||
configParserSettings.m_registryRootPointerPath = AssetProcessor::AssetProcessorSettingsKey;
|
||||
AssetProcessor::PlatformConfiguration::MergeConfigFileToSettingsRegistry(*settingsRegistry,
|
||||
assetProcesorPlatformConfigPath.toUtf8().data());
|
||||
QString assetServerAddressReturned = AssetUtilities::ServerAddress();
|
||||
EXPECT_STREQ(assetServerAddressReturned.toUtf8().data(), assetServerAddress.toUtf8().data());
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ void MainWindow::Activate()
|
||||
|
||||
ui->projectLabel->setText(QStringLiteral("%1: %2")
|
||||
.arg(tr("Project"))
|
||||
.arg(m_guiApplicationManager->GetGameName()));
|
||||
.arg(QDir{m_guiApplicationManager->GetProjectPath()}.absolutePath()));
|
||||
|
||||
ui->rootLabel->setText(QStringLiteral("%1: %2")
|
||||
.arg(tr("Root"))
|
||||
|
||||
@@ -1,655 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#if defined(UNIT_TEST)
|
||||
|
||||
#include <AzCore/base.h>
|
||||
|
||||
#include "AssetCatalogUnitTests.h"
|
||||
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/Casting/lossy_cast.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzFramework/Asset/AssetProcessorMessages.h>
|
||||
|
||||
#include <AzToolsFramework/Asset/AssetProcessorMessages.h>
|
||||
#include <native/AssetManager/assetProcessorManager.h>
|
||||
#include <native/utilities/PlatformConfiguration.h>
|
||||
#include <native/utilities/AssetBuilderInfo.h>
|
||||
#include <native/AssetManager/assetScanFolderInfo.h>
|
||||
#include <native/utilities/assetUtils.h>
|
||||
#include <native/resourcecompiler/RCBuilder.h>
|
||||
#include <native/assetprocessor.h>
|
||||
|
||||
#include <QTemporaryDir>
|
||||
#include <QString>
|
||||
#include <QCoreApplication>
|
||||
#include <QSet>
|
||||
#include <QList>
|
||||
#include <QTime>
|
||||
#include <QThread>
|
||||
#include <QPair>
|
||||
#include <AzToolsFramework/API/AssetDatabaseBus.h>
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
|
||||
namespace AssetProcessor
|
||||
{
|
||||
using namespace UnitTestUtils;
|
||||
using namespace AzFramework::AssetSystem;
|
||||
using namespace AzToolsFramework::AssetSystem;
|
||||
using namespace AzToolsFramework::AssetDatabase;
|
||||
|
||||
REGISTER_UNIT_TEST(AssetCatalogUnitTests)
|
||||
REGISTER_UNIT_TEST(AssetCatalogUnitTests_AssetInfo)
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// a utility class to redirect the location the database is stored to a different location so that we don't
|
||||
// touch real data during unit tests.
|
||||
class FakeDatabaseLocationListener
|
||||
: protected AzToolsFramework::AssetDatabase::AssetDatabaseRequests::Bus::Handler
|
||||
{
|
||||
public:
|
||||
FakeDatabaseLocationListener(const char* desiredLocation, const char* assetPath)
|
||||
: m_location(desiredLocation)
|
||||
, m_assetPath(assetPath)
|
||||
{
|
||||
AzToolsFramework::AssetDatabase::AssetDatabaseRequests::Bus::Handler::BusConnect();
|
||||
}
|
||||
~FakeDatabaseLocationListener()
|
||||
{
|
||||
AzToolsFramework::AssetDatabase::AssetDatabaseRequests::Bus::Handler::BusDisconnect();
|
||||
}
|
||||
protected:
|
||||
// IMPLEMENTATION OF -------------- AzToolsFramework::AssetDatabase::AssetDatabaseRequests::Bus::Listener
|
||||
bool GetAssetDatabaseLocation(AZStd::string& location) override
|
||||
{
|
||||
location = m_location;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
private:
|
||||
AZStd::string m_location;
|
||||
AZStd::string m_assetPath;
|
||||
};
|
||||
|
||||
// Adds a scan folder to the config and to the database
|
||||
void AddScanFolder(const ScanFolderInfo& scanFolderInfo, PlatformConfiguration& config, AssetDatabaseConnection* dbConn)
|
||||
{
|
||||
config.AddScanFolder(scanFolderInfo);
|
||||
ScanFolderDatabaseEntry newScanFolder(
|
||||
scanFolderInfo.ScanPath().toStdString().c_str(),
|
||||
scanFolderInfo.GetDisplayName().toStdString().c_str(),
|
||||
scanFolderInfo.GetPortableKey().toStdString().c_str(),
|
||||
scanFolderInfo.GetOutputPrefix().toStdString().c_str(),
|
||||
scanFolderInfo.IsRoot());
|
||||
dbConn->SetScanFolder(newScanFolder);
|
||||
}
|
||||
|
||||
void BuildConfig(const QDir& tempPath, AssetDatabaseConnection* dbConn, PlatformConfiguration& config)
|
||||
{
|
||||
config.EnablePlatform({ "pc" ,{ "desktop", "renderer" } }, true);
|
||||
config.EnablePlatform({ "es3" ,{ "mobile", "renderer" } }, true);
|
||||
config.EnablePlatform({ "fandango" ,{ "console", "renderer" } }, false);
|
||||
AZStd::vector<AssetBuilderSDK::PlatformInfo> platforms;
|
||||
config.PopulatePlatformsForScanFolder(platforms);
|
||||
// PATH DisplayName PortKey outputfolder root recurse platforms order
|
||||
AddScanFolder(ScanFolderInfo(tempPath.filePath("subfolder4"), "subfolder4", "subfolder4", "", false, false, platforms, -6), config, dbConn); // subfolder 4 overrides subfolder3
|
||||
AddScanFolder(ScanFolderInfo(tempPath.filePath("subfolder3"), "subfolder3", "subfolder3", "", false, false, platforms, -5), config, dbConn); // subfolder 3 overrides subfolder2
|
||||
AddScanFolder(ScanFolderInfo(tempPath.filePath("subfolder2"), "subfolder2", "subfolder2", "", false, true, platforms, -2), config, dbConn); // subfolder 2 overrides subfolder1
|
||||
AddScanFolder(ScanFolderInfo(tempPath.filePath("subfolder1"), "subfolder1", "subfolder1", "editor", false, true, platforms, -1), config, dbConn); // subfolder1 overrides root
|
||||
AddScanFolder(ScanFolderInfo(tempPath.absolutePath(), "temp", "tempfolder", "", true, false, platforms, 0), config, dbConn); // add the root
|
||||
|
||||
config.AddMetaDataType("exportsettings", QString());
|
||||
|
||||
AZ::Uuid buildIDRcLegacy;
|
||||
BUILDER_ID_RC.GetUuid(buildIDRcLegacy);
|
||||
|
||||
AssetRecognizer rec;
|
||||
AssetPlatformSpec specpc;
|
||||
AssetPlatformSpec speces3;
|
||||
|
||||
speces3.m_extraRCParams = "somerandomparam";
|
||||
rec.m_name = "random files";
|
||||
rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.random", AssetBuilderSDK::AssetBuilderPattern::Wildcard);
|
||||
rec.m_platformSpecs.insert("pc", specpc);
|
||||
config.AddRecognizer(rec);
|
||||
|
||||
specpc.m_extraRCParams = ""; // blank must work
|
||||
speces3.m_extraRCParams = "testextraparams";
|
||||
|
||||
const char* builderTxt1Name = "txt files";
|
||||
rec.m_name = builderTxt1Name;
|
||||
rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.txt", AssetBuilderSDK::AssetBuilderPattern::Wildcard);
|
||||
rec.m_platformSpecs.insert("pc", specpc);
|
||||
rec.m_platformSpecs.insert("es3", speces3);
|
||||
|
||||
config.AddRecognizer(rec);
|
||||
|
||||
// Ignore recognizer
|
||||
AssetPlatformSpec ignore_spec;
|
||||
ignore_spec.m_extraRCParams = "skip";
|
||||
AssetRecognizer ignore_rec;
|
||||
ignore_rec.m_name = "ignore files";
|
||||
ignore_rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.ignore", AssetBuilderSDK::AssetBuilderPattern::Wildcard);
|
||||
ignore_rec.m_platformSpecs.insert("pc", specpc);
|
||||
ignore_rec.m_platformSpecs.insert("es3", ignore_spec);
|
||||
config.AddRecognizer(ignore_rec);
|
||||
|
||||
ExcludeAssetRecognizer excludeRecogniser;
|
||||
excludeRecogniser.m_name = "backup";
|
||||
excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(".*\\/savebackup\\/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
|
||||
config.AddExcludeRecognizer(excludeRecogniser);
|
||||
}
|
||||
|
||||
// Adds a source file and job entry to the database, jobId is output
|
||||
bool AddSourceAndJob(const char* scanFolder, const char* sourceRelPath, AssetDatabaseConnection* dbConn, AZ::s64& jobId, AZ::Uuid assetId = AZ::Uuid::CreateRandom())
|
||||
{
|
||||
ScanFolderDatabaseEntry scanFolderEntry;
|
||||
bool result = dbConn->GetScanFolderByPortableKey(scanFolder, scanFolderEntry);
|
||||
|
||||
if(!result)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SourceDatabaseEntry sourceEntry(scanFolderEntry.m_scanFolderID, sourceRelPath, assetId, "fingerprint");
|
||||
dbConn->SetSource(sourceEntry);
|
||||
|
||||
JobDatabaseEntry jobEntry(sourceEntry.m_sourceID, "test", 1234, "pc", assetId, AzToolsFramework::AssetSystem::JobStatus::Completed, 12345);
|
||||
dbConn->SetJob(jobEntry);
|
||||
|
||||
jobId = jobEntry.m_jobID;
|
||||
return true;
|
||||
};
|
||||
|
||||
// Calls the GetRelativeProductPathFromFullSourceOrProductPath function and checks the return results, returning true if it matches both of the expected results
|
||||
bool TestGetRelativeProductPath(const QString fileToCheck, bool expectedToFind, AZStd::initializer_list<const char*> expectedPaths)
|
||||
{
|
||||
bool relPathfound = false;
|
||||
AZStd::string relPath;
|
||||
AZStd::string fullPath(fileToCheck.toStdString().c_str());
|
||||
|
||||
EBUS_EVENT_RESULT(relPathfound, AzToolsFramework::AssetSystemRequestBus, GetRelativeProductPathFromFullSourceOrProductPath, fullPath, relPath);
|
||||
|
||||
if (relPathfound != expectedToFind)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& path : expectedPaths)
|
||||
{
|
||||
if (relPath == path)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calls the GetFullSourcePathFromRelativeProductPath function and checks the return results, returning true if it matches both of the expected results
|
||||
bool TestGetFullSourcePath(const QString& fileToCheck, const QDir& tempPath, bool expectToFind, const char* expectedPath)
|
||||
{
|
||||
bool fullPathfound = false;
|
||||
AZStd::string fullPath;
|
||||
AZStd::string relPath(fileToCheck.toStdString().c_str());
|
||||
|
||||
EBUS_EVENT_RESULT(fullPathfound, AzToolsFramework::AssetSystemRequestBus, GetFullSourcePathFromRelativeProductPath, relPath, fullPath);
|
||||
|
||||
if (fullPathfound != expectToFind)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QString output(fullPath.c_str());
|
||||
output.remove(0, tempPath.path().length() + 1); //adding one for the native separator
|
||||
|
||||
return (output == expectedPath);
|
||||
}
|
||||
|
||||
} // end anon namespace
|
||||
|
||||
void AssetCatalogUnitTests::StartTest()
|
||||
{
|
||||
QDir oldRoot;
|
||||
AssetUtilities::ComputeAssetRoot(oldRoot);
|
||||
AssetUtilities::ResetAssetRoot();
|
||||
|
||||
// the canonicalization of the path here is to get around the fact that on some platforms
|
||||
// the "temporary" folder location could be junctioned into some other folder and getting "QDir::current()"
|
||||
// and other similar functions may actually return a different string but still be referring to the same folder
|
||||
QTemporaryDir dir;
|
||||
QDir tempPath(dir.path());
|
||||
QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
|
||||
UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
|
||||
tempPath = QDir(canonicalTempDirPath);
|
||||
NetworkRequestID requestId(1, 1);
|
||||
|
||||
FakeDatabaseLocationListener listener(tempPath.filePath("statedatabase.sqlite").toUtf8().constData(), "displayString");
|
||||
|
||||
AZStd::unique_ptr<AssetDatabaseConnection> dbConn = AZStd::make_unique<AssetDatabaseConnection>();
|
||||
dbConn->OpenDatabase();
|
||||
|
||||
CreateDummyFile(tempPath.absoluteFilePath("bootstrap.cfg"), QString("sys_game_folder=SamplesProject\n"));
|
||||
|
||||
// system is already actually initialized, along with gEnv, so this will always return that game name.
|
||||
QString gameName = AssetUtilities::ComputeGameName();
|
||||
|
||||
// update the engine root
|
||||
AssetUtilities::ResetAssetRoot();
|
||||
QDir newRoot;
|
||||
AssetUtilities::ComputeAssetRoot(newRoot, &tempPath);
|
||||
|
||||
UNIT_TEST_EXPECT_FALSE(gameName.isEmpty());
|
||||
// should create cache folder in the root, and read everything from there.
|
||||
|
||||
QSet<QString> expectedFiles;
|
||||
// set up some interesting files:
|
||||
expectedFiles << tempPath.absoluteFilePath("rootfile2.txt");
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder1/rootfile1.txt"); // note: Must override the actual root file
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder1/basefile.txt");
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder2/basefile.txt");
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder2/aaa/basefile.txt");
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder2/aaa/bbb/basefile.txt");
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder2/aaa/bbb/ccc/basefile.txt");
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder2/aaa/bbb/ccc/ddd/basefile.txt");
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder3/BaseFile.txt"); // note the case upper here
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder8/a/b/c/test.txt");
|
||||
|
||||
// subfolder3 is not recursive so none of these should show up in any scan or override check
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder3/aaa/basefile.txt");
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder3/aaa/bbb/basefile.txt");
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder3/aaa/bbb/ccc/basefile.txt");
|
||||
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder3/uniquefile.txt"); // only exists in subfolder3
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder3/uniquefile.ignore"); // only exists in subfolder3
|
||||
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder3/rootfile3.txt"); // must override rootfile3 in root
|
||||
expectedFiles << tempPath.absoluteFilePath("rootfile1.txt");
|
||||
expectedFiles << tempPath.absoluteFilePath("rootfile3.txt");
|
||||
expectedFiles << tempPath.absoluteFilePath("unrecognised.file"); // a file that should not be recognised
|
||||
expectedFiles << tempPath.absoluteFilePath("unrecognised2.file"); // a file that should not be recognised
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder1/test/test.format"); // a file that should be recognised
|
||||
expectedFiles << tempPath.absoluteFilePath("test.format"); // a file that should NOT be recognised
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder3/somefile.xxx");
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder3/savebackup/test.txt");//file that should be excluded
|
||||
expectedFiles << tempPath.absoluteFilePath("subfolder3/somerandomfile.random");
|
||||
|
||||
for (const QString& expect : expectedFiles)
|
||||
{
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(expect));
|
||||
AZ_TracePrintf(AssetProcessor::DebugChannel, "Created file %s with msecs %llu\n", expect.toUtf8().constData(),
|
||||
QFileInfo(expect).lastModified().toMSecsSinceEpoch());
|
||||
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
QThread::msleep(35); // give at least some milliseconds so that the files never share the same timestamp exactly
|
||||
#else
|
||||
// on platforms such as mac, the file time resolution is only a second :(
|
||||
QThread::msleep(1001);
|
||||
#endif
|
||||
}
|
||||
|
||||
PlatformConfiguration config;
|
||||
BuildConfig(tempPath, dbConn.get(), config);
|
||||
|
||||
AssetCatalog assetCatalog(nullptr, &config);
|
||||
QDir cacheRoot;
|
||||
UNIT_TEST_EXPECT_TRUE(AssetUtilities::ComputeProjectCacheRoot(cacheRoot));
|
||||
QString normalizedCacheRoot = AssetUtilities::NormalizeDirectoryPath(cacheRoot.absolutePath());
|
||||
|
||||
// make sure it picked up the one in the current folder
|
||||
|
||||
QString normalizedDirPathCheck = AssetUtilities::NormalizeDirectoryPath(tempPath.absoluteFilePath("Cache/" + gameName));
|
||||
UNIT_TEST_EXPECT_TRUE(normalizedCacheRoot == normalizedDirPathCheck);
|
||||
QDir normalizedCacheRootDir(normalizedCacheRoot);
|
||||
|
||||
// ----- Test the get asset path functions, which given a full path to an asset, checks the mappings and turns it into an Asset ID ---
|
||||
{
|
||||
// sanity check - make sure it does not crash or misbehave when given empty names
|
||||
QString fileToCheck = "";
|
||||
{
|
||||
UnitTestUtils::AssertAbsorber absorb;
|
||||
// empty requests should generate an assert.
|
||||
GetRelativeProductPathFromFullSourceOrProductPathRequest request(fileToCheck.toUtf8().constData());
|
||||
UNIT_TEST_EXPECT_TRUE(absorb.m_numAssertsAbsorbed == 1);
|
||||
GetFullSourcePathFromRelativeProductPathRequest sourceRequest(fileToCheck.toUtf8().constData());
|
||||
UNIT_TEST_EXPECT_TRUE(absorb.m_numAssertsAbsorbed == 2);
|
||||
}
|
||||
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetRelativeProductPath("", false, {""}));
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetFullSourcePath("", tempPath, false, ""));
|
||||
|
||||
// Add a source file with 4 products
|
||||
{
|
||||
AZ::s64 jobId;
|
||||
bool result = AddSourceAndJob("subfolder3", "BaseFile.txt", dbConn.get(), jobId);
|
||||
UNIT_TEST_EXPECT_TRUE(result);
|
||||
|
||||
AZ::u32 productSubId = 0;
|
||||
for (auto& relativeProductPath : { "subfolder3/basefilez.arc2", "subfolder3/basefileaz.azm2", "subfolder3/basefile.arc2", "subfolder3/basefile.azm2" })
|
||||
{
|
||||
ProductDatabaseEntry newProduct(jobId, productSubId++, cacheRoot.relativeFilePath(relativeProductPath).toStdString().c_str(), AZ::Data::AssetType::CreateRandom());
|
||||
dbConn->SetProduct(newProduct);
|
||||
}
|
||||
}
|
||||
|
||||
// GetRelativeProductPathFromFullSourceOrProductPath has 4 code paths:
|
||||
// 1) Relative input paths are returned straight away
|
||||
// 2) Paths inside the cache folder are transformed to product paths, no database involvement
|
||||
// 3) Source files that have a product return the product path
|
||||
// 4) Source files that don't have a product return the source file's relative path since that is the path a product might have
|
||||
|
||||
// Failure case
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
fileToCheck = "d:\\test.txt";
|
||||
#else
|
||||
fileToCheck = "/test.txt"; // rooted
|
||||
#endif
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetRelativeProductPath(fileToCheck, false, { fileToCheck.toStdString().c_str() }));
|
||||
|
||||
// (Case 1) feed it a relative path
|
||||
fileToCheck = "\test.txt";
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "\test.txt" }));
|
||||
|
||||
// (Case 2) feed it a product path with gamename
|
||||
fileToCheck = normalizedCacheRootDir.filePath("pc/" + gameName + "/aaa/basefile.txt");
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "aaa/basefile.txt" }));
|
||||
|
||||
// (Case 2) feed it a product path without gamename
|
||||
fileToCheck = normalizedCacheRootDir.filePath("pc/basefile.txt");
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "basefile.txt" }));
|
||||
|
||||
// (Case 2) feed it a product path with gamename but poor casing (test 1: the pc platform is not matching case)
|
||||
fileToCheck = normalizedCacheRootDir.filePath("Pc/" + gameName + "/aaa/basefile.txt");
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "aaa/basefile.txt" }));
|
||||
|
||||
// (Case 2) feed it a product path with gamename but poor casing (test 2: the gameName is not matching case)
|
||||
fileToCheck = normalizedCacheRootDir.filePath("pc/" + gameName.toUpper() + "/aaa/basefile.txt");
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "aaa/basefile.txt" }));
|
||||
|
||||
// (Case 2) feed it a product path that resolves to a directory name instead of a file.
|
||||
fileToCheck = normalizedCacheRootDir.filePath("pc/" + gameName.toUpper() + "/aaa");
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "aaa" }));
|
||||
|
||||
// Case 3
|
||||
fileToCheck = tempPath.absoluteFilePath("subfolder3/BaseFile.txt");
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "basefilez.arc2", "basefileaz.azm2", "basefile.arc2", "basefile.azm2" }));
|
||||
|
||||
// Case 4
|
||||
fileToCheck = tempPath.absoluteFilePath("subfolder2/aaa/basefile.txt");
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "aaa/basefile.txt" }));
|
||||
|
||||
// ----- Test the ProcessGetFullAssetPath function
|
||||
{
|
||||
QStringList pcouts;
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/") + gameName + "/subfolder3/randomfileoutput.random"));
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/") + gameName + "/subfolder3/randomfileoutput.random1"));
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/") + gameName + "/subfolder3/randomfileoutput.random2"));
|
||||
|
||||
AZ::s64 jobId;
|
||||
bool result = AddSourceAndJob("subfolder3", "somerandomfile.random", dbConn.get(), jobId);
|
||||
UNIT_TEST_EXPECT_TRUE(result);
|
||||
|
||||
AZ::u32 productSubID = 0;
|
||||
for (auto& product : pcouts)
|
||||
{
|
||||
ProductDatabaseEntry newProduct(jobId, productSubID++, cacheRoot.relativeFilePath(product).toStdString().c_str(), AZ::Data::AssetType::CreateRandom());
|
||||
dbConn->SetProduct(newProduct);
|
||||
}
|
||||
}
|
||||
|
||||
//feed it an relative product, and expect a full, absolute source file path in return.
|
||||
fileToCheck = "subfolder3/randomfileoutput.random1";
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetFullSourcePath(fileToCheck, tempPath, true, "subfolder3/somerandomfile.random"));
|
||||
|
||||
//feed it another relative product
|
||||
fileToCheck = "subfolder3/randomfileoutput.random2";
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetFullSourcePath(fileToCheck, tempPath, true, "subfolder3/somerandomfile.random"));
|
||||
|
||||
//feed it the same relative product with different separators
|
||||
fileToCheck = "subfolder3\\randomfileoutput.random2";
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetFullSourcePath(fileToCheck, tempPath, true, "subfolder3/somerandomfile.random"));
|
||||
|
||||
//feed it a full path
|
||||
fileToCheck = tempPath.filePath("somefolder/somefile.txt");
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetFullSourcePath(fileToCheck, tempPath, true, "somefolder/somefile.txt"));
|
||||
|
||||
//feed it a path with alias and asset id
|
||||
fileToCheck = "@assets@/subfolder3/randomfileoutput.random1";
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetFullSourcePath(fileToCheck, tempPath, true, "subfolder3/somerandomfile.random"));
|
||||
|
||||
//feed it a path with some random alias and asset id
|
||||
fileToCheck = "@somerandomalias@/subfolder3/randomfileoutput.random1";
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetFullSourcePath(fileToCheck, tempPath, true, "subfolder3/somerandomfile.random"));
|
||||
|
||||
//feed it a path with some random alias and asset id but no separator
|
||||
fileToCheck = "@somerandomalias@subfolder3/randomfileoutput.random1";
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetFullSourcePath(fileToCheck, tempPath, true, "subfolder3/somerandomfile.random"));
|
||||
|
||||
//feed it a path with alias and input name
|
||||
fileToCheck = "@assets@/somerandomfile.random";
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetFullSourcePath(fileToCheck, tempPath, true, "subfolder3/somerandomfile.random"));
|
||||
|
||||
//feed it an absolute path with cacheroot
|
||||
fileToCheck = normalizedCacheRootDir.filePath("pc/" + gameName + "/subfolder3/randomfileoutput.random1");
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetFullSourcePath(fileToCheck, tempPath, true, "subfolder3/somerandomfile.random"));
|
||||
|
||||
//feed it a productName directly
|
||||
fileToCheck = "pc/" + gameName + "/subfolder3/randomfileoutput.random1";
|
||||
UNIT_TEST_EXPECT_TRUE(TestGetFullSourcePath(fileToCheck, tempPath, true, "subfolder3/somerandomfile.random"));
|
||||
}
|
||||
|
||||
Q_EMIT UnitTestPassed();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void AssetCatalogUnitTests_AssetInfo::StartTest()
|
||||
{
|
||||
using namespace AZ::Data;
|
||||
using namespace AzToolsFramework;
|
||||
|
||||
// the canonicalization of the path here is to get around the fact that on some platforms
|
||||
// the "temporary" folder location could be junctioned into some other folder and getting "QDir::current()"
|
||||
// and other similar functions may actually return a different string but still be referring to the same folder
|
||||
QTemporaryDir dir;
|
||||
QDir tempPath(dir.path());
|
||||
QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
|
||||
UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
|
||||
tempPath = QDir(canonicalTempDirPath);
|
||||
|
||||
CreateDummyFile(tempPath.absoluteFilePath("bootstrap.cfg"), QString("sys_game_folder=SamplesProject\n"));
|
||||
|
||||
// system is already actually initialized, along with gEnv, so this will always return that game name.
|
||||
QString gameName = AssetUtilities::ComputeGameName();
|
||||
|
||||
// update the engine root
|
||||
AssetUtilities::ResetAssetRoot();
|
||||
QDir newRoot;
|
||||
AssetUtilities::ComputeAssetRoot(newRoot, &tempPath);
|
||||
QDir cacheRoot;
|
||||
AssetUtilities::ComputeProjectCacheRoot(cacheRoot);
|
||||
AZStd::string cacheRootPath = cacheRoot.absolutePath().toStdString().c_str();
|
||||
|
||||
FakeDatabaseLocationListener listener(tempPath.filePath("statedatabase.sqlite").toUtf8().constData(), "displayString");
|
||||
|
||||
AZStd::unique_ptr<AssetDatabaseConnection> dbConn = AZStd::make_unique<AssetDatabaseConnection>();
|
||||
dbConn->OpenDatabase();
|
||||
|
||||
PlatformConfiguration config;
|
||||
BuildConfig(tempPath, dbConn.get(), config);
|
||||
|
||||
AssetCatalog assetCatalog(nullptr, &config);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AssetId assetA(AZ::Uuid::CreateRandom(), 0);
|
||||
AZ::Uuid assetALegacyUuid = AZ::Uuid::CreateRandom();
|
||||
AssetType assetAType = AssetType::CreateRandom();
|
||||
AZStd::string assetAFileFilter = "*.source";
|
||||
AZStd::string subfolder1AbsolutePath = tempPath.absoluteFilePath("subfolder1").toStdString().c_str();
|
||||
AZStd::string assetASourceRelPath = "assetA.source";
|
||||
AZStd::string assetASourceDatabasePath = "editor/assetA.source";
|
||||
AZStd::string assetAProductRelPath = "editor/assetA.product";
|
||||
|
||||
AZStd::string assetAFullPath;
|
||||
AzFramework::StringFunc::Path::Join(subfolder1AbsolutePath.c_str(), assetASourceRelPath.c_str(), assetAFullPath);
|
||||
CreateDummyFile(QString::fromUtf8(assetAFullPath.c_str()), "Its the Asset A"); // 15 bytes of data
|
||||
|
||||
AZStd::string assetAProductFullPath;
|
||||
AzFramework::StringFunc::Path::Join(cacheRootPath.c_str(), assetAProductRelPath.c_str(), assetAProductFullPath);
|
||||
CreateDummyFile(QString::fromUtf8(assetAProductFullPath.c_str()), "Its a product A"); // 15 bytes of data
|
||||
|
||||
auto getAssetInfoById = [assetA, assetAType, subfolder1AbsolutePath](bool expectedResult, AZStd::string expectedRelPath, AZStd::string expectedRootPath, AssetType assetType) -> bool
|
||||
{
|
||||
bool result = false;
|
||||
AssetInfo assetInfo;
|
||||
AZStd::string rootPath;
|
||||
AssetSystemRequestBus::BroadcastResult(result, &AssetSystem::AssetSystemRequest::GetAssetInfoById, assetA, assetType, assetInfo, rootPath);
|
||||
|
||||
if (result != expectedResult)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expectedResult)
|
||||
{
|
||||
return (assetInfo.m_assetId == assetA)
|
||||
&& (assetInfo.m_assetType == assetAType)
|
||||
&& (assetInfo.m_relativePath == expectedRelPath)
|
||||
&& (assetInfo.m_sizeBytes == 15)
|
||||
&& (rootPath == expectedRootPath);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
auto getAssetInfoByIdPair = [&](bool expectedResult, AZStd::string expectedRelPath, AZStd::string expectedRootPath) -> bool
|
||||
{
|
||||
// First test without providing the assetType
|
||||
bool result = getAssetInfoById(expectedResult, expectedRelPath, expectedRootPath, AssetType::CreateNull());
|
||||
|
||||
// If successful, test again, this time providing the assetType
|
||||
if (result)
|
||||
{
|
||||
result = getAssetInfoById(expectedResult, expectedRelPath, expectedRootPath, assetAType);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
auto getSourceInfoBySourcePath = [](bool expectedResult, AZStd::string sourcePath, AZ::Uuid expectedUuid, AZStd::string expectedRelPath, AZStd::string expectedRootPath, AZ::Data::AssetType expectedType = AZ::Data::s_invalidAssetType) -> bool
|
||||
{
|
||||
bool result = false;
|
||||
AssetInfo assetInfo;
|
||||
AZStd::string rootPath;
|
||||
AssetSystemRequestBus::BroadcastResult(result, &AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, sourcePath.c_str(), assetInfo, rootPath);
|
||||
|
||||
if (result != expectedResult)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expectedResult)
|
||||
{
|
||||
return (assetInfo.m_assetId == expectedUuid)
|
||||
&& (assetInfo.m_assetType == expectedType)
|
||||
&& (assetInfo.m_relativePath == expectedRelPath)
|
||||
&& (assetInfo.m_sizeBytes == 15)
|
||||
&& (rootPath == expectedRootPath)
|
||||
;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
//Test 1: Asset not in database
|
||||
UNIT_TEST_EXPECT_TRUE(getAssetInfoByIdPair(false, "", ""));
|
||||
UNIT_TEST_EXPECT_TRUE(getSourceInfoBySourcePath(false, "", AZ::Uuid::CreateNull(), "", ""));
|
||||
|
||||
// Add asset to database
|
||||
AZ::s64 jobId;
|
||||
UNIT_TEST_EXPECT_TRUE(AddSourceAndJob("subfolder1", assetASourceDatabasePath.c_str(), dbConn.get(), jobId, assetA.m_guid));
|
||||
ProductDatabaseEntry newProductEntry(jobId, 0, assetAProductRelPath.c_str(), assetAType);
|
||||
dbConn->SetProduct(newProductEntry);
|
||||
|
||||
// Test 2: Asset in database, not registered as source asset
|
||||
// note that when asking for products, a performance improvement causes the catalog to use its REGISTRY
|
||||
// rather than the database to ask for products, so to set this up the registry must be present and must have the asset registered within it
|
||||
AzFramework::AssetSystem::AssetNotificationMessage message(assetAProductRelPath.c_str(), AssetNotificationMessage::AssetChanged, assetAType);
|
||||
message.m_sizeBytes = 15;
|
||||
message.m_assetId = AZ::Data::AssetId(assetA.m_guid, 0);
|
||||
assetCatalog.OnAssetMessage("pc", message);
|
||||
|
||||
// also of note: When looking up products, you don't get a root path since they are all in the cache.
|
||||
// its important here that we specifically get an empty root path.
|
||||
UNIT_TEST_EXPECT_TRUE(getAssetInfoByIdPair(true, assetAProductRelPath, ""));
|
||||
|
||||
// this call has to work with full and relative path.
|
||||
UNIT_TEST_EXPECT_TRUE(getSourceInfoBySourcePath(true, assetASourceRelPath.c_str(), assetA.m_guid, assetASourceRelPath.c_str(), subfolder1AbsolutePath.c_str()));
|
||||
UNIT_TEST_EXPECT_TRUE(getSourceInfoBySourcePath(true, assetAFullPath.c_str(), assetA.m_guid, assetASourceRelPath.c_str(), subfolder1AbsolutePath.c_str()));
|
||||
|
||||
dbConn->RemoveProductsByJobID(jobId);
|
||||
|
||||
// similar to the above, because its not the DB that is used for products, we have to inform the catalog that its gone
|
||||
message.m_type = AssetNotificationMessage::AssetRemoved;
|
||||
assetCatalog.OnAssetMessage("pc", message);
|
||||
|
||||
// Add to queue
|
||||
assetCatalog.OnSourceQueued(assetA.m_guid, assetALegacyUuid, subfolder1AbsolutePath.c_str(), assetASourceRelPath.c_str());
|
||||
|
||||
//Test 3: Asset in queue, not registered as source asset
|
||||
UNIT_TEST_EXPECT_TRUE(getAssetInfoByIdPair(false, "", ""));
|
||||
|
||||
// this call should STILL work even after the above call to "OnSourceQueued" since its explicitly asking for the source details.
|
||||
UNIT_TEST_EXPECT_TRUE(getSourceInfoBySourcePath(true, assetASourceRelPath.c_str(), assetA.m_guid, assetASourceRelPath.c_str(), subfolder1AbsolutePath.c_str()));
|
||||
UNIT_TEST_EXPECT_TRUE(getSourceInfoBySourcePath(true, assetAFullPath.c_str(), assetA.m_guid, assetASourceRelPath.c_str(), subfolder1AbsolutePath.c_str()));
|
||||
|
||||
// Register as source type
|
||||
// note that once this call has been made, ALL REQUESTS for this type of asset should always include an appropriate type (non zero)
|
||||
ToolsAssetSystemBus::Broadcast(&ToolsAssetSystemRequests::RegisterSourceAssetType, assetAType, assetAFileFilter.c_str());
|
||||
|
||||
//Test 4: Asset in queue, registered as source asset
|
||||
UNIT_TEST_EXPECT_TRUE(getAssetInfoByIdPair(true, assetASourceRelPath, subfolder1AbsolutePath));
|
||||
|
||||
// these calls are identical to the two above, but should continue to work even though we have registered the asset type as a source asset type.
|
||||
UNIT_TEST_EXPECT_TRUE(getSourceInfoBySourcePath(true, assetASourceRelPath.c_str(), assetA.m_guid, assetASourceRelPath.c_str(), subfolder1AbsolutePath.c_str(), assetAType));
|
||||
UNIT_TEST_EXPECT_TRUE(getSourceInfoBySourcePath(true, assetAFullPath.c_str(), assetA.m_guid, assetASourceRelPath.c_str(), subfolder1AbsolutePath.c_str(), assetAType));
|
||||
|
||||
// Remove from queue
|
||||
assetCatalog.OnSourceFinished(assetA.m_guid, assetALegacyUuid);
|
||||
|
||||
// Add asset to database
|
||||
ProductDatabaseEntry assetAEntry(jobId, 0, assetAProductRelPath.c_str(), assetAType);
|
||||
dbConn->SetProduct(assetAEntry);
|
||||
|
||||
//Test 5: Asset in database, registered as source asset
|
||||
UNIT_TEST_EXPECT_TRUE(getAssetInfoByIdPair(true, assetASourceRelPath, subfolder1AbsolutePath));
|
||||
|
||||
// at this point the details about the asset in question is no longer in memory, only the database. However, these calls should continue find the
|
||||
// information, because the system is supposed check both the database AND the in-memory queue in the to find the info being requested.
|
||||
UNIT_TEST_EXPECT_TRUE(getSourceInfoBySourcePath(true, assetASourceRelPath.c_str(), assetA.m_guid, assetASourceRelPath.c_str(), subfolder1AbsolutePath.c_str(), assetAType));
|
||||
UNIT_TEST_EXPECT_TRUE(getSourceInfoBySourcePath(true, assetAFullPath.c_str(), assetA.m_guid, assetASourceRelPath.c_str(), subfolder1AbsolutePath.c_str(), assetAType));
|
||||
|
||||
Q_EMIT UnitTestPassed();
|
||||
}
|
||||
|
||||
#include <native/unittests/moc_AssetCatalogUnitTests.cpp>
|
||||
} // namespace AssetProcessor
|
||||
|
||||
#endif
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "AssetProcessorManagerUnitTests.h"
|
||||
|
||||
#include <AzCore/Casting/lossy_cast.h>
|
||||
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
|
||||
#include "MockApplicationManager.h"
|
||||
#include "native/FileWatcher/FileWatcher.h"
|
||||
@@ -56,7 +56,7 @@ namespace AssetProcessor
|
||||
using GetFullSourcePathFromRelativeProductPathResponse = AzFramework::AssetSystem::GetFullSourcePathFromRelativeProductPathResponse;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
REGISTER_UNIT_TEST(AssetProcessorManagerUnitTests_ScanFolders)
|
||||
|
||||
@@ -138,7 +138,7 @@ namespace AssetProcessor
|
||||
AssetUtilities::ComputeAssetRoot(oldRoot);
|
||||
AssetUtilities::ResetAssetRoot();
|
||||
FileWatcher fileWatcher;
|
||||
|
||||
|
||||
UNIT_TEST_EXPECT_TRUE(QDir::temp().exists());
|
||||
QString tmpDirPath = AssetUtilities::NormalizeDirectoryPath(QDir::tempPath());
|
||||
|
||||
@@ -153,8 +153,7 @@ namespace AssetProcessor
|
||||
UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
|
||||
NetworkRequestID requestId(1, 1);
|
||||
|
||||
// system is already actually initialized, along with gEnv, so this will always return that game name.
|
||||
QString gameName = AssetUtilities::ComputeGameName(AssetProcessorManagerTestGameProject);
|
||||
QString gameName = AssetUtilities::ComputeProjectName(AssetProcessorManagerTestGameProject);
|
||||
|
||||
// update the engine root
|
||||
AssetUtilities::ResetAssetRoot();
|
||||
@@ -162,6 +161,13 @@ namespace AssetProcessor
|
||||
AssetUtilities::ComputeAssetRoot(newRoot, &tempPath);
|
||||
|
||||
// create a dummy file in the cache folder, so the folder structure gets created
|
||||
// Override the cache folder to be the within the temporary directory
|
||||
auto projectCacheRootKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/project_cache_path", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey);
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
settingsRegistry->Set(projectCacheRootKey, tempPath.absoluteFilePath("Cache").toUtf8().data());
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry);
|
||||
}
|
||||
QDir projectCacheRoot;
|
||||
AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot);
|
||||
CreateDummyFile(projectCacheRoot.absoluteFilePath("placeholder.txt"));
|
||||
@@ -343,7 +349,7 @@ namespace AssetProcessor
|
||||
|
||||
// make sure it picked up the one in the cache and not for example the real working folder
|
||||
|
||||
QString normalizedDirPathCheck = AssetUtilities::NormalizeDirectoryPath(QDir(canonicalTempDirPath).absoluteFilePath("Cache/" + gameName));
|
||||
QString normalizedDirPathCheck = AssetUtilities::NormalizeDirectoryPath(QDir(canonicalTempDirPath).absoluteFilePath("Cache"));
|
||||
UNIT_TEST_EXPECT_TRUE(normalizedCacheRoot == normalizedDirPathCheck);
|
||||
QDir normalizedCacheRootDir(normalizedCacheRoot);
|
||||
|
||||
@@ -441,7 +447,7 @@ namespace AssetProcessor
|
||||
QString relativePathFromWatchFolder = "uniquefile.txt";
|
||||
QString watchFolderPath = tempPath.absoluteFilePath("subfolder3");
|
||||
QString absolutePath = AssetUtilities::NormalizeFilePath(watchFolderPath + "/" + relativePathFromWatchFolder);
|
||||
|
||||
|
||||
QMetaObject::invokeMethod(&apm, "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, absolutePath));
|
||||
|
||||
UNIT_TEST_EXPECT_TRUE(BlockUntil(idling, 5000));
|
||||
@@ -469,7 +475,7 @@ namespace AssetProcessor
|
||||
UNIT_TEST_EXPECT_TRUE(processResults[checkIdx].m_jobEntry.m_watchFolderPath == AssetUtilities::NormalizeFilePath(watchFolderPath));
|
||||
UNIT_TEST_EXPECT_TRUE(processResults[checkIdx].m_jobEntry.m_pathRelativeToWatchFolder == "uniquefile.txt");
|
||||
UNIT_TEST_EXPECT_TRUE(processResults[checkIdx].m_jobEntry.m_databaseSourceName == "uniquefile.txt");
|
||||
QString platformFolder = cacheRoot.filePath(QString::fromUtf8(processResults[checkIdx].m_jobEntry.m_platformInfo.m_identifier.c_str()) + "/" + gameName.toLower());
|
||||
QString platformFolder = cacheRoot.filePath(QString::fromUtf8(processResults[checkIdx].m_jobEntry.m_platformInfo.m_identifier.c_str()));
|
||||
platformFolder = AssetUtilities::NormalizeDirectoryPath(platformFolder);
|
||||
UNIT_TEST_EXPECT_TRUE(processResults[checkIdx].m_destinationPath.startsWith(platformFolder));
|
||||
UNIT_TEST_EXPECT_TRUE(processResults[checkIdx].m_jobEntry.m_computedFingerprint != 0);
|
||||
@@ -659,8 +665,8 @@ namespace AssetProcessor
|
||||
|
||||
|
||||
QStringList es3outs;
|
||||
es3outs.push_back(cacheRoot.filePath(QString("es3/") + gameName + "/basefile.arc1"));
|
||||
es3outs.push_back(cacheRoot.filePath(QString("es3/") + gameName + "/basefile.arc2"));
|
||||
es3outs.push_back(cacheRoot.filePath(QString("es3/basefile.arc1")));
|
||||
es3outs.push_back(cacheRoot.filePath(QString("es3/basefile.arc2")));
|
||||
|
||||
// feed it the messages its waiting for (create the files)
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "products."));
|
||||
@@ -790,7 +796,7 @@ namespace AssetProcessor
|
||||
assetMessages.clear();
|
||||
|
||||
es3outs.clear();
|
||||
es3outs.push_back(cacheRoot.filePath(QString("es3/") + gameName + "/basefile.azm"));
|
||||
es3outs.push_back(cacheRoot.filePath(QString("es3/basefile.azm")));
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "products."));
|
||||
|
||||
//Invoke Asset Processed for es3 platform , txt files2 job description
|
||||
@@ -815,7 +821,7 @@ namespace AssetProcessor
|
||||
assetMessages.clear();
|
||||
|
||||
QStringList pcouts;
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/") + gameName + "/basefile.arc1"));
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/basefile.arc1")));
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts[0], "products."));
|
||||
|
||||
response.m_outputProducts.clear();
|
||||
@@ -843,7 +849,7 @@ namespace AssetProcessor
|
||||
assetMessages.clear();
|
||||
|
||||
pcouts.clear();
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/") + gameName + "/basefile.azm"));
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/basefile.azm")));
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts[0], "products."));
|
||||
|
||||
response.m_outputProducts.clear();
|
||||
@@ -1012,7 +1018,7 @@ namespace AssetProcessor
|
||||
{
|
||||
QString processFile1 = processResults[checkIdx].m_jobEntry.GetAbsoluteSourcePath();
|
||||
UNIT_TEST_EXPECT_TRUE(AssetUtilities::NormalizeFilePath(processFile1) == AssetUtilities::NormalizeFilePath(absolutePath));
|
||||
QString platformFolder = cacheRoot.filePath(QString::fromUtf8(processResults[checkIdx].m_jobEntry.m_platformInfo.m_identifier.c_str()) + "/" + gameName.toLower());
|
||||
QString platformFolder = cacheRoot.filePath(QString::fromUtf8(processResults[checkIdx].m_jobEntry.m_platformInfo.m_identifier.c_str()));
|
||||
platformFolder = AssetUtilities::NormalizeDirectoryPath(platformFolder);
|
||||
processFile1 = processResults[checkIdx].m_destinationPath;
|
||||
UNIT_TEST_EXPECT_TRUE(processFile1.startsWith(platformFolder));
|
||||
@@ -1029,12 +1035,12 @@ namespace AssetProcessor
|
||||
QStringList pcouts2;
|
||||
es3outs.clear();
|
||||
pcouts.clear();
|
||||
es3outs.push_back(cacheRoot.filePath(QString("es3/") + gameName + "/basefilea.arc1"));
|
||||
es3outs2.push_back(cacheRoot.filePath(QString("es3/") + gameName + "/basefilea.azm"));
|
||||
es3outs.push_back(cacheRoot.filePath(QString("es3/basefilea.arc1")));
|
||||
es3outs2.push_back(cacheRoot.filePath(QString("es3/basefilea.azm")));
|
||||
// note that the ES3 outs have changed
|
||||
// but the pc outs are still the same.
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/") + gameName + "/basefile.arc1"));
|
||||
pcouts2.push_back(cacheRoot.filePath(QString("pc/") + gameName + "/basefile.azm"));
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/basefile.arc1")));
|
||||
pcouts2.push_back(cacheRoot.filePath(QString("pc/basefile.azm")));
|
||||
|
||||
// feed it the messages its waiting for (create the files)
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "newfile."));
|
||||
@@ -1155,7 +1161,7 @@ namespace AssetProcessor
|
||||
{
|
||||
QString processFile1 = processResults[checkIdx].m_jobEntry.GetAbsoluteSourcePath();
|
||||
UNIT_TEST_EXPECT_TRUE(AssetUtilities::NormalizeFilePath(processFile1) == AssetUtilities::NormalizeFilePath(absolutePath));
|
||||
QString platformFolder = cacheRoot.filePath(QString::fromUtf8(processResults[checkIdx].m_jobEntry.m_platformInfo.m_identifier.c_str()) + "/" + gameName.toLower());
|
||||
QString platformFolder = cacheRoot.filePath(QString::fromUtf8(processResults[checkIdx].m_jobEntry.m_platformInfo.m_identifier.c_str()));
|
||||
platformFolder = AssetUtilities::NormalizeDirectoryPath(platformFolder);
|
||||
processFile1 = processResults[checkIdx].m_destinationPath;
|
||||
UNIT_TEST_EXPECT_TRUE(processFile1.startsWith(platformFolder));
|
||||
@@ -1261,7 +1267,7 @@ namespace AssetProcessor
|
||||
// 4 * file claimed for the produce file to be able to update it safely.
|
||||
// 4 * file released for the produce file so it's free for other tools to use it again.
|
||||
UNIT_TEST_EXPECT_TRUE(payloadList.size() == 9);
|
||||
unsigned int messageLoadCount = 0;
|
||||
unsigned int messageLoadCount = 0;
|
||||
for (auto payload : payloadList)
|
||||
{
|
||||
if (payload.first == SourceFileNotificationMessage::MessageType)
|
||||
@@ -1487,9 +1493,9 @@ namespace AssetProcessor
|
||||
|
||||
pcouts.clear();
|
||||
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/") + gameName + "/subfolder3/randomfileoutput.random"));
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/") + gameName + "/subfolder3/randomfileoutput.random1"));
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/") + gameName + "/subfolder3/randomfileoutput.random2"));
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/subfolder3/randomfileoutput.random")));
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/subfolder3/randomfileoutput.random1")));
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/subfolder3/randomfileoutput.random2")));
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts[0], "products."));
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts[1], "products."));
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts[2], "products."));
|
||||
@@ -1535,12 +1541,12 @@ namespace AssetProcessor
|
||||
es3outs2.clear();
|
||||
pcouts.clear();
|
||||
pcouts2.clear();
|
||||
es3outs.push_back(cacheRoot.filePath(QString("es3/") + gameName + "/basefilez.arc2"));
|
||||
es3outs2.push_back(cacheRoot.filePath(QString("es3/") + gameName + "/basefileaz.azm2"));
|
||||
es3outs.push_back(cacheRoot.filePath(QString("es3/basefilez.arc2")));
|
||||
es3outs2.push_back(cacheRoot.filePath(QString("es3/basefileaz.azm2")));
|
||||
// note that the ES3 outs have changed
|
||||
// but the pc outs are still the same.
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/") + gameName + "/basefile.arc2"));
|
||||
pcouts2.push_back(cacheRoot.filePath(QString("pc/") + gameName + "/basefile.azm2"));
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/basefile.arc2")));
|
||||
pcouts2.push_back(cacheRoot.filePath(QString("pc/basefile.azm2")));
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "newfile."));
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts[0], "newfile."));
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs2[0], "newfile."));
|
||||
@@ -1629,7 +1635,7 @@ namespace AssetProcessor
|
||||
{
|
||||
QString processFile1 = processResults[checkIdx].m_jobEntry.GetAbsoluteSourcePath();
|
||||
UNIT_TEST_EXPECT_TRUE(processFile1 == expectedReplacementInputFile);
|
||||
QString platformFolder = cacheRoot.filePath(QString::fromUtf8(processResults[checkIdx].m_jobEntry.m_platformInfo.m_identifier.c_str()) + "/" + gameName.toLower());
|
||||
QString platformFolder = cacheRoot.filePath(QString::fromUtf8(processResults[checkIdx].m_jobEntry.m_platformInfo.m_identifier.c_str()));
|
||||
platformFolder = AssetUtilities::NormalizeDirectoryPath(platformFolder);
|
||||
processFile1 = processResults[checkIdx].m_destinationPath;
|
||||
UNIT_TEST_EXPECT_TRUE(processFile1.startsWith(platformFolder));
|
||||
@@ -1951,13 +1957,13 @@ namespace AssetProcessor
|
||||
}
|
||||
|
||||
UNIT_TEST_EXPECT_TRUE(BlockUntil(idling, 5000));
|
||||
|
||||
|
||||
// it now believes that there are a whole bunch of assets in subfolder1/done_renaming and they resulted in
|
||||
// a whole bunch of files to have been created in the asset cache, listed in processresults, and they exist in outputscreated...
|
||||
// rename the output folder:
|
||||
|
||||
QString originalCacheFolderName = normalizedCacheRootDir.absoluteFilePath("pc") + "/" + gameName.toLower() + "/done_renaming";
|
||||
QString newCacheFolderName = normalizedCacheRootDir.absoluteFilePath("pc") + "/" + gameName.toLower() + "/renamed_again";
|
||||
QString originalCacheFolderName = normalizedCacheRootDir.absoluteFilePath("pc") + "/done_renaming";
|
||||
QString newCacheFolderName = normalizedCacheRootDir.absoluteFilePath("pc") + "/renamed_again";
|
||||
|
||||
UNIT_TEST_EXPECT_TRUE(renamer.rename(originalCacheFolderName, newCacheFolderName));
|
||||
|
||||
@@ -2017,8 +2023,8 @@ namespace AssetProcessor
|
||||
|
||||
// setup complete. now RENAME that folder.
|
||||
|
||||
originalCacheFolderName = normalizedCacheRootDir.absoluteFilePath("pc") + "/" + gameName.toLower() + "/rename_this_secondly";
|
||||
newCacheFolderName = normalizedCacheRootDir.absoluteFilePath("pc") + "/" + gameName.toLower() + "/done_renaming_again";
|
||||
originalCacheFolderName = normalizedCacheRootDir.absoluteFilePath("pc") + "/rename_this_secondly";
|
||||
newCacheFolderName = normalizedCacheRootDir.absoluteFilePath("pc") + "/done_renaming_again";
|
||||
|
||||
UNIT_TEST_EXPECT_TRUE(renamer.rename(originalCacheFolderName, newCacheFolderName));
|
||||
|
||||
@@ -2305,8 +2311,7 @@ namespace AssetProcessor
|
||||
UnitTestUtils::ScopedDir changeDir(dir.path());
|
||||
QDir tempPath(dir.path());
|
||||
|
||||
// system is already actually initialized, along with gEnv, so this will always return that game name.
|
||||
QString gameName = AssetUtilities::ComputeGameName(AssetProcessorManagerTestGameProject);
|
||||
QString gameName = AssetUtilities::ComputeProjectName(AssetProcessorManagerTestGameProject);
|
||||
|
||||
// update the engine root
|
||||
AssetUtilities::ResetAssetRoot();
|
||||
@@ -2382,8 +2387,8 @@ namespace AssetProcessor
|
||||
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_jobKey.compare(processResults[1].m_jobEntry.m_jobKey) != 0);
|
||||
|
||||
QStringList pcouts;
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/") + gameName + "/basefile.arc1"));
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/") + gameName + "/basefile.arc2"));
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/basefile.arc1")));
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/basefile.arc2")));
|
||||
|
||||
// Create the product files for the first job
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts[0], "product1"));
|
||||
@@ -2416,7 +2421,7 @@ namespace AssetProcessor
|
||||
UNIT_TEST_EXPECT_TRUE(AssetUtilities::NormalizeFilePath(changedInputResults[0].first) == AssetUtilities::NormalizeFilePath(sourceFile));
|
||||
|
||||
pcouts.clear();
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/") + gameName + "/basefile.arc3"));
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/basefile.arc3")));
|
||||
// Create the product files for the second job
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts[0], "product1"));
|
||||
|
||||
@@ -2515,9 +2520,7 @@ namespace AssetProcessor
|
||||
UnitTestUtils::ScopedDir changeDir(dir.path());
|
||||
QDir tempPath(dir.path());
|
||||
|
||||
|
||||
// system is already actually initialized, along with gEnv, so this will always return that game name.
|
||||
QString gameName = AssetUtilities::ComputeGameName(AssetProcessorManagerTestGameProject);
|
||||
QString gameName = AssetUtilities::ComputeProjectName(AssetProcessorManagerTestGameProject);
|
||||
|
||||
// update the engine root
|
||||
AssetUtilities::ResetAssetRoot();
|
||||
@@ -2745,7 +2748,7 @@ namespace AssetProcessor
|
||||
}
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
|
||||
};
|
||||
|
||||
|
||||
AssetProcessor::AssetBuilderInfoBus::Handler::BusConnect();
|
||||
QDir oldRoot;
|
||||
AssetUtilities::ComputeAssetRoot(oldRoot);
|
||||
@@ -2757,8 +2760,7 @@ namespace AssetProcessor
|
||||
UnitTestUtils::ScopedDir changeDir(dir.path());
|
||||
QDir tempPath(dir.path());
|
||||
|
||||
// system is already actually initialized, along with gEnv, so this will always return that game name.
|
||||
QString gameName = AssetUtilities::ComputeGameName(AssetProcessorManagerTestGameProject);
|
||||
QString gameName = AssetUtilities::ComputeProjectName(AssetProcessorManagerTestGameProject);
|
||||
|
||||
// update the engine root
|
||||
AssetUtilities::ResetAssetRoot();
|
||||
@@ -2803,11 +2805,11 @@ namespace AssetProcessor
|
||||
QDir cacheRoot;
|
||||
UNIT_TEST_EXPECT_TRUE(AssetUtilities::ComputeProjectCacheRoot(cacheRoot));
|
||||
|
||||
QString productFileAPath = cacheRoot.filePath(QString("pc/") + gameName + "/FileAProduct.txt");
|
||||
QString productFileBPath = cacheRoot.filePath(QString("pc/") + gameName + "/FileBProduct1.txt");
|
||||
QString product2FileBPath = cacheRoot.filePath(QString("pc/") + gameName + "/FileBProduct2.txt");
|
||||
QString productFileCPath = cacheRoot.filePath(QString("pc/") + gameName + "/FileCProduct.txt");
|
||||
QString product2FileCPath = cacheRoot.filePath(QString("pc/") + gameName + "/FileCProduct2.txt");
|
||||
QString productFileAPath = cacheRoot.filePath(QString("pc/FileAProduct.txt"));
|
||||
QString productFileBPath = cacheRoot.filePath(QString("pc/FileBProduct1.txt"));
|
||||
QString product2FileBPath = cacheRoot.filePath(QString("pc/FileBProduct2.txt"));
|
||||
QString productFileCPath = cacheRoot.filePath(QString("pc/FileCProduct.txt"));
|
||||
QString product2FileCPath = cacheRoot.filePath(QString("pc/FileCProduct2.txt"));
|
||||
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(sourceFileAPath, ""));
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(sourceFileBPath, ""));
|
||||
@@ -2817,7 +2819,7 @@ namespace AssetProcessor
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(product2FileBPath, "product"));
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(productFileCPath, "product"));
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(product2FileCPath, "product"));
|
||||
|
||||
|
||||
// Analyze FileA
|
||||
QMetaObject::invokeMethod(&apm, "AssessAddedFile", Qt::QueuedConnection, Q_ARG(QString, sourceFileAPath));
|
||||
|
||||
@@ -2927,7 +2929,7 @@ namespace AssetProcessor
|
||||
{
|
||||
// Ensure that we are processing the right FileB job
|
||||
UNIT_TEST_EXPECT_TRUE(QString(jobDetail.m_jobEntry.m_jobKey).compare("yyy") == 0);
|
||||
|
||||
|
||||
response.m_outputProducts.clear();
|
||||
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(product2FileBPath.toUtf8().constData()));
|
||||
QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, jobDetail.m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response));
|
||||
@@ -2981,7 +2983,7 @@ namespace AssetProcessor
|
||||
|
||||
processResults.clear();
|
||||
// Modify fingerprint of Job("FileA", "xxx", "pc") and analyze FileA again,
|
||||
changeJobAFingerprint = false; // This will revert back the changes in the extra info used for fingerprinting of this job
|
||||
changeJobAFingerprint = false; // This will revert back the changes in the extra info used for fingerprinting of this job
|
||||
|
||||
QMetaObject::invokeMethod(&apm, "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, sourceFileAPath));
|
||||
|
||||
@@ -3001,7 +3003,7 @@ namespace AssetProcessor
|
||||
{
|
||||
// Verify FileB jobinfo
|
||||
UNIT_TEST_EXPECT_TRUE(QString(jobDetail.m_jobEntry.m_jobKey).compare("yyy") == 0);
|
||||
|
||||
|
||||
}
|
||||
else if (QString(jobDetail.m_jobEntry.m_pathRelativeToWatchFolder).endsWith("FileC.txt"))
|
||||
{
|
||||
@@ -3011,14 +3013,14 @@ namespace AssetProcessor
|
||||
}
|
||||
|
||||
|
||||
// Since one of the FileC job("FileC.txt","zzz") have emitted a job dependency on a FileB job("FileB.txt", "yyy")
|
||||
// which also have a job dependency on a FileA job("FileA.txt", "xxx") therefore deleting File A source file should
|
||||
// Since one of the FileC job("FileC.txt","zzz") have emitted a job dependency on a FileB job("FileB.txt", "yyy")
|
||||
// which also have a job dependency on a FileA job("FileA.txt", "xxx") therefore deleting File A source file should
|
||||
// cause both jobs (File B and File C) to be processed again.
|
||||
|
||||
|
||||
processResults.clear();
|
||||
|
||||
QFile::remove(sourceFileAPath);
|
||||
|
||||
|
||||
QMetaObject::invokeMethod(&apm, "AssessDeletedFile", Qt::QueuedConnection, Q_ARG(QString, sourceFileAPath));
|
||||
|
||||
UNIT_TEST_EXPECT_TRUE(BlockUntil(idling, 5000));
|
||||
@@ -3053,7 +3055,7 @@ namespace AssetProcessor
|
||||
|
||||
UNIT_TEST_EXPECT_TRUE(BlockUntil(idling, 5000));
|
||||
|
||||
|
||||
|
||||
UNIT_TEST_EXPECT_TRUE(processResults.size() == 3);
|
||||
|
||||
for (JobDetails& jobDetail : processResults)
|
||||
@@ -3091,15 +3093,14 @@ namespace AssetProcessor
|
||||
|
||||
// the canonicalization of the path here is to get around the fact that on some platforms
|
||||
// the "temporary" folder location could be junctioned into some other folder and getting "QDir::current()"
|
||||
// and other similar functions may actually return a different string but still be referring to the same folder
|
||||
// and other similar functions may actually return a different string but still be referring to the same folder
|
||||
QTemporaryDir dir;
|
||||
QDir tempPath(dir.path());
|
||||
QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
|
||||
UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
|
||||
tempPath = QDir(canonicalTempDirPath);
|
||||
|
||||
// system is already actually initialized, along with gEnv, so this will always return that game name.
|
||||
QString gameName = AssetUtilities::ComputeGameName(AssetProcessorManagerTestGameProject);
|
||||
QString gameName = AssetUtilities::ComputeProjectName(AssetProcessorManagerTestGameProject);
|
||||
|
||||
// update the engine root
|
||||
AssetUtilities::ResetAssetRoot();
|
||||
@@ -3116,7 +3117,7 @@ namespace AssetProcessor
|
||||
|
||||
// note: the crux of this test is that we ar redirecting output into the cache at a different location instead of default.
|
||||
// so our scan folder has a "redirected" folder.
|
||||
config.AddScanFolder(ScanFolderInfo(tempPath.filePath("subfolder1"), "subfolder1", "subfolder1", "redirected", false, true, platforms, -1));
|
||||
config.AddScanFolder(ScanFolderInfo(tempPath.filePath("subfolder1"), "subfolder1", "subfolder1", "redirected", false, true, platforms, -1));
|
||||
|
||||
AssetProcessorManager_Test apm(&config);
|
||||
|
||||
@@ -3173,7 +3174,7 @@ namespace AssetProcessor
|
||||
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_watchFolderPath == tempPath.filePath("subfolder1"));
|
||||
|
||||
QStringList pcouts;
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/") + gameName + "/redirected/basefile.arc1"));
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/redirected/basefile.arc1")));
|
||||
|
||||
// Create the product files for the first job
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts[0], "product1"));
|
||||
@@ -3230,7 +3231,7 @@ namespace AssetProcessor
|
||||
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_databaseSourceName == "redirected/basefile.foo");
|
||||
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_watchFolderPath == tempPath.absoluteFilePath("subfolder1"));
|
||||
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/") + gameName + "/redirected/basefile.arc1"));
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/redirected/basefile.arc1")));
|
||||
|
||||
// Create the product files for the first job
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts[0], "product1"));
|
||||
@@ -3260,7 +3261,7 @@ namespace AssetProcessor
|
||||
assetMessages.clear();
|
||||
changedInputResults.clear();
|
||||
|
||||
QString deletedProductPath = cacheRoot.filePath(QString("pc/") + gameName + "/redirected/basefile.arc1");
|
||||
QString deletedProductPath = cacheRoot.filePath(QString("pc/redirected/basefile.arc1"));
|
||||
|
||||
QFile::remove(deletedProductPath);
|
||||
|
||||
@@ -3279,7 +3280,7 @@ namespace AssetProcessor
|
||||
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_databaseSourceName == "redirected/basefile.foo");
|
||||
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_watchFolderPath == tempPath.absoluteFilePath("subfolder1"));
|
||||
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/") + gameName + "/redirected/basefile.arc1"));
|
||||
pcouts.push_back(cacheRoot.filePath(QString("pc/redirected/basefile.arc1")));
|
||||
|
||||
// Create the product files for the first job
|
||||
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts[0], "product1"));
|
||||
|
||||
@@ -78,9 +78,8 @@ void AssetProcessorServerUnitTest::RunFirstPartOfUnitTestsForAssetProcessorServe
|
||||
|
||||
void AssetProcessorServerUnitTest::RunAssetProcessorConnectionStressTest(bool failNegotiation)
|
||||
{
|
||||
AZStd::string azAppRoot = AZStd::string(QDir::current().absolutePath().toUtf8().constData());
|
||||
AZStd::string azBranchToken;
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForAppRoot, azBranchToken);
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForEngineRoot, azBranchToken);
|
||||
|
||||
QString branchToken(azBranchToken.c_str());
|
||||
|
||||
@@ -99,7 +98,7 @@ void AssetProcessorServerUnitTest::RunAssetProcessorConnectionStressTest(bool fa
|
||||
for (int idx = 0; idx < NUMBER_OF_TRIES; ++idx)
|
||||
{
|
||||
AzFramework::AssetSystem::AssetProcessorConnection connection;
|
||||
connection.Configure(branchToken.toUtf8().data(), "pc", "UNITTEST", AssetUtilities::ComputeGameName().toUtf8().constData()); // UNITTEST identifier will skip the processID validation during negotiation
|
||||
connection.Configure(branchToken.toUtf8().data(), "pc", "UNITTEST", AssetUtilities::ComputeProjectName().toUtf8().constData()); // UNITTEST identifier will skip the processID validation during negotiation
|
||||
connection.Connect("127.0.0.1", FEATURE_TEST_LISTEN_PORT);
|
||||
while (!connection.IsConnected() && !connection.NegotiationFailed())
|
||||
{
|
||||
|
||||
@@ -268,7 +268,7 @@ namespace UnitTestUtils
|
||||
|
||||
m_localFileIO->SetAlias("@assets@", (newDir + QString("/ALIAS/assets")).toUtf8().constData());
|
||||
m_localFileIO->SetAlias("@log@", (newDir + QString("/ALIAS/logs")).toUtf8().constData());
|
||||
m_localFileIO->SetAlias("@cache@", (newDir + QString("/ALIAS/cache")).toUtf8().constData());
|
||||
m_localFileIO->SetAlias("@usercache@", (newDir + QString("/ALIAS/cache")).toUtf8().constData());
|
||||
m_localFileIO->SetAlias("@user@", (newDir + QString("/ALIAS/user")).toUtf8().constData());
|
||||
m_localFileIO->SetAlias("@root@", (newDir + QString("/ALIAS/root")).toUtf8().constData());
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ using namespace AssetProcessor;
|
||||
namespace AssetProcessor
|
||||
{
|
||||
const char* const TEST_BOOTSTRAP_DATA =
|
||||
"sys_game_folder = TestProject \r\n\
|
||||
"project_path = TestProject \r\n\
|
||||
assets = pc \r\n\
|
||||
-- ip and port of the asset processor.Only if you need to change defaults \r\n\
|
||||
-- remote_ip = 127.0.0.1 \r\n\
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "native/utilities/ApplicationManager.h"
|
||||
|
||||
#include <AzCore/Casting/lossy_cast.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
@@ -83,7 +84,7 @@ namespace AssetProcessor
|
||||
// we are in a job thread - return early to make it so that the global log file does not get this message
|
||||
// there will also be a log listener in the actual job log thread which will get the message too, and that one
|
||||
// will write it to the individual log.
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
AzFramework::LogComponent::OutputMessage(severity, window, message);
|
||||
@@ -119,29 +120,14 @@ AssetProcessorAZApplication::AssetProcessorAZApplication(int* argc, char*** argv
|
||||
*AZ::SettingsRegistry::Get(), AssetProcessorBuildTarget::GetBuildTargetName());
|
||||
|
||||
// Adding the PreModuleLoad event to the AssetProcessor application for logging when a gem loads
|
||||
m_preModuleLoadHandler = AZ::ModuleManagerRequests::PreModuleLoadEvent::Handler{ []([[maybe_unused]] AZStd::string_view modulePath)
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Loading (Gem) Module '%.*s'...\n", aznumeric_cast<int>(modulePath.size()), modulePath.data());
|
||||
} };
|
||||
m_preModuleLoadHandler = AZ::ModuleManagerRequests::PreModuleLoadEvent::Handler{
|
||||
[]([[maybe_unused]] AZStd::string_view modulePath)
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Loading (Gem) Module '%.*s'...\n", aznumeric_cast<int>(modulePath.size()), modulePath.data());
|
||||
}
|
||||
};
|
||||
|
||||
m_preModuleLoadHandler.Connect(m_moduleManager->m_preModuleLoadEvent);
|
||||
|
||||
// Override the /Amazon/AzCore/Bootstrap/sys_game_folder entry in the Settings Registry using the -gamefolder parameter
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
if (m_commandLine.GetNumSwitchValues("gamefolder") > 0)
|
||||
{
|
||||
const AZStd::string& gameFolderOverride = m_commandLine.GetSwitchValue("gamefolder", 0);
|
||||
auto gameFolderCommandLineOverride = AZStd::string::format("--regset=%s/sys_game_folder=%s", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey,
|
||||
gameFolderOverride.c_str());
|
||||
|
||||
AZ::CommandLine::ParamContainer commandLineArgs;
|
||||
m_commandLine.Dump(commandLineArgs);
|
||||
commandLineArgs.emplace_back(gameFolderCommandLineOverride);
|
||||
m_commandLine.Parse(commandLineArgs);
|
||||
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*settingsRegistry, m_commandLine, false);
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry);
|
||||
}
|
||||
}
|
||||
|
||||
AZ::ComponentTypeList AssetProcessorAZApplication::GetRequiredSystemComponents() const
|
||||
@@ -155,7 +141,7 @@ AZ::ComponentTypeList AssetProcessorAZApplication::GetRequiredSystemComponents()
|
||||
|| *iter == AZ::Uuid("{624a7be2-3c7e-4119-aee2-1db2bdb6cc89}") // ScriptDebugAgent
|
||||
|| *iter == AZ::Uuid("{CAF3A025-FAC9-4537-B99E-0A800A9326DF}") // InputSystemComponent
|
||||
|| *iter == azrtti_typeid<AssetProcessor::ToolsAssetCatalogComponent>()
|
||||
)
|
||||
)
|
||||
{
|
||||
// AP does not require the above components to be active
|
||||
iter = components.erase(iter);
|
||||
@@ -274,7 +260,7 @@ void ApplicationManager::GetExternalBuilderFileList(QStringList& externalBuilder
|
||||
|
||||
if (externalBuilderModules.empty())
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "AssetProcessor was unable to locate any builders\n");
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "AssetProcessor was unable to locate any external builders\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,9 +270,16 @@ QDir ApplicationManager::GetSystemRoot() const
|
||||
{
|
||||
return m_systemRoot;
|
||||
}
|
||||
QString ApplicationManager::GetGameName() const
|
||||
QString ApplicationManager::GetProjectPath() const
|
||||
{
|
||||
return m_gameName;
|
||||
auto projectPath = AZ::Utils::GetProjectPath();
|
||||
if (!projectPath.empty())
|
||||
{
|
||||
return QString::fromUtf8(projectPath.c_str(), aznumeric_cast<int>(projectPath.size()));
|
||||
}
|
||||
|
||||
AZ_Warning("AssetUtils", false, "Unable to obtain the Project Path from the settings registry.");
|
||||
return {};
|
||||
}
|
||||
|
||||
QCoreApplication* ApplicationManager::GetQtApplication()
|
||||
@@ -451,7 +444,7 @@ void ApplicationManager::PopulateApplicationDependencies()
|
||||
m_filesOfInterest.push_back(applicationPath);
|
||||
|
||||
// add some known-dependent files (this can be removed when they are no longer a dependency)
|
||||
// Note that its not necessary for any of these files to actually exist. It is considered a "change" if they
|
||||
// Note that its not necessary for any of these files to actually exist. It is considered a "change" if they
|
||||
// change their file modtime, or if they go from existing to not existing, or if they go from not existing, to existing.
|
||||
// any of those should cause AP to drop.
|
||||
for (const QString& pathName : { "CrySystem",
|
||||
@@ -475,11 +468,10 @@ void ApplicationManager::PopulateApplicationDependencies()
|
||||
QDir assetRoot;
|
||||
AssetUtilities::ComputeAssetRoot(assetRoot);
|
||||
|
||||
QString globalConfigPath = assetRoot.filePath("AssetProcessorPlatformConfig.ini");
|
||||
QString globalConfigPath = assetRoot.filePath("AssetProcessorPlatformConfig.setreg");
|
||||
m_filesOfInterest.push_back(globalConfigPath);
|
||||
|
||||
QString gameName = AssetUtilities::ComputeGameName();
|
||||
QString gamePlatformConfigPath = assetRoot.filePath(gameName + "/AssetProcessorGamePlatformConfig.ini");
|
||||
QString gamePlatformConfigPath = QDir(AssetUtilities::ComputeProjectPath()).filePath("AssetProcessorGamePlatformConfig.setreg");
|
||||
m_filesOfInterest.push_back(gamePlatformConfigPath);
|
||||
|
||||
// add app modules
|
||||
@@ -510,43 +502,14 @@ void ApplicationManager::PopulateApplicationDependencies()
|
||||
}
|
||||
}
|
||||
|
||||
bool ApplicationManager::StartAZFramework(QString appRootOverride)
|
||||
bool ApplicationManager::StartAZFramework()
|
||||
{
|
||||
AzFramework::Application::Descriptor appDescriptor;
|
||||
AZ::ComponentApplication::StartupParameters params;
|
||||
|
||||
QString gameName = AssetUtilities::ComputeGameName();
|
||||
QString projectName = AssetUtilities::ComputeProjectName();
|
||||
AZ::SettingsRegistryInterface& registry = *AZ::SettingsRegistry::Get();
|
||||
// Add the supplied gameName as specialization key in the registry
|
||||
if (!gameName.isEmpty())
|
||||
{
|
||||
auto gameNameSpecialization = QString("%1/%2").arg(AZ::SettingsRegistryMergeUtils::SpecializationsRootKey).arg(gameName);
|
||||
QByteArray specializationByteArray = gameNameSpecialization.toUtf8();
|
||||
registry.Set(AZStd::string_view(specializationByteArray.data(), specializationByteArray.size()), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add the project name as a registry specialization
|
||||
auto projectKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/sys_game_folder", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey);
|
||||
if (AZ::SettingsRegistryInterface::FixedValueString bootstrapProjectName; registry.Get(bootstrapProjectName, projectKey) && !bootstrapProjectName.empty())
|
||||
{
|
||||
registry.Set(AZ::SettingsRegistryInterface::FixedValueString::format("%s/%s",
|
||||
AZ::SettingsRegistryMergeUtils::SpecializationsRootKey, bootstrapProjectName.c_str()),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
// The application will live in a bin folder one level up from the app root.
|
||||
static char s_storageForRootPath[AZ_MAX_PATH_LEN] = { 0 };
|
||||
if (!appRootOverride.isEmpty())
|
||||
{
|
||||
azstrcpy(s_storageForRootPath, AZ_MAX_PATH_LEN, appRootOverride.toUtf8().data());
|
||||
params.m_appRootOverride = s_storageForRootPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
params.m_appRootOverride = nullptr;
|
||||
}
|
||||
// Prevent loading of gems in the Create method of the ComponentApplication
|
||||
params.m_loadDynamicModules = false;
|
||||
|
||||
@@ -556,31 +519,27 @@ bool ApplicationManager::StartAZFramework(QString appRootOverride)
|
||||
AZ::Debug::Trace::HandleExceptions(true);
|
||||
|
||||
m_frameworkApp.Start(appDescriptor, params);
|
||||
|
||||
|
||||
//Registering all the Components
|
||||
m_frameworkApp.RegisterComponentDescriptor(AzFramework::LogComponent::CreateDescriptor());
|
||||
|
||||
|
||||
|
||||
Reflect();
|
||||
|
||||
QDir engineRoot;
|
||||
AssetUtilities::ComputeEngineRoot(engineRoot);
|
||||
|
||||
AZ::IO::FileIOBase::GetInstance()->SetAlias("@devroot@", engineRoot.absolutePath().toUtf8().data());
|
||||
|
||||
const AzFramework::CommandLine* commandLine = nullptr;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(commandLine, &AzFramework::ApplicationRequests::GetCommandLine);
|
||||
if (commandLine && commandLine->HasSwitch("logDir"))
|
||||
{
|
||||
AZ::IO::FileIOBase::GetInstance()->SetAlias("@log@", commandLine->GetSwitchValue("logDir", 0).c_str());
|
||||
}
|
||||
else
|
||||
else if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
char executableDirectory[AZ_MAX_PATH_LEN];
|
||||
if (AZ::Utils::GetExecutableDirectory(executableDirectory, AZStd::size(executableDirectory)) == AZ::Utils::ExecutablePathResult::Success)
|
||||
{
|
||||
AZ::IO::FileIOBase::GetInstance()->SetAlias("@log@", executableDirectory);
|
||||
}
|
||||
AZ::IO::Path projectUserPath;
|
||||
settingsRegistry->Get(projectUserPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectUserPath);
|
||||
|
||||
AZ::IO::Path logUserPath = projectUserPath / "log";
|
||||
auto fileIo = AZ::IO::FileIOBase::GetInstance();
|
||||
fileIo->SetAlias("@log@", logUserPath.c_str());
|
||||
}
|
||||
m_entity = aznew AZ::Entity("Application Entity");
|
||||
if (m_entity)
|
||||
@@ -622,7 +581,6 @@ bool ApplicationManager::ActivateModules()
|
||||
AZ_Error(AssetProcessor::ConsoleChannel, false, "Cannot compute the asset root folder. Is AssetProcessor being run from the appropriate folder?");
|
||||
return false;
|
||||
}
|
||||
assetRoot.cd(AssetUtilities::ComputeGameName());
|
||||
|
||||
m_frameworkApp.LoadDynamicModules();
|
||||
return true;
|
||||
@@ -633,89 +591,13 @@ void ApplicationManager::addRunningThread(AssetProcessor::ThreadWorker* thread)
|
||||
m_runningThreads.push_back(thread);
|
||||
}
|
||||
|
||||
QString ApplicationManager::ParseOptionAppRootArgument()
|
||||
{
|
||||
AZ_Assert(m_qApp!=nullptr,"m_qApp not initialized. QT application must be created before this call.")
|
||||
// Parse any parameters.
|
||||
static const char* app_root_parameter = "app-root";
|
||||
static const char* app_root_parameter_desc = "Optional external path outside of the current engine to set as the application root.";
|
||||
QCommandLineOption appRootPathOption(QString(app_root_parameter), tr(app_root_parameter_desc), QString("path"));
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("Asset Processor");
|
||||
parser.addOption(appRootPathOption);
|
||||
parser.parse(m_qApp->arguments());
|
||||
|
||||
QString appRootArgValue = parser.value(appRootPathOption);
|
||||
appRootArgValue.remove(QChar('\"'));
|
||||
return appRootArgValue.trimmed();
|
||||
}
|
||||
|
||||
bool ApplicationManager::ValidateExternalAppRoot(QString appRootPath) const
|
||||
{
|
||||
static const char* bootstrap_cfg_name = "bootstrap.cfg";
|
||||
|
||||
QDir testAppRootPath(appRootPath);
|
||||
|
||||
// Make sure the path exists
|
||||
if (!testAppRootPath.exists())
|
||||
{
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, testAppRootPath.exists(), "Invalid Application Root path override (--app-root): %s. Directory does not exist.\n", appRootPath.toUtf8().data());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the path contains bootstrap.cfg
|
||||
if (!testAppRootPath.exists(bootstrap_cfg_name))
|
||||
{
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, testAppRootPath.exists(), "Invalid Application Root path override (--app-root): %s. Directory does not contain %s.\n", appRootPath.toUtf8().data(), bootstrap_cfg_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure we can read the 'sys_game_folder' settings from bootstrap.cfg
|
||||
QSettings settings(testAppRootPath.absoluteFilePath(bootstrap_cfg_name), QSettings::Format::IniFormat);
|
||||
static const char* sysGameFolderKeyName = "sys_game_folder";
|
||||
auto sysGameFolderSettings = settings.value(sysGameFolderKeyName);
|
||||
if (!sysGameFolderSettings.isValid() || sysGameFolderSettings.isNull())
|
||||
{
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, testAppRootPath.exists(), "Invalid Application Root path override (--app-root): %s. %s in the path is not valid.\n", appRootPath.toUtf8().data(), bootstrap_cfg_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the 'sys_game_folder' value in the external bootstrap.cfg points to a valid subfolder in that path
|
||||
QString sysGameFolder = sysGameFolderSettings.toString();
|
||||
QDir gameFolderPath(appRootPath);
|
||||
if (!gameFolderPath.cd(sysGameFolder))
|
||||
{
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, testAppRootPath.exists(), "Invalid Application Root path override (--app-root): %s. Configured Game folder %s in the path is not valid.\n", appRootPath.toUtf8().data(), sysGameFolder.toUtf8().data());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ApplicationManager::BeforeRunStatus ApplicationManager::BeforeRun()
|
||||
{
|
||||
// Create the Qt Application
|
||||
CreateQtApplication();
|
||||
|
||||
// Calculate the override app root path if provided and validate it before passing it along
|
||||
QString overrideAppRootPath = ParseOptionAppRootArgument();
|
||||
|
||||
if (!overrideAppRootPath.isEmpty())
|
||||
{
|
||||
if (ValidateExternalAppRoot(overrideAppRootPath))
|
||||
{
|
||||
QDir overrideAppRoot(overrideAppRootPath);
|
||||
QDir resultAppRoot;
|
||||
AssetUtilities::ComputeAssetRoot(resultAppRoot, &overrideAppRoot);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(AssetProcessor::ConsoleChannel, false, "Invalid override app root folder '%s'.", overrideAppRootPath.toUtf8().data());
|
||||
return ApplicationManager::BeforeRunStatus::Status_Failure;
|
||||
}
|
||||
}
|
||||
|
||||
if (!StartAZFramework(overrideAppRootPath))
|
||||
if (!StartAZFramework())
|
||||
{
|
||||
return ApplicationManager::BeforeRunStatus::Status_Failure;
|
||||
}
|
||||
@@ -742,14 +624,13 @@ bool ApplicationManager::Activate()
|
||||
return false;
|
||||
}
|
||||
|
||||
m_gameName = AssetUtilities::ComputeGameName();
|
||||
|
||||
if (m_gameName.isEmpty())
|
||||
auto projectName = AssetUtilities::ComputeProjectName();
|
||||
if (projectName.isEmpty())
|
||||
{
|
||||
AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable to detect name of current game project. Is bootstrap.cfg appropriately configured?");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// the following controls what registry keys (or on mac or linux what entries in home folder) are used
|
||||
// so they should not be translated!
|
||||
qApp->setOrganizationName(GetOrganizationName());
|
||||
|
||||
@@ -101,7 +101,7 @@ public:
|
||||
QCoreApplication* GetQtApplication();
|
||||
|
||||
QDir GetSystemRoot() const;
|
||||
QString GetGameName() const;
|
||||
QString GetProjectPath() const;
|
||||
|
||||
void RegisterComponentDescriptor(const AZ::ComponentDescriptor* descriptor);
|
||||
|
||||
@@ -163,9 +163,7 @@ protected:
|
||||
virtual const char* GetLogBaseName() = 0;
|
||||
virtual RegistryCheckInstructions PopupRegistryProblemsMessage(QString warningText) = 0;
|
||||
private:
|
||||
bool StartAZFramework(QString appRootOverride);
|
||||
bool ValidateExternalAppRoot(QString appRootPath) const;
|
||||
QString ParseOptionAppRootArgument();
|
||||
bool StartAZFramework();
|
||||
|
||||
// QuitPair - Object pointer and "is ready" boolean pair.
|
||||
typedef QPair<QObject*, bool> QuitPair;
|
||||
@@ -178,7 +176,6 @@ private:
|
||||
bool m_needRestart = false;
|
||||
bool m_queuedCheckQuit = false;
|
||||
QDir m_systemRoot;
|
||||
QString m_gameName;
|
||||
AZ::Entity* m_entity = nullptr;
|
||||
|
||||
};
|
||||
|
||||
@@ -337,13 +337,13 @@ bool ApplicationManagerBase::InitPlatformConfiguration()
|
||||
m_platformConfiguration = new AssetProcessor::PlatformConfiguration();
|
||||
QDir assetRoot;
|
||||
AssetUtilities::ComputeAssetRoot(assetRoot);
|
||||
return m_platformConfiguration->InitializeFromConfigFiles(GetSystemRoot().absolutePath(), assetRoot.absolutePath(), GetGameName());
|
||||
return m_platformConfiguration->InitializeFromConfigFiles(GetSystemRoot().absolutePath(), assetRoot.absolutePath(), GetProjectPath());
|
||||
}
|
||||
|
||||
bool ApplicationManagerBase::InitBuilderConfiguration()
|
||||
{
|
||||
m_builderConfig = AZStd::make_unique<AssetProcessor::BuilderConfigurationManager>();
|
||||
QString configFile = GetSystemRoot().absoluteFilePath(GetGameName() + "/" + AssetProcessor::BuilderConfigFile);
|
||||
QString configFile = QDir(GetProjectPath()).absoluteFilePath(AssetProcessor::BuilderConfigFile);
|
||||
|
||||
if (!QFile::exists(configFile))
|
||||
{
|
||||
@@ -1193,7 +1193,8 @@ bool ApplicationManagerBase::Activate()
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "AssetProcessor will process assets from gameproject %s.\n", AssetUtilities::ComputeGameName().toUtf8().data());
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel,
|
||||
"AssetProcessor will process assets from project root %s.\n", AssetUtilities::ComputeProjectPath().toUtf8().data());
|
||||
|
||||
// Shutdown if the disk has less than 128MB of free space
|
||||
if (!CheckSufficientDiskSpace(projectCache.absolutePath(), 128 * 1024 * 1024, true))
|
||||
@@ -1219,7 +1220,7 @@ bool ApplicationManagerBase::Activate()
|
||||
|
||||
if (!InitPlatformConfiguration())
|
||||
{
|
||||
AZ_Error("AssetProcessor", false, "Failed to Initialize from AssetProcessorPlatformConfig.ini - check the log files in the logs/ subfolder for more information.");
|
||||
AZ_Error("AssetProcessor", false, "Failed to Initialize from AssetProcessorPlatformConfig.setreg - check the log files in the logs/ subfolder for more information.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1401,7 +1402,7 @@ bool ApplicationManagerBase::InitializeExternalBuilders()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ApplicationManagerBase::WaitForBuilderExit(AzToolsFramework::ProcessWatcher* processWatcher, AssetBuilderSDK::JobCancelListener* jobCancelListener, AZ::u32 processTimeoutLimitInSeconds)
|
||||
bool ApplicationManagerBase::WaitForBuilderExit(AzFramework::ProcessWatcher* processWatcher, AssetBuilderSDK::JobCancelListener* jobCancelListener, AZ::u32 processTimeoutLimitInSeconds)
|
||||
{
|
||||
AZ::u32 exitCode = 0;
|
||||
bool finishedOK = false;
|
||||
|
||||
@@ -177,7 +177,7 @@ protected:
|
||||
|
||||
AssetProcessor::AssetCatalog* GetAssetCatalog() const { return m_assetCatalog; }
|
||||
|
||||
static bool WaitForBuilderExit(AzToolsFramework::ProcessWatcher* processWatcher, AssetBuilderSDK::JobCancelListener* jobCancelListener, AZ::u32 processTimeoutLimitInSeconds);
|
||||
static bool WaitForBuilderExit(AzFramework::ProcessWatcher* processWatcher, AssetBuilderSDK::JobCancelListener* jobCancelListener, AZ::u32 processTimeoutLimitInSeconds);
|
||||
|
||||
ApplicationServer* m_applicationServer = nullptr;
|
||||
ConnectionManager* m_connectionManager = nullptr;
|
||||
|
||||
@@ -142,10 +142,6 @@ namespace AssetProcessor
|
||||
|
||||
bool Builder::Start()
|
||||
{
|
||||
// Get the app root to locate the builders
|
||||
AZStd::string appRootString;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(appRootString, &AzFramework::ApplicationRequests::GetAppRoot);
|
||||
|
||||
// Get the current BinXXX folder based on the current running AP
|
||||
QString applicationDir = QCoreApplication::instance()->applicationDirPath();
|
||||
|
||||
@@ -190,24 +186,26 @@ namespace AssetProcessor
|
||||
QDir projectCacheRoot;
|
||||
AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot);
|
||||
|
||||
QString gameName = AssetUtilities::ComputeGameName();
|
||||
|
||||
AZStd::string appRootString;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(appRootString, &AzFramework::ApplicationRequests::GetAppRoot);
|
||||
QDir appRoot(QString(appRootString.c_str()));
|
||||
QString gameRoot = appRoot.absoluteFilePath(gameName);
|
||||
QString gameName = AssetUtilities::ComputeProjectName();
|
||||
QString projectPath = AssetUtilities::ComputeProjectPath();
|
||||
QDir engineRoot;
|
||||
AssetUtilities::ComputeEngineRoot(engineRoot);
|
||||
|
||||
int portNumber = 0;
|
||||
ApplicationServerBus::BroadcastResult(portNumber, &ApplicationServerBus::Events::GetServerListeningPort);
|
||||
|
||||
AZStd::string params;
|
||||
#if !AZ_TRAIT_OS_PLATFORM_APPLE && !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
|
||||
params = AZStd::string::format(R"(-task=%s -id="%s" -gamename="%s" -gamecache="%s" -gameroot="%s" -port %d)",
|
||||
task, builderGuid.c_str(), gameName.toUtf8().constData(), projectCacheRoot.absolutePath().toUtf8().constData(), gameRoot.toUtf8().constData(), portNumber);
|
||||
#else
|
||||
params = AZStd::string::format(R"(-task=%s -id="%s" -gamename="\"%s\"" -gamecache="\"%s\"" -gameroot="\"%s\"" -port %d)",
|
||||
task, builderGuid.c_str(), gameName.toUtf8().constData(), projectCacheRoot.absolutePath().toUtf8().constData(), gameRoot.toUtf8().constData(), portNumber);
|
||||
#endif // !AZ_TRAIT_OS_PLATFORM_APPLE && !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
|
||||
#if !AZ_TRAIT_OS_PLATFORM_APPLE && !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
|
||||
params = AZStd::string::format(
|
||||
R"(-task=%s -id="%s" -project-name="%s" -project-cache-path="%s" -project-path="%s" -engine-path="%s" -port %d)", task,
|
||||
builderGuid.c_str(), gameName.toUtf8().constData(), projectCacheRoot.absolutePath().toUtf8().constData(),
|
||||
projectPath.toUtf8().constData(), engineRoot.absolutePath().toUtf8().constData(), portNumber);
|
||||
#else
|
||||
params = AZStd::string::format(
|
||||
R"(-task=%s -id="%s" -project-name="\"%s\"" -project-cache-path="\"%s\"" -project-path="\"%s\"" -engine-path="\"%s\"" -port %d)",
|
||||
task, builderGuid.c_str(), gameName.toUtf8().constData(), projectCacheRoot.absolutePath().toUtf8().constData(),
|
||||
projectPath.toUtf8().constData(), engineRoot.absolutePath().toUtf8().constData(), portNumber);
|
||||
#endif // !AZ_TRAIT_OS_PLATFORM_APPLE && !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
|
||||
|
||||
if (moduleFilePath && moduleFilePath[0])
|
||||
{
|
||||
@@ -230,17 +228,17 @@ namespace AssetProcessor
|
||||
return params;
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AzToolsFramework::ProcessWatcher> Builder::LaunchProcess(const char* fullExePath, const AZStd::string& params) const
|
||||
AZStd::unique_ptr<AzFramework::ProcessWatcher> Builder::LaunchProcess(const char* fullExePath, const AZStd::string& params) const
|
||||
{
|
||||
AzToolsFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
processLaunchInfo.m_processExecutableString = fullExePath;
|
||||
processLaunchInfo.m_commandlineParameters = AZStd::string::format("\"%s\" %s", fullExePath, params.c_str());
|
||||
processLaunchInfo.m_showWindow = false;
|
||||
processLaunchInfo.m_processPriority = AzToolsFramework::ProcessPriority::PROCESSPRIORITY_IDLE;
|
||||
processLaunchInfo.m_processPriority = AzFramework::ProcessPriority::PROCESSPRIORITY_IDLE;
|
||||
|
||||
AZ_TracePrintf(AssetProcessor::DebugChannel, "Executing AssetBuilder with parameters: %s\n", processLaunchInfo.m_commandlineParameters.c_str());
|
||||
|
||||
auto processWatcher = AZStd::unique_ptr<AzToolsFramework::ProcessWatcher>(AzToolsFramework::ProcessWatcher::LaunchProcess(processLaunchInfo, AzToolsFramework::COMMUNICATOR_TYPE_STDINOUT));
|
||||
auto processWatcher = AZStd::unique_ptr<AzFramework::ProcessWatcher>(AzFramework::ProcessWatcher::LaunchProcess(processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_STDINOUT));
|
||||
|
||||
AZ_Error(AssetProcessor::ConsoleChannel, processWatcher, "Failed to start %s", fullExePath);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/parallel/binary_semaphore.h>
|
||||
#include <AzToolsFramework/Process/ProcessWatcher.h>
|
||||
#include <AzFramework/Process/ProcessWatcher.h>
|
||||
#include <AssetBuilderSDK/AssetBuilderSDK.h>
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
#include <QString>
|
||||
@@ -108,7 +108,7 @@ namespace AssetProcessor
|
||||
void SetConnection(AZ::u32 connId);
|
||||
|
||||
AZStd::string BuildParams(const char* task, const char* moduleFilePath, const AZStd::string& builderGuid, const AZStd::string& jobDescriptionFile, const AZStd::string& jobResponseFile) const;
|
||||
AZStd::unique_ptr<AzToolsFramework::ProcessWatcher> LaunchProcess(const char* fullExePath, const AZStd::string& params) const;
|
||||
AZStd::unique_ptr<AzFramework::ProcessWatcher> LaunchProcess(const char* fullExePath, const AZStd::string& params) const;
|
||||
|
||||
//! Waits for the builder exe to send the job response and pumps stdout/err
|
||||
BuilderRunJobOutcome WaitForBuilderResponse(AssetBuilderSDK::JobCancelListener* jobCancelListener, AZ::u32 processTimeoutLimitInSeconds, AZStd::binary_semaphore* waitEvent) const;
|
||||
@@ -128,7 +128,7 @@ namespace AssetProcessor
|
||||
AZStd::binary_semaphore m_connectionEvent;
|
||||
|
||||
//! Optional process watcher
|
||||
AZStd::unique_ptr<AzToolsFramework::ProcessWatcher> m_processWatcher = nullptr;
|
||||
AZStd::unique_ptr<AzFramework::ProcessWatcher> m_processWatcher = nullptr;
|
||||
|
||||
//! Optional communicator, only available if we have a process watcher
|
||||
AZStd::unique_ptr<CommunicatorTracePrinter> m_tracePrinter = nullptr;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#include "CommunicatorTracePrinter.h"
|
||||
|
||||
CommunicatorTracePrinter::CommunicatorTracePrinter(AzToolsFramework::ProcessCommunicator* communicator, const char* window) :
|
||||
CommunicatorTracePrinter::CommunicatorTracePrinter(AzFramework::ProcessCommunicator* communicator, const char* window) :
|
||||
m_communicator(communicator),
|
||||
m_window(window)
|
||||
{
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzToolsFramework/Process/ProcessCommunicator.h>
|
||||
#include <AzFramework/Process/ProcessCommunicator.h>
|
||||
|
||||
//! CommunicatorTracePrinter listens to stderr and stdout of a running process and writes its output to the AZ_Trace system
|
||||
//! Importantly, it does not do any blocking operations.
|
||||
class CommunicatorTracePrinter
|
||||
{
|
||||
public:
|
||||
CommunicatorTracePrinter(AzToolsFramework::ProcessCommunicator* communicator, const char* window);
|
||||
CommunicatorTracePrinter(AzFramework::ProcessCommunicator* communicator, const char* window);
|
||||
~CommunicatorTracePrinter();
|
||||
|
||||
// call this periodically to drain the buffers and write them.
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
|
||||
private:
|
||||
AZStd::string m_window;
|
||||
AzToolsFramework::ProcessCommunicator* m_communicator;
|
||||
AzFramework::ProcessCommunicator* m_communicator;
|
||||
char m_streamBuffer[128];
|
||||
AZStd::string m_stringBeingConcatenated;
|
||||
AZStd::string m_errorStringBeingConcatenated;
|
||||
|
||||
@@ -130,37 +130,36 @@ ApplicationManager::BeforeRunStatus GUIApplicationManager::BeforeRun()
|
||||
m_qtFileWatcher.addPath(assetDbPath);
|
||||
|
||||
// if our Gems file changes, make sure we watch that, too.
|
||||
QString gameName = AssetUtilities::ComputeGameName();
|
||||
QString gemsConfigFile = devRoot.filePath(gameName + "/gems.json");
|
||||
|
||||
m_qtFileWatcher.addPath(gemsConfigFile);
|
||||
QString projectPath = AssetUtilities::ComputeProjectPath();
|
||||
|
||||
QObject::connect(&m_qtFileWatcher, &QFileSystemWatcher::fileChanged, this, &GUIApplicationManager::FileChanged);
|
||||
QObject::connect(&m_qtFileWatcher, &QFileSystemWatcher::directoryChanged, this, &GUIApplicationManager::DirectoryChanged);
|
||||
|
||||
// Register a notifier for when the sys_game_folder property changes within the SettingsRegistry
|
||||
// Register a notifier for when the project_path property changes within the SettingsRegistry
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
auto onBootStrapGameFolderChanged = [this, cachedGameName = gameName](AZStd::string_view path, AZ::SettingsRegistryInterface::Type type)
|
||||
// Needs to be updated to project_path.
|
||||
auto OnProjectPathChanged = [this, cachedProjectPath = projectPath](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
constexpr auto projectKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey)
|
||||
+ "/sys_game_folder";
|
||||
if (projectKey == path && type == AZ::SettingsRegistryInterface::Type::String)
|
||||
constexpr auto projectPathKey =
|
||||
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey)
|
||||
+ "/project_path";
|
||||
if (projectPathKey == path)
|
||||
{
|
||||
auto registry = AZ::SettingsRegistry::Get();
|
||||
AZ::SettingsRegistryInterface::FixedValueString newGameName;
|
||||
if (registry->Get(newGameName, path))
|
||||
AZ::SettingsRegistryInterface::FixedValueString newProjectPath;
|
||||
if (auto registry = AZ::SettingsRegistry::Get(); registry && registry->Get(newProjectPath, path))
|
||||
{
|
||||
// we only have to quit if the actual project name has changed, not if just the bootstrap has changed.
|
||||
if (cachedGameName.compare(newGameName.c_str()) != 0)
|
||||
// we only have to quit if the project path has changed, not if just the bootstrap has changed.
|
||||
if (cachedProjectPath.compare(newProjectPath.c_str()) != 0)
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Bootstrap.cfg Game Name changed from %s to %s. Quitting\n", cachedGameName.toUtf8().constData(), newGameName.c_str());
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "bootstrap.cfg Project Path changed from %s to %s. Quitting\n",
|
||||
cachedProjectPath.toUtf8().constData(), newProjectPath.c_str());
|
||||
QMetaObject::invokeMethod(this, "QuitRequested", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
m_bootstrapGameFolderChangedHandler = settingsRegistry->RegisterNotifier(AZStd::move(onBootStrapGameFolderChanged));
|
||||
m_bootstrapGameFolderChangedHandler = settingsRegistry->RegisterNotifier(AZStd::move(OnProjectPathChanged));
|
||||
}
|
||||
|
||||
return ApplicationManager::BeforeRunStatus::Status_Success;
|
||||
@@ -189,8 +188,13 @@ bool GUIApplicationManager::Run()
|
||||
qRegisterMetaType<AZ::u32>("AZ::u32");
|
||||
qRegisterMetaType<AZ::Uuid>("AZ::Uuid");
|
||||
|
||||
AZ::IO::FixedMaxPath engineRootPath;
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
settingsRegistry->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
}
|
||||
AzQtComponents::StyleManager* styleManager = new AzQtComponents::StyleManager(qApp);
|
||||
styleManager->Initialize(qApp);
|
||||
styleManager->initialize(qApp, engineRootPath);
|
||||
|
||||
QDir engineRoot;
|
||||
AssetUtilities::ComputeAssetRoot(engineRoot);
|
||||
@@ -198,7 +202,8 @@ bool GUIApplicationManager::Run()
|
||||
AzQtComponents::StyleManager::addSearchPaths(
|
||||
QStringLiteral("style"),
|
||||
engineRoot.filePath(QStringLiteral("Code/Tools/AssetProcessor/native/ui/style")),
|
||||
QStringLiteral(":/AssetProcessor/style"));
|
||||
QStringLiteral(":/AssetProcessor/style"),
|
||||
engineRootPath);
|
||||
|
||||
m_mainWindow = new MainWindow(this);
|
||||
auto wrapper = new AzQtComponents::WindowDecorationWrapper(
|
||||
@@ -485,7 +490,7 @@ bool GUIApplicationManager::OnError(const char* /*window*/, const char* message)
|
||||
QVariant settingValue = loader.value("Game Projects/enabled_game_projects");
|
||||
QStringList compiledProjects = settingValue.toStringList();
|
||||
|
||||
if(compiledProjects.isEmpty())
|
||||
if (compiledProjects.isEmpty())
|
||||
{
|
||||
QByteArray byteArray;
|
||||
QFile jsonFile;
|
||||
@@ -497,12 +502,12 @@ bool GUIApplicationManager::OnError(const char* /*window*/, const char* message)
|
||||
QJsonObject settingsObject = QJsonDocument::fromJson(byteArray).object();
|
||||
QJsonArray projectsArray = settingsObject["Game Projects"].toArray();
|
||||
|
||||
if(!projectsArray.isEmpty())
|
||||
if (!projectsArray.isEmpty())
|
||||
{
|
||||
auto projectObject = projectsArray[0].toObject();
|
||||
QString projects = projectObject["default_value"].toString();
|
||||
|
||||
if(!projects.isEmpty())
|
||||
if (!projects.isEmpty())
|
||||
{
|
||||
compiledProjects = projects.split(',');
|
||||
usingDefaults = true;
|
||||
@@ -515,13 +520,13 @@ bool GUIApplicationManager::OnError(const char* /*window*/, const char* message)
|
||||
compiledProjects[i] = compiledProjects[i].trimmed();
|
||||
}
|
||||
|
||||
QString enabledProject = AssetUtilities::ComputeGameName();
|
||||
QString enabledProject = AssetUtilities::ComputeProjectName();
|
||||
|
||||
if(!compiledProjects.contains(enabledProject))
|
||||
if (!compiledProjects.contains(enabledProject))
|
||||
{
|
||||
QString projectSourceLine;
|
||||
|
||||
if(usingDefaults)
|
||||
if (usingDefaults)
|
||||
{
|
||||
projectSourceLine = QString("The currently compiled projects according to the defaults in %1 are '%2'").arg(defaultSettingsFile);
|
||||
}
|
||||
@@ -531,7 +536,6 @@ bool GUIApplicationManager::OnError(const char* /*window*/, const char* message)
|
||||
}
|
||||
|
||||
projectSourceLine = projectSourceLine.arg(compiledProjects.join(", "));
|
||||
|
||||
friendlyErrorMessage = QString("An error occurred while loading gems.\n"
|
||||
"The enabled game project is not in the list of compiled projects.\n"
|
||||
"Please configure the enabled project to be compiled and rebuild or change the enabled project.\n"
|
||||
@@ -539,13 +543,12 @@ bool GUIApplicationManager::OnError(const char* /*window*/, const char* message)
|
||||
"%2\n"
|
||||
"Full error text:\n"
|
||||
"%3"
|
||||
).arg(enabledProject).arg(projectSourceLine).arg(message).arg(AssetUtilities::GameFolderOverrideParameter);
|
||||
).arg(enabledProject).arg(projectSourceLine).arg(message).arg(AssetUtilities::ProjectPathOverrideParameter);
|
||||
}
|
||||
}
|
||||
|
||||
if(friendlyErrorMessage.isEmpty())
|
||||
if (friendlyErrorMessage.isEmpty())
|
||||
{
|
||||
|
||||
friendlyErrorMessage = QString("An error occurred while loading gems.\n"
|
||||
"This can happen when new gems are added to a project, but those gems need to be built in order to function.\n"
|
||||
"This can also happen when switching to a different project, one which uses gems which are not yet built.\n"
|
||||
@@ -631,34 +634,18 @@ void GUIApplicationManager::CreateQtApplication()
|
||||
m_qApp = new QApplication(*m_frameworkApp.GetArgC(), *m_frameworkApp.GetArgV());
|
||||
}
|
||||
|
||||
void GUIApplicationManager::DirectoryChanged(QString path)
|
||||
void GUIApplicationManager::DirectoryChanged([[maybe_unused]] QString path)
|
||||
{
|
||||
AZ_UNUSED(path);
|
||||
QDir devRoot = ApplicationManager::GetSystemRoot();
|
||||
QString cacheRoot = devRoot.filePath("Cache");
|
||||
if (!QDir(cacheRoot).exists())
|
||||
QDir projectCacheRoot;
|
||||
AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot);
|
||||
if (!projectCacheRoot.exists() || !projectCacheRoot.exists("assetdb.sqlite"))
|
||||
{
|
||||
//Cache directory is removed we need to restart
|
||||
// If either the Cache directory or database file has been removed, we need to restart
|
||||
QTimer::singleShot(200, this, [this]()
|
||||
{
|
||||
QMetaObject::invokeMethod(this, "Restart", Qt::QueuedConnection);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
QDir projectCacheRoot;
|
||||
AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot);
|
||||
QString assetDbPath = projectCacheRoot.filePath("assetdb.sqlite");
|
||||
|
||||
if (!QFile::exists(assetDbPath))
|
||||
{
|
||||
// even if cache directory exists but the the database file is missing we need to restart
|
||||
QTimer::singleShot(200, this, [this]()
|
||||
{
|
||||
QMetaObject::invokeMethod(this, "Restart", Qt::QueuedConnection);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GUIApplicationManager::FileChanged(QString path)
|
||||
@@ -696,70 +683,6 @@ void GUIApplicationManager::FileChanged(QString path)
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (AssetUtilities::NormalizeFilePath(path).endsWith("gems.json", Qt::CaseInsensitive))
|
||||
{
|
||||
AZStd::vector<AzToolsFramework::AssetUtils::GemInfo> oldGemsList = GetPlatformConfiguration()->GetGemsInformation();
|
||||
AZStd::vector<AzToolsFramework::AssetUtils::GemInfo> newGemsList;
|
||||
QDir assetRoot;
|
||||
AssetUtilities::ComputeAssetRoot(assetRoot);
|
||||
AzToolsFramework::AssetUtils::GetGemsInfo(GetSystemRoot().absolutePath().toUtf8().constData(), assetRoot.absolutePath().toUtf8().constData(), GetGameName().toUtf8().constData(), newGemsList);
|
||||
|
||||
for (auto oldGemIter = oldGemsList.begin(); oldGemIter != oldGemsList.end();)
|
||||
{
|
||||
bool gemMatch = false;
|
||||
for (auto newGemIter = newGemsList.begin(); newGemIter != newGemsList.end();)
|
||||
{
|
||||
if (AzFramework::StringFunc::Equal(oldGemIter->m_identifier.c_str(), newGemIter->m_identifier.c_str()))
|
||||
{
|
||||
gemMatch = true;
|
||||
newGemIter = newGemsList.erase(newGemIter);
|
||||
break;
|
||||
}
|
||||
newGemIter++;
|
||||
}
|
||||
|
||||
if (gemMatch)
|
||||
{
|
||||
oldGemIter = oldGemsList.erase(oldGemIter);
|
||||
}
|
||||
else
|
||||
{
|
||||
oldGemIter++;
|
||||
}
|
||||
}
|
||||
|
||||
// oldGemslist should contain the list of gems that got removed and newGemsList should contain the list of gems that were added to the project
|
||||
// if the project requires to be built again then we will quit otherwise we can restart
|
||||
bool exitApp = false;
|
||||
for (const AzToolsFramework::AssetUtils::GemInfo& gemInfo : newGemsList)
|
||||
{
|
||||
if (!gemInfo.m_assetOnly)
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Gem %s was added to the project and require building. Quitting\n", gemInfo.m_gemName.c_str());
|
||||
exitApp = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (exitApp)
|
||||
{
|
||||
QuitRequested();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (oldGemsList.size() || newGemsList.size())
|
||||
{
|
||||
if (oldGemsList.size())
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::DebugChannel, "Gem(s) were removed from the project. Restarting\n");
|
||||
}
|
||||
else if (newGemsList.size())
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::DebugChannel, "Assets only gem(s) were added to the project. Restarting\n");
|
||||
}
|
||||
QMetaObject::invokeMethod(this, "Restart", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GUIApplicationManager::InitApplicationServer()
|
||||
|
||||
@@ -20,43 +20,48 @@ AZ_POP_DISABLE_WARNING
|
||||
#include "native/AssetDatabase/AssetDatabase.h"
|
||||
#include "native/assetprocessor.h"
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/FileTag/FileTagBus.h>
|
||||
#include <AzCore/std/string/wildcard.h>
|
||||
#include <AzCore/XML/rapidxml.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/FileTag/FileTag.h>
|
||||
#include <AzFramework/FileTag/FileTagBus.h>
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
|
||||
namespace AssetProcessor
|
||||
{
|
||||
const char* EngineFolder = "Engine";
|
||||
|
||||
AZStd::string GetXMLDependenciesFile(const AZStd::string& fullPath, const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, AZStd::string& tokenName)
|
||||
AZStd::string GetXMLDependenciesFile(const AZStd::string& fullPath, const AZStd::vector<AzFramework::GemInfo>& gemInfoList, AZStd::string& tokenName)
|
||||
{
|
||||
AZStd::string xmlDependenciesFileFullPath;
|
||||
AZ::IO::Path xmlDependenciesFileFullPath;
|
||||
tokenName = EngineFolder;
|
||||
for (const AzToolsFramework::AssetUtils::GemInfo& gemElement : gemInfoList)
|
||||
for (const AzFramework::GemInfo& gemElement : gemInfoList)
|
||||
{
|
||||
if (AzFramework::StringFunc::StartsWith(fullPath.c_str(), gemElement.m_absoluteFilePath.c_str()) || AzFramework::StringFunc::Equal(gemElement.m_absoluteFilePath.c_str(), fullPath.c_str()))
|
||||
for (const AZ::IO::Path& absoluteSourcePath : gemElement.m_absoluteSourcePaths)
|
||||
{
|
||||
AZStd::string fileName = AZStd::string::format("%s_Dependencies.xml", gemElement.m_gemName.c_str());
|
||||
AzFramework::StringFunc::Path::ConstructFull(gemElement.m_absoluteFilePath.c_str(), AzToolsFramework::AssetUtils::GemInfo::GetGemAssetFolder().c_str(), fileName.c_str() , "xml", xmlDependenciesFileFullPath);
|
||||
if (AZ::IO::FileIOBase::GetInstance()->Exists(xmlDependenciesFileFullPath.c_str()))
|
||||
if (AZ::StringFunc::StartsWith(fullPath, absoluteSourcePath.Native()) || AZ::StringFunc::Equal(absoluteSourcePath.Native(), fullPath))
|
||||
{
|
||||
tokenName = gemElement.m_gemName;
|
||||
return xmlDependenciesFileFullPath;
|
||||
xmlDependenciesFileFullPath /= AzFramework::GemInfo::GetGemAssetFolder();
|
||||
xmlDependenciesFileFullPath /= AZStd::string::format("%s_Dependencies.xml", gemElement.m_gemName.c_str());;
|
||||
if (AZ::IO::FileIOBase::GetInstance()->Exists(xmlDependenciesFileFullPath.c_str()))
|
||||
{
|
||||
tokenName = gemElement.m_gemName;
|
||||
return xmlDependenciesFileFullPath.Native();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we are here than either the %gemName%_Dependencies.xml file does not exists or the user inputted path is not inside a gems folder,
|
||||
// in both the cases we will return the engine dependencies file
|
||||
const char* devRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devroot@");
|
||||
AzFramework::StringFunc::Path::ConstructFull(devRoot, EngineFolder, "Engine_Dependencies.xml", "xml", xmlDependenciesFileFullPath);
|
||||
xmlDependenciesFileFullPath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devroot@");
|
||||
xmlDependenciesFileFullPath /= EngineFolder;
|
||||
xmlDependenciesFileFullPath /= "Engine_Dependencies.xml";
|
||||
|
||||
return xmlDependenciesFileFullPath;
|
||||
return xmlDependenciesFileFullPath.Native();
|
||||
}
|
||||
|
||||
const int MissingDependencyScanner::DefaultMaxScanIteration = 800;
|
||||
@@ -731,7 +736,7 @@ namespace AssetProcessor
|
||||
}
|
||||
}
|
||||
|
||||
bool MissingDependencyScanner::PopulateRulesForScanFolder(const AZStd::string& scanFolderPath, const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, AZStd::string& dependencyTokenName)
|
||||
bool MissingDependencyScanner::PopulateRulesForScanFolder(const AZStd::string& scanFolderPath, const AZStd::vector<AzFramework::GemInfo>& gemInfoList, AZStd::string& dependencyTokenName)
|
||||
{
|
||||
AZStd::string xmlDependenciesFullFilePath = GetXMLDependenciesFile(scanFolderPath, gemInfoList, dependencyTokenName);
|
||||
if (xmlDependenciesFullFilePath.empty())
|
||||
|
||||
@@ -131,7 +131,7 @@ namespace AssetProcessor
|
||||
|
||||
void RegisterSpecializedScanner(AZStd::shared_ptr<SpecializedDependencyScanner> scanner);
|
||||
|
||||
bool PopulateRulesForScanFolder(const AZStd::string& scanFolderPath, const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, AZStd::string& dependencyTokenName);
|
||||
bool PopulateRulesForScanFolder(const AZStd::string& scanFolderPath, const AZStd::vector<AzFramework::GemInfo>& gemInfoList, AZStd::string& dependencyTokenName);
|
||||
|
||||
protected:
|
||||
bool RunScan(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,7 @@
|
||||
#include <QVector>
|
||||
#include <QSet>
|
||||
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <native/utilities/assetUtils.h>
|
||||
#include <native/AssetManager/assetScanFolderInfo.h>
|
||||
@@ -29,10 +30,15 @@
|
||||
#include <AzToolsFramework/Asset/AssetUtils.h>
|
||||
#endif
|
||||
|
||||
class QSettings;
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class SettingsRegistryInterface;
|
||||
}
|
||||
|
||||
namespace AssetProcessor
|
||||
{
|
||||
inline constexpr const char* AssetProcessorSettingsKey{ "/Amazon/AssetProcessor/Settings" };
|
||||
class PlatformConfiguration;
|
||||
class ScanFolderInfo;
|
||||
extern const char AssetConfigPlatformDir[];
|
||||
@@ -105,6 +111,73 @@ namespace AssetProcessor
|
||||
virtual const ExcludeRecognizerContainer& GetExcludeAssetRecognizerContainer() const = 0;
|
||||
};
|
||||
|
||||
//! Visitor for reading the "/Amazon/AssetProcessor/Settings/ScanFolder *" entries from the Settings Registry
|
||||
//! Expects the key to path to the visitor to be "/Amazon/AssetProcessor/Settings"
|
||||
struct ScanFolderVisitor
|
||||
: AZ::SettingsRegistryInterface::Visitor
|
||||
{
|
||||
AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName,
|
||||
AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type) override;
|
||||
void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value) override;
|
||||
void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override;
|
||||
|
||||
struct ScanFolderInfo
|
||||
{
|
||||
AZStd::string m_scanFolderIdentifier;
|
||||
AZStd::string m_scanFolderDisplayName;
|
||||
AZ::IO::Path m_watchPath{ AZ::IO::PosixPathSeparator };
|
||||
AZStd::vector<AZStd::string> m_includeIdentifiers;
|
||||
AZStd::vector<AZStd::string> m_excludeIdentifiers;
|
||||
AZStd::string m_outputPrefix;
|
||||
int m_scanOrder{};
|
||||
bool m_isRecursive{};
|
||||
};
|
||||
AZStd::vector<ScanFolderInfo> m_scanFolderInfos;
|
||||
private:
|
||||
AZStd::stack<AZStd::string> m_scanFolderStack;
|
||||
};
|
||||
|
||||
struct ExcludeVisitor
|
||||
: AZ::SettingsRegistryInterface::Visitor
|
||||
{
|
||||
AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName,
|
||||
AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type) override;
|
||||
void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override;
|
||||
|
||||
AZStd::vector<ExcludeAssetRecognizer> m_excludeAssetRecognizers;
|
||||
private:
|
||||
AZStd::stack<AZStd::string> m_excludeNameStack;
|
||||
};
|
||||
|
||||
struct RCVisitor
|
||||
: AZ::SettingsRegistryInterface::Visitor
|
||||
{
|
||||
RCVisitor(const AZ::SettingsRegistryInterface& settingsRegistry, const AZStd::vector<AssetBuilderSDK::PlatformInfo>& enabledPlatforms)
|
||||
: m_registry(settingsRegistry)
|
||||
, m_enabledPlatforms(enabledPlatforms)
|
||||
{
|
||||
}
|
||||
AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName,
|
||||
AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type) override;
|
||||
void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, bool value) override;
|
||||
void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value) override;
|
||||
void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override;
|
||||
|
||||
struct RCAssetRecognizer
|
||||
{
|
||||
AssetRecognizer m_recognizer;
|
||||
AZStd::string m_defaultParams;
|
||||
bool m_ignore{};
|
||||
};
|
||||
AZStd::vector<RCAssetRecognizer> m_assetRecognizers;
|
||||
private:
|
||||
void ApplyParamsOverrides(AZStd::string_view path);
|
||||
|
||||
AZStd::stack<AZStd::string> m_rcNameStack;
|
||||
const AZ::SettingsRegistryInterface& m_registry;
|
||||
const AZStd::vector<AssetBuilderSDK::PlatformInfo>& m_enabledPlatforms;
|
||||
};
|
||||
|
||||
/** Reads the platform ini configuration file to determine
|
||||
* platforms for which assets needs to be build
|
||||
*/
|
||||
@@ -127,22 +200,23 @@ namespace AssetProcessor
|
||||
* Note that order of the config files is relevant - later files override settings in
|
||||
* files that are earlier.
|
||||
**/
|
||||
bool InitializeFromConfigFiles(QString absoluteSystemRoot, QString absoluteAssetRoot, QString gameName, bool addPlatformConfigs = true, bool addGemsConfigs = true);
|
||||
bool InitializeFromConfigFiles(const QString& absoluteSystemRoot, const QString& absoluteAssetRoot, const QString& projectPath, bool addPlatformConfigs = true, bool addGemsConfigs = true);
|
||||
|
||||
QString PlatformName(unsigned int platformCrc) const;
|
||||
QString RendererName(unsigned int rendererCrc) const;
|
||||
//! Merge an AssetProcessor*Config.ini path to the Settings Registry
|
||||
//! The settings are anchored underneath the AssetProcessor::AssetProcessorSettingsKey JSON pointer
|
||||
static bool MergeConfigFileToSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry, const AZ::IO::PathView& filePathView);
|
||||
|
||||
const AZStd::vector<AssetBuilderSDK::PlatformInfo>& GetEnabledPlatforms() const;
|
||||
const AssetBuilderSDK::PlatformInfo* const GetPlatformByIdentifier(const char* identifier) const;
|
||||
|
||||
//! Add AssetProcessor config files from platform specific folders
|
||||
bool AddPlatformConfigFilePaths(QStringList& configList);
|
||||
bool AddPlatformConfigFilePaths(AZStd::vector<AZ::IO::Path>& configList);
|
||||
|
||||
int MetaDataFileTypesCount() const { return m_metaDataFileTypes.count(); }
|
||||
// Metadata file types are (meta file extension, original file extension - or blank if its tacked on the end instead of replacing).
|
||||
// so for example if its
|
||||
// blah.tif + blah.tif.metadata, then its ("metadata", "")
|
||||
// but if its blah.tif + blah.metadata (rplacing tif, data is lost) then its ("metadata", "tif")
|
||||
// but if its blah.tif + blah.metadata (replacing tif, data is lost) then its ("metadata", "tif")
|
||||
QPair<QString, QString> GetMetaDataFileTypeAt(int pos) const;
|
||||
|
||||
// Metadata extensions can also be a real file, to create a dependency on file types if a specific file changes
|
||||
@@ -160,7 +234,7 @@ namespace AssetProcessor
|
||||
int GetScanFolderCount() const;
|
||||
|
||||
//! Return the gems info list
|
||||
AZStd::vector<AzToolsFramework::AssetUtils::GemInfo> GetGemsInformation() const;
|
||||
AZStd::vector<AzFramework::GemInfo> GetGemsInformation() const;
|
||||
|
||||
//! Retrieve the scan folder at a given index.
|
||||
AssetProcessor::ScanFolderInfo& GetScanFolderAt(int index);
|
||||
@@ -249,18 +323,18 @@ namespace AssetProcessor
|
||||
protected:
|
||||
|
||||
// call this first, to populate the list of platform informations
|
||||
void ReadPlatformInfosFromConfigFile(QString fileSource);
|
||||
void ReadPlatformInfosFromSettingsRegistry();
|
||||
// call this next, in order to find out what platforms are enabled
|
||||
void PopulateEnabledPlatforms(QStringList configFiles);
|
||||
// finaly, call this, in order to delete the platforminfos for non-enabled platforms
|
||||
void PopulateEnabledPlatforms();
|
||||
// finally, call this, in order to delete the platforminfos for non-enabled platforms
|
||||
void FinalizeEnabledPlatforms();
|
||||
|
||||
// iterate over all the gems and add their folders to the "scan folders" list as appropriate.
|
||||
void AddGemScanFolders(const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList);
|
||||
void AddGemScanFolders(const AZStd::vector<AzFramework::GemInfo>& gemInfoList);
|
||||
|
||||
void ReadEnabledPlatformsFromConfigFile(QString fileSource);
|
||||
bool ReadRecognizersFromConfigFile(QString fileSource, bool skipScanFolders = false, QStringList scanFolderPatterns = QStringList() );
|
||||
void ReadMetaDataFromConfigFile(QString fileSource);
|
||||
void ReadEnabledPlatformsFromSettingsRegistry();
|
||||
bool ReadRecognizersFromSettingsRegistry(const QString& assetRoot, bool skipScanFolders = false, QStringList scanFolderPatterns = QStringList() );
|
||||
void ReadMetaDataFromSettingsRegistry();
|
||||
|
||||
private:
|
||||
AZStd::vector<AssetBuilderSDK::PlatformInfo> m_enabledPlatforms;
|
||||
@@ -269,15 +343,13 @@ namespace AssetProcessor
|
||||
AZStd::vector<AssetProcessor::ScanFolderInfo> m_scanFolders;
|
||||
QList<QPair<QString, QString> > m_metaDataFileTypes;
|
||||
QSet<QString> m_metaDataRealFiles;
|
||||
AZStd::vector<AzToolsFramework::AssetUtils::GemInfo> m_gemInfoList;
|
||||
AZStd::vector<AzFramework::GemInfo> m_gemInfoList;
|
||||
|
||||
int m_minJobs = 1;
|
||||
int m_maxJobs = 3;
|
||||
|
||||
// used only during file read, keeps the total running list of all the enabled platforms from all config files and command lines
|
||||
QStringList m_tempEnabledPlatforms;
|
||||
|
||||
bool ReadRecognizerFromConfig(AssetRecognizer& target, QSettings& loader); // assumes the group is already selected
|
||||
AZStd::vector<AZStd::string> m_tempEnabledPlatforms;
|
||||
|
||||
///! if non-empty, fatalError contains the error that occurred during read.
|
||||
///! it will be printed out to the log when
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <QElapsedTimer>
|
||||
#include <QTemporaryDir>
|
||||
#include <QTextStream>
|
||||
#include <QSettings>
|
||||
#include <QTimeZone>
|
||||
#include <QRandomGenerator>
|
||||
|
||||
@@ -38,7 +37,9 @@
|
||||
|
||||
#include <AzCore/JSON/document.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/Platform/PlatformDefaults.h>
|
||||
#include <AzToolsFramework/UI/Logging/LogLine.h>
|
||||
#include <xxhash/xxhash.h>
|
||||
|
||||
@@ -77,7 +78,7 @@ namespace AssetUtilsInternal
|
||||
{
|
||||
if (waitTimeInSeconds < 0)
|
||||
{
|
||||
AZ_Warning("Asset Processor", waitTimeInSeconds >= 0, "Invalid timeout specified by the user")
|
||||
AZ_Warning("Asset Processor", waitTimeInSeconds >= 0, "Invalid timeout specified by the user");
|
||||
waitTimeInSeconds = 0;
|
||||
}
|
||||
bool failureOccurredOnce = false; // used for logging.
|
||||
@@ -152,12 +153,64 @@ namespace AssetUtilsInternal
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool DumpAssetProcessorUserSettingsToFile(AZ::SettingsRegistryInterface& settingsRegistry,
|
||||
const AZ::IO::FixedMaxPath& setregPath)
|
||||
{
|
||||
// The AssetProcessor settings are currently under the Bootstrap object(This may change in the future
|
||||
constexpr AZStd::string_view AssetProcessorUserSettingsRootKey = AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey;
|
||||
AZStd::string apSettingsJson;
|
||||
AZ::IO::ByteContainerStream apSettingsStream(&apSettingsJson);
|
||||
|
||||
AZ::SettingsRegistryMergeUtils::DumperSettings apDumperSettings;
|
||||
apDumperSettings.m_prettifyOutput = true;
|
||||
apDumperSettings.m_includeFilter = [&AssetProcessorUserSettingsRootKey](AZStd::string_view path)
|
||||
{
|
||||
// The AssetUtils only updates the following keys in the registry
|
||||
// Dump them all out to the setreg file
|
||||
auto allowedListKey = AZ::SettingsRegistryInterface::FixedValueString(AssetProcessorUserSettingsRootKey)
|
||||
+ "/allowed_list";
|
||||
auto branchTokenKey = AZ::SettingsRegistryInterface::FixedValueString(AssetProcessorUserSettingsRootKey)
|
||||
+ "/assetProcessor_branch_token";
|
||||
// The objects leading up to the keys to dump must be included in order the keys to be dumped
|
||||
return allowedListKey.starts_with(path.substr(0, allowedListKey.size()))
|
||||
|| branchTokenKey.starts_with(path.substr(0, branchTokenKey.size()));
|
||||
};
|
||||
apDumperSettings.m_jsonPointerPrefix = AssetProcessorUserSettingsRootKey;
|
||||
|
||||
if (AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(settingsRegistry, AssetProcessorUserSettingsRootKey,
|
||||
apSettingsStream, apDumperSettings))
|
||||
{
|
||||
|
||||
constexpr auto modeFlags = AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY | AZ::IO::SystemFile::SF_OPEN_CREATE
|
||||
| AZ::IO::SystemFile::SF_OPEN_CREATE_PATH;
|
||||
if (AZ::IO::SystemFile apSetregFile; apSetregFile.Open(setregPath.c_str(), modeFlags))
|
||||
{
|
||||
size_t bytesWritten = apSetregFile.Write(apSettingsJson.data(), apSettingsJson.size());
|
||||
return bytesWritten == apSettingsJson.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Unable to open AssetProcessor user setreg file (%s)\n", setregPath.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Dump of AssetProcessor User Settings failed at JSON pointer %.*s \n",
|
||||
aznumeric_cast<int>(AssetProcessorUserSettingsRootKey.size()), AssetProcessorUserSettingsRootKey.data());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
namespace AssetUtilities
|
||||
{
|
||||
constexpr AZStd::string_view AssetProcessorUserSetregRelPath = "user/Registry/asset_processor.setreg";
|
||||
|
||||
// do not place Qt objects in global scope, they allocate and refcount threaded data.
|
||||
AZ::SettingsRegistryInterface::FixedValueString s_gameName;
|
||||
AZ::SettingsRegistryInterface::FixedValueString s_projectPath;
|
||||
AZ::SettingsRegistryInterface::FixedValueString s_projectName;
|
||||
AZ::SettingsRegistryInterface::FixedValueString s_assetRoot;
|
||||
AZ::SettingsRegistryInterface::FixedValueString s_assetServerAddress;
|
||||
AZ::SettingsRegistryInterface::FixedValueString s_cachedEngineRoot;
|
||||
@@ -191,7 +244,7 @@ namespace AssetUtilities
|
||||
|
||||
void ResetGameName()
|
||||
{
|
||||
s_gameName = {};
|
||||
s_projectName = {};
|
||||
}
|
||||
|
||||
bool CopyDirectory(QDir source, QDir destination)
|
||||
@@ -245,7 +298,7 @@ namespace AssetUtilities
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ComputeAssetRoot(QDir& root, const QDir* appRootOverride)
|
||||
bool ComputeAssetRoot(QDir& root, const QDir* rootOverride)
|
||||
{
|
||||
if (!s_assetRoot.empty())
|
||||
{
|
||||
@@ -253,10 +306,10 @@ namespace AssetUtilities
|
||||
return true;
|
||||
}
|
||||
|
||||
// Use the appRoot if supplied is supplied and not an empty string
|
||||
if (appRootOverride && !appRootOverride->path().isEmpty())
|
||||
// Use the override if supplied and not an empty string
|
||||
if (rootOverride && !rootOverride->path().isEmpty())
|
||||
{
|
||||
root = *appRootOverride;
|
||||
root = *rootOverride;
|
||||
s_assetRoot = root.absolutePath().toUtf8().constData();
|
||||
return true;
|
||||
}
|
||||
@@ -288,7 +341,7 @@ namespace AssetUtilities
|
||||
return true;
|
||||
}
|
||||
|
||||
// The EngineRootFolder Key has not been found in the SettingsRegistry, log an warning about
|
||||
// The EngineRootFolder Key has not been found in the SettingsRegistry
|
||||
auto engineRootError = AZ::SettingsRegistryInterface::FixedValueString::format("The EngineRootFolder is not set in the SettingsRegistry at key %s.",
|
||||
AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
AssetProcessor::MessageInfoBus::Broadcast(&AssetProcessor::MessageInfoBusTraits::OnErrorMessage, engineRootError.c_str());
|
||||
@@ -296,7 +349,7 @@ namespace AssetUtilities
|
||||
return false;
|
||||
}
|
||||
|
||||
//! Get the engine root folder
|
||||
//! Get the external engine root folder if the engine is external to the current root folder.
|
||||
//! If the current root folder is also the engine folder, then this behaves the same as ComputeEngineRoot
|
||||
bool ComputeEngineRoot(QDir& root, const QDir* engineRootOverride)
|
||||
{
|
||||
@@ -312,6 +365,7 @@ namespace AssetUtilities
|
||||
AssetUtilities::ComputeAssetRoot(root, engineRootOverride);
|
||||
}
|
||||
|
||||
AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
// Use the engineRootOverride if supplied and not empty
|
||||
if (engineRootOverride && !engineRootOverride->path().isEmpty())
|
||||
{
|
||||
@@ -320,11 +374,11 @@ namespace AssetUtilities
|
||||
return true;
|
||||
}
|
||||
|
||||
AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
if (settingsRegistry == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPathString engineRootFolder;
|
||||
if (settingsRegistry->Get(engineRootFolder, AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
|
||||
{
|
||||
@@ -336,7 +390,7 @@ namespace AssetUtilities
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MakeFileWritable(QString fileName)
|
||||
bool MakeFileWritable(const QString& fileName)
|
||||
{
|
||||
#if defined WIN32
|
||||
DWORD fileAttributes = GetFileAttributesA(fileName.toUtf8());
|
||||
@@ -386,7 +440,7 @@ namespace AssetUtilities
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CheckCanLock(QString fileName)
|
||||
bool CheckCanLock(const QString& fileName)
|
||||
{
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
AZStd::wstring usableFileName;
|
||||
@@ -419,42 +473,55 @@ namespace AssetUtilities
|
||||
#endif
|
||||
}
|
||||
|
||||
QString ComputeGameName(QString gameNameOverride, bool force)
|
||||
QString ComputeProjectName(QString gameNameOverride, bool force)
|
||||
{
|
||||
if (force || s_gameName.empty())
|
||||
if (force || s_projectName.empty())
|
||||
{
|
||||
// if its been specified on the command line, then ignore bootstrap:
|
||||
// Override Game Name if a non-empty override string has been supplied
|
||||
if (!gameNameOverride.isEmpty())
|
||||
{
|
||||
s_projectName = gameNameOverride.toUtf8().constData();
|
||||
}
|
||||
else
|
||||
{
|
||||
s_projectName = AZ::Utils::GetProjectName();
|
||||
}
|
||||
}
|
||||
|
||||
return QString::fromUtf8(s_projectName.c_str(), aznumeric_cast<int>(s_projectName.size()));
|
||||
}
|
||||
|
||||
QString ComputeProjectPath()
|
||||
{
|
||||
if (s_projectPath.empty())
|
||||
{
|
||||
// Check command-line args first
|
||||
QStringList args = QCoreApplication::arguments();
|
||||
for (QString arg : args)
|
||||
{
|
||||
if (arg.contains(QString("/%1=").arg(GameFolderOverrideParameter), Qt::CaseInsensitive) || arg.contains(QString("--%1=").arg(GameFolderOverrideParameter), Qt::CaseInsensitive))
|
||||
if (arg.contains(QString("/%1=").arg(ProjectPathOverrideParameter), Qt::CaseInsensitive)
|
||||
|| arg.contains(QString("--%1=").arg(ProjectPathOverrideParameter), Qt::CaseInsensitive))
|
||||
{
|
||||
QString rawValueString = arg.split("=")[1].trimmed();
|
||||
if (!rawValueString.isEmpty())
|
||||
{
|
||||
s_gameName = rawValueString.toUtf8().constData();
|
||||
return rawValueString;
|
||||
QDir path(rawValueString);
|
||||
if (path.isAbsolute())
|
||||
{
|
||||
s_projectPath = rawValueString.toUtf8().constData();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Override Game Name if a non-empty override string has been supplied
|
||||
if (!gameNameOverride.isEmpty())
|
||||
{
|
||||
s_gameName = gameNameOverride.toUtf8().constData();
|
||||
}
|
||||
else
|
||||
{
|
||||
QDir engineRoot;
|
||||
if (!AssetUtilities::ComputeEngineRoot(engineRoot))
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
s_gameName = ReadGameNameFromSettingsRegistry(engineRoot.absolutePath()).toUtf8().constData();
|
||||
}
|
||||
}
|
||||
|
||||
return QString::fromUtf8(s_gameName.c_str(), aznumeric_cast<int>(s_gameName.size()));
|
||||
if (s_projectPath.empty())
|
||||
{
|
||||
s_projectPath = AZ::Utils::GetProjectPath();
|
||||
}
|
||||
|
||||
return QString::fromUtf8(s_projectPath.c_str(), aznumeric_cast<int>(s_projectPath.size()));
|
||||
}
|
||||
|
||||
bool InServerMode()
|
||||
@@ -479,7 +546,7 @@ namespace AssetUtilities
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, false, "Invalid server address, please check the AssetProcessorPlatformConfig.ini file \
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, false, "Invalid server address, please check the AssetProcessorPlatformConfig.setreg file \
|
||||
to ensure that the address is correct. Asset Processor won't be running in server mode.");
|
||||
}
|
||||
|
||||
@@ -517,19 +584,18 @@ to ensure that the address is correct. Asset Processor won't be running in serve
|
||||
}
|
||||
}
|
||||
|
||||
QDir engineRoot;
|
||||
ComputeEngineRoot(engineRoot);
|
||||
QString rootConfigFile = engineRoot.absoluteFilePath("AssetProcessorPlatformConfig.ini");
|
||||
|
||||
if (QFile::exists(rootConfigFile))
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
if (settingsRegistry)
|
||||
{
|
||||
QString address;
|
||||
QSettings loader(rootConfigFile, QSettings::IniFormat);
|
||||
loader.beginGroup("Server");
|
||||
address = loader.value("cacheServerAddress", QString()).toString();
|
||||
loader.endGroup();
|
||||
s_assetServerAddress = address.toUtf8().constData();
|
||||
return address;
|
||||
AZStd::string address;
|
||||
if (settingsRegistry->Get(address, AZ::SettingsRegistryInterface::FixedValueString(AssetProcessor::AssetProcessorSettingsKey)
|
||||
+ "/Server/cacheServerAddress"))
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::DebugChannel, "Server Address: %s\n", address.c_str());
|
||||
}
|
||||
s_assetServerAddress = address;
|
||||
|
||||
return QString::fromUtf8(address.data(), aznumeric_cast<int>(address.size()));
|
||||
}
|
||||
|
||||
return QString();
|
||||
@@ -549,18 +615,12 @@ to ensure that the address is correct. Asset Processor won't be running in serve
|
||||
return *s_fileHashSetting;
|
||||
}
|
||||
|
||||
QDir engineRoot;
|
||||
ComputeEngineRoot(engineRoot);
|
||||
QString rootConfigFile = engineRoot.absoluteFilePath("AssetProcessorPlatformConfig.ini");
|
||||
|
||||
if (QFile::exists(rootConfigFile))
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
if (settingsRegistry)
|
||||
{
|
||||
bool curValue;
|
||||
QSettings loader(rootConfigFile, QSettings::IniFormat);
|
||||
loader.beginGroup("Fingerprinting");
|
||||
curValue = loader.value("UseFileHashing", true).toBool();
|
||||
loader.endGroup();
|
||||
|
||||
bool curValue = true;
|
||||
settingsRegistry->Get(curValue, AZ::SettingsRegistryInterface::FixedValueString(AssetProcessor::AssetProcessorSettingsKey)
|
||||
+ "/Fingerprinting/UseFileHashing");
|
||||
AZ_TracePrintf(AssetProcessor::DebugChannel, "UseFileHashing: %s\n", curValue ? "True" : "False");
|
||||
s_fileHashSetting = curValue;
|
||||
|
||||
@@ -573,46 +633,8 @@ to ensure that the address is correct. Asset Processor won't be running in serve
|
||||
return *s_fileHashSetting;
|
||||
}
|
||||
|
||||
QString ReadGameNameFromSettingsRegistry(QString initialFolder /*= QString()*/)
|
||||
QString ReadAllowedlistFromSettingsRegistry([[maybe_unused]] QString initialFolder)
|
||||
{
|
||||
if (initialFolder.isEmpty())
|
||||
{
|
||||
QDir engineRoot;
|
||||
if (!AssetUtilities::ComputeEngineRoot(engineRoot))
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
initialFolder = engineRoot.absolutePath();
|
||||
}
|
||||
|
||||
constexpr size_t BufferSize = AZ_ARRAY_SIZE(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + AZStd::char_traits<char>::length("/sys_game_folder");
|
||||
AZStd::fixed_string<BufferSize> projectKey{ AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey };
|
||||
projectKey += "/sys_game_folder";
|
||||
|
||||
AZ::SettingsRegistryInterface::FixedValueString projectName;
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry && settingsRegistry->Get(projectName, projectKey))
|
||||
{
|
||||
return QString::fromUtf8(projectName.c_str(), aznumeric_cast<int>(projectName.size()));
|
||||
}
|
||||
|
||||
AZ_Warning("AssetUtils", false, "Unable to find the Project Name(sys_game_folder) key in the settings registry");
|
||||
return {};
|
||||
}
|
||||
|
||||
QString ReadAllowedlistFromSettingsRegistry(QString initialFolder /*= QString()*/)
|
||||
{
|
||||
if (initialFolder.isEmpty())
|
||||
{
|
||||
QDir assetRoot;
|
||||
if (!AssetUtilities::ComputeAssetRoot(assetRoot))
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
initialFolder = assetRoot.absolutePath();
|
||||
}
|
||||
|
||||
constexpr size_t BufferSize = AZ_ARRAY_SIZE(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + AZStd::char_traits<char>::length("/allowed_list");
|
||||
AZStd::fixed_string<BufferSize> allowedListKey{ AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey };
|
||||
allowedListKey += "/allowed_list";
|
||||
@@ -626,18 +648,8 @@ to ensure that the address is correct. Asset Processor won't be running in serve
|
||||
return {};
|
||||
}
|
||||
|
||||
QString ReadRemoteIpFromSettingsRegistry(QString initialFolder /*= QString()*/)
|
||||
QString ReadRemoteIpFromSettingsRegistry([[maybe_unused]] QString initialFolder)
|
||||
{
|
||||
if (initialFolder.isEmpty())
|
||||
{
|
||||
QDir engineRoot;
|
||||
if (!AssetUtilities::ComputeEngineRoot(engineRoot))
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
initialFolder = engineRoot.absolutePath();
|
||||
}
|
||||
constexpr size_t BufferSize = AZ_ARRAY_SIZE(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + AZStd::char_traits<char>::length("/remote_ip");
|
||||
AZStd::fixed_string<BufferSize> remoteIpKey{ AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey };
|
||||
remoteIpKey += "/remote_ip";
|
||||
@@ -651,159 +663,44 @@ to ensure that the address is correct. Asset Processor won't be running in serve
|
||||
return {};
|
||||
}
|
||||
|
||||
bool WriteAllowedlistToBootstrap(QStringList newAllowedList)
|
||||
bool WriteAllowedlistToSettingsRegistry(const QStringList& newAllowedList)
|
||||
{
|
||||
QDir assetRoot;
|
||||
ComputeAssetRoot(assetRoot);
|
||||
QString bootstrapFilename = assetRoot.filePath("bootstrap.cfg");
|
||||
QFile bootstrapFile(bootstrapFilename);
|
||||
AZ::IO::FixedMaxPath assetProcessorUserSetregPath = AZ::Utils::GetProjectPath();
|
||||
assetProcessorUserSetregPath /= AssetProcessorUserSetregRelPath;
|
||||
|
||||
// do not alter the branch file unless we are able to obtain an exclusive lock. Other apps (such as NPP) may actually write 0 bytes first, then slowly spool out the remainder)
|
||||
if (!CheckCanLock(bootstrapFilename))
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
if (!settingsRegistry)
|
||||
{
|
||||
AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable access Settings Registry. Branch Token cannot be updated");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bootstrapFile.open(QIODevice::ReadOnly))
|
||||
auto allowedListKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey)
|
||||
+ "/allowed_list";
|
||||
AZStd::string currentAllowedList;
|
||||
if (settingsRegistry->Get(currentAllowedList, allowedListKey))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Split the current allowedList into an array and compare against the new allowed list
|
||||
AZStd::vector<AZStd::string_view> allowedListArray;
|
||||
auto AppendAllowedIpTokens = [&allowedListArray](AZStd::string_view token) { allowedListArray.emplace_back(token); };
|
||||
AZ::StringFunc::TokenizeVisitor(currentAllowedList, AppendAllowedIpTokens, ',');
|
||||
|
||||
// regexp that matches either the beginning of the file, some whitespace, and allowed_list, or,
|
||||
// matches a newline, then whitespace, then allowed_list it will not match comments.
|
||||
QRegExp allowedListPattern("(^|\\n)\\s*allowed_list\\s*=\\s*(.+)", Qt::CaseInsensitive, QRegExp::RegExp);
|
||||
|
||||
//read the file line by line and try to find the allowed_list line
|
||||
QString readAllowedList;
|
||||
QString allowedListline;
|
||||
while (!bootstrapFile.atEnd())
|
||||
{
|
||||
QString contents(bootstrapFile.readLine());
|
||||
int matchIdx = allowedListPattern.indexIn(contents);
|
||||
if (matchIdx != -1)
|
||||
auto CompareQListToAzVector = [](AZStd::string_view currentAllowedIp, const QString& newAllowedIp)
|
||||
{
|
||||
allowedListline = contents;
|
||||
readAllowedList = allowedListPattern.cap(2);
|
||||
break;
|
||||
return currentAllowedIp == newAllowedIp.toUtf8().constData();
|
||||
};
|
||||
if (AZStd::equal(allowedListArray.begin(), allowedListArray.end(), newAllowedList.begin(), newAllowedList.end(), CompareQListToAzVector))
|
||||
{
|
||||
// no need to update, remote_ip already matches
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//read the entire file into so we can do a buffer replacement
|
||||
bootstrapFile.seek(0);
|
||||
QString fileContents;
|
||||
fileContents = bootstrapFile.readAll();
|
||||
bootstrapFile.close();
|
||||
// Update Settings Registry with new token
|
||||
AZStd::string azNewAllowedList{ newAllowedList.join(', ').toUtf8().constData() };
|
||||
settingsRegistry->Set(allowedListKey, azNewAllowedList);
|
||||
|
||||
//format the new allowed list
|
||||
QString formattedNewAllowedList = newAllowedList.join(", ");
|
||||
|
||||
//if we didn't find a allowed_list entry then append one
|
||||
if (allowedListline.isEmpty())
|
||||
{
|
||||
fileContents.append("\nallowed_list = " + formattedNewAllowedList + "\n");
|
||||
}
|
||||
else if (QString::compare(formattedNewAllowedList, readAllowedList, Qt::CaseInsensitive) == 0)
|
||||
{
|
||||
// no need to update, they match
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Replace the found line with a new one
|
||||
fileContents.replace(allowedListline, "allowed_list = " + formattedNewAllowedList + "\n");
|
||||
}
|
||||
|
||||
// Make the bootstrap file writable
|
||||
if (!MakeFileWritable(bootstrapFilename))
|
||||
{
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, false, "Failed to make the bootstrap file writable.")
|
||||
return false;
|
||||
}
|
||||
if (!bootstrapFile.open(QIODevice::WriteOnly))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QTextStream output(&bootstrapFile);
|
||||
output << fileContents;
|
||||
bootstrapFile.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteRemoteIpToBootstrap(QString newRemoteIp)
|
||||
{
|
||||
QDir assetRoot;
|
||||
ComputeAssetRoot(assetRoot);
|
||||
QString bootstrapFilename = assetRoot.filePath("bootstrap.cfg");
|
||||
QFile bootstrapFile(bootstrapFilename);
|
||||
|
||||
// do not alter the branch file unless we are able to obtain an exclusive lock. Other apps (such as NPP) may actually write 0 bytes first, then slowly spool out the remainder)
|
||||
if (!CheckCanLock(bootstrapFilename))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bootstrapFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// regexp that matches either the beginning of the file, and remote_ip, or,
|
||||
// matches a newline, then whitespace, then remote_ip it will not match comments.
|
||||
QRegExp remoteIpPattern("(^|\\n)\\s*remote_ip\\s*=\\s*(.+)", Qt::CaseInsensitive, QRegExp::RegExp);
|
||||
|
||||
//read the file line by line and try to find the remote_ip line
|
||||
QString readRemoteIp;
|
||||
QString remoteIpline;
|
||||
while (!bootstrapFile.atEnd())
|
||||
{
|
||||
QString contents(bootstrapFile.readLine());
|
||||
int matchIdx = remoteIpPattern.indexIn(contents);
|
||||
if (matchIdx != -1)
|
||||
{
|
||||
remoteIpline = contents;
|
||||
readRemoteIp = remoteIpPattern.cap(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//read the entire file into so we can do a buffer replacement
|
||||
bootstrapFile.seek(0);
|
||||
QString fileContents;
|
||||
fileContents = bootstrapFile.readAll();
|
||||
bootstrapFile.close();
|
||||
|
||||
//if we didn't find a remote_ip entry then append one
|
||||
if (remoteIpline.isEmpty())
|
||||
{
|
||||
fileContents.append("\nremote_ip = " + newRemoteIp + "\n");
|
||||
}
|
||||
else if (QString::compare(newRemoteIp, readRemoteIp, Qt::CaseInsensitive) == 0)
|
||||
{
|
||||
// no need to update, they match
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Replace the found line with a new one
|
||||
fileContents.replace(remoteIpline, "remote_ip = " + newRemoteIp + "\n");
|
||||
}
|
||||
|
||||
// Make the bootstrap file writable
|
||||
if (!MakeFileWritable(bootstrapFilename))
|
||||
{
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, false, "Failed to make the bootstrap file writable.")
|
||||
return false;
|
||||
}
|
||||
if (!bootstrapFile.open(QIODevice::WriteOnly))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QTextStream output(&bootstrapFile);
|
||||
output << fileContents;
|
||||
bootstrapFile.close();
|
||||
return true;
|
||||
return AssetUtilsInternal::DumpAssetProcessorUserSettingsToFile(*settingsRegistry, assetProcessorUserSetregPath);
|
||||
}
|
||||
|
||||
quint16 ReadListeningPortFromSettingsRegistry(QString initialFolder /*= QString()*/)
|
||||
@@ -923,23 +820,20 @@ to ensure that the address is correct. Asset Processor won't be running in serve
|
||||
|
||||
bool ComputeProjectCacheRoot(QDir& projectCacheRoot)
|
||||
{
|
||||
QDir assetRoot;
|
||||
if (!ComputeAssetRoot(assetRoot))
|
||||
if (auto registry = AZ::SettingsRegistry::Get(); registry != nullptr)
|
||||
{
|
||||
return false; // failed to detect engine root
|
||||
AZ::SettingsRegistryInterface::FixedValueString projectCacheRootValue;
|
||||
if (registry->Get(projectCacheRootValue, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder);
|
||||
!projectCacheRootValue.empty())
|
||||
{
|
||||
projectCacheRoot = QDir(QString::fromUtf8(projectCacheRootValue.c_str(), aznumeric_cast<int>(projectCacheRootValue.size())));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
QString gameDir = ComputeGameName(assetRoot.absolutePath());
|
||||
if (gameDir.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
projectCacheRoot = QDir(assetRoot.filePath("Cache/" + gameDir));
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool ComputeFenceDirectory(QDir& fenceDir)
|
||||
{
|
||||
QDir cacheRoot;
|
||||
@@ -951,6 +845,25 @@ to ensure that the address is correct. Asset Processor won't be running in serve
|
||||
return true;
|
||||
}
|
||||
|
||||
QString StripAssetPlatform(AZStd::string_view relativeProductPath)
|
||||
{
|
||||
// Skip over the assetPlatform path segment if it is matches one of the platform defaults
|
||||
// Otherwise return the path unchanged
|
||||
AZStd::string_view strippedProductPath{ relativeProductPath };
|
||||
if (AZStd::optional pathSegment = AZ::StringFunc::TokenizeNext(strippedProductPath, AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR);
|
||||
pathSegment.has_value())
|
||||
{
|
||||
AZ::IO::FixedMaxPathString assetPlatformSegmentLower{ *pathSegment };
|
||||
AZStd::to_lower(assetPlatformSegmentLower.begin(), assetPlatformSegmentLower.end());
|
||||
if (AzFramework::PlatformHelper::GetPlatformIdFromName(assetPlatformSegmentLower) != AzFramework::PlatformId::Invalid)
|
||||
{
|
||||
return QString::fromUtf8(strippedProductPath.data(), aznumeric_cast<int>(strippedProductPath.size()));
|
||||
}
|
||||
}
|
||||
|
||||
return QString::fromUtf8(relativeProductPath.data(), aznumeric_cast<int>(relativeProductPath.size()));
|
||||
}
|
||||
|
||||
QString NormalizeFilePath(const QString& filePath)
|
||||
{
|
||||
// do NOT convert to absolute paths here, we just want to manipulate the string itself.
|
||||
@@ -1035,92 +948,41 @@ to ensure that the address is correct. Asset Processor won't be running in serve
|
||||
|
||||
bool UpdateBranchToken()
|
||||
{
|
||||
QDir assetRoot;
|
||||
ComputeAssetRoot(assetRoot);
|
||||
QString bootstrapFilename = assetRoot.filePath("bootstrap.cfg");
|
||||
QFile bootstrapFile(bootstrapFilename);
|
||||
QString fileContents;
|
||||
|
||||
// do not alter the branch file unless we are able to obtain an exclusive lock. Other apps (such as NPP) may actually write 0 bytes first, then slowly spool out the remainder)
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
bool hasLock = false;
|
||||
do
|
||||
{
|
||||
if (CheckCanLock(bootstrapFilename))
|
||||
{
|
||||
hasLock = true;
|
||||
break;
|
||||
}
|
||||
|
||||
QThread::msleep(AssetUtilsInternal::g_RetryWaitInterval);
|
||||
|
||||
} while (!timer.hasExpired(10 * AssetUtilsInternal::g_RetryWaitInterval));
|
||||
if (!hasLock)
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Unable to lock bootstrap file at: %s\n", bootstrapFilename.toUtf8().constData());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bootstrapFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Unable to open bootstrap file at: %s\n", bootstrapFilename.toUtf8().constData());
|
||||
return false;
|
||||
}
|
||||
|
||||
fileContents = bootstrapFile.readAll();
|
||||
bootstrapFile.close();
|
||||
AZ::IO::FixedMaxPath assetProcessorUserSetregPath = AZ::Utils::GetProjectPath();
|
||||
assetProcessorUserSetregPath /= AssetProcessorUserSetregRelPath;
|
||||
|
||||
AZStd::string appBranchToken;
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForAppRoot, appBranchToken);
|
||||
QString currentBranchToken(appBranchToken.c_str());
|
||||
QString readBranchToken;
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForEngineRoot, appBranchToken);
|
||||
|
||||
// regexp that matches either the beginning of the file, some whitespace, and assetProcessor_branch_token, or,
|
||||
// matches a newline, then whitespace, then assetProcessor_branch_token
|
||||
// it will not match comments.
|
||||
QRegExp branchTokenPattern("(^|\\n)\\s*assetProcessor_branch_token\\s*=\\s*(\\S+)\\b", Qt::CaseInsensitive, QRegExp::RegExp);
|
||||
|
||||
int matchIdx = branchTokenPattern.indexIn(fileContents);
|
||||
if (matchIdx != -1)
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
if (!settingsRegistry)
|
||||
{
|
||||
readBranchToken = branchTokenPattern.cap(2);
|
||||
AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable access Settings Registry. Branch Token cannot be updated");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (readBranchToken.isEmpty())
|
||||
AZStd::string registryBranchToken;
|
||||
auto branchTokenKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/assetProcessor_branch_token";
|
||||
if (settingsRegistry->Get(registryBranchToken, branchTokenKey))
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "adding branch token (%s) in (%s)\n", currentBranchToken.toUtf8().constData(), bootstrapFilename.toUtf8().constData());
|
||||
fileContents.append("\nassetProcessor_branch_token = " + currentBranchToken + "\n");
|
||||
}
|
||||
else if (QString::compare(currentBranchToken, readBranchToken, Qt::CaseInsensitive) == 0)
|
||||
{
|
||||
// no need to update, branch token match
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Branch token (%s) is already correct in (%s)\n", currentBranchToken.toUtf8().constData(), bootstrapFilename.toUtf8().constData());
|
||||
return true;
|
||||
if (appBranchToken == registryBranchToken)
|
||||
{
|
||||
// no need to update, branch token match
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Branch token (%s) is already correct in (%s)\n", appBranchToken.c_str(), assetProcessorUserSetregPath.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Updating branch token (%s) in (%s)\n", appBranchToken.c_str(), assetProcessorUserSetregPath.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
//Updating branch token
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Updating branch token (%s) in (%s)\n", currentBranchToken.toUtf8().constData(), bootstrapFilename.toUtf8().constData());
|
||||
fileContents.replace(branchTokenPattern.cap(0), "\nassetProcessor_branch_token = " + currentBranchToken + "\n");
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Adding branch token (%s) in (%s)\n", appBranchToken.c_str(), assetProcessorUserSetregPath.c_str());
|
||||
}
|
||||
|
||||
// Make the bootstrap file writable
|
||||
if (!MakeFileWritable(bootstrapFilename))
|
||||
{
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, false, "Failed to make the bootstrap file writable.")
|
||||
return false;
|
||||
}
|
||||
if (!bootstrapFile.open(QIODevice::WriteOnly))
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Unable to open bootstrap file (%s)\n", bootstrapFilename.toUtf8().constData());
|
||||
return false;
|
||||
}
|
||||
// Update Settings Registry with new token
|
||||
settingsRegistry->Set(branchTokenKey, appBranchToken);
|
||||
|
||||
QTextStream output(&bootstrapFile);
|
||||
output << fileContents;
|
||||
bootstrapFile.close();
|
||||
return true;
|
||||
return AssetUtilsInternal::DumpAssetProcessorUserSettingsToFile(*settingsRegistry, assetProcessorUserSetregPath);
|
||||
}
|
||||
|
||||
QString ComputeJobDescription(const AssetProcessor::AssetRecognizer* recognizer)
|
||||
@@ -1130,7 +992,7 @@ to ensure that the address is correct. Asset Processor won't be running in serve
|
||||
|
||||
AZStd::string ComputeJobLogFolder()
|
||||
{
|
||||
return AZStd::string::format("@log@/logs/JobLogs");
|
||||
return AZStd::string::format("@log@/JobLogs");
|
||||
}
|
||||
|
||||
AZStd::string ComputeJobLogFileName(const AzToolsFramework::AssetSystem::JobInfo& jobInfo)
|
||||
@@ -1477,14 +1339,28 @@ to ensure that the address is correct. Asset Processor won't be running in serve
|
||||
|
||||
bool CreateTempWorkspace(QString& result)
|
||||
{
|
||||
// Use the engine root as a temp workspace folder
|
||||
// this works better for numerous reasons
|
||||
// * Its on the same drive as the /Cache/ so we will be moving files instead of copying from drive to drive
|
||||
// Use the project user folder as a temp workspace folder
|
||||
// The benefits are
|
||||
// * It's on the same drive as the Cache/ so we will be moving files instead of copying from drive to drive
|
||||
// * It is discoverable by the user and thus deletable and we can also tell people to send us that folder without them having to go digging for it
|
||||
// * If you can't write to it you have much bigger problems
|
||||
|
||||
QDir rootDir;
|
||||
if (ComputeAssetRoot(rootDir))
|
||||
bool foundValidPath{};
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
if (AZ::IO::Path userPath; settingsRegistry->Get(userPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectUserPath))
|
||||
{
|
||||
rootDir.setPath(QString::fromUtf8(userPath.c_str(), aznumeric_cast<int>(userPath.Native().size())));
|
||||
foundValidPath = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundValidPath)
|
||||
{
|
||||
foundValidPath = ComputeAssetRoot(rootDir);
|
||||
}
|
||||
|
||||
if (foundValidPath)
|
||||
{
|
||||
QString tempPath = rootDir.absolutePath();
|
||||
return CreateTempWorkspace(tempPath, result);
|
||||
@@ -1499,7 +1375,6 @@ to ensure that the address is correct. Asset Processor won't be running in serve
|
||||
QString inputName;
|
||||
QString platformName;
|
||||
QString jobDescription;
|
||||
QString gameName = AssetUtilities::ComputeGameName();
|
||||
AZ::Uuid guid = AZ::Uuid::CreateNull();
|
||||
|
||||
using namespace AzToolsFramework::AssetDatabase;
|
||||
@@ -1513,16 +1388,16 @@ to ensure that the address is correct. Asset Processor won't be running in serve
|
||||
platform = AzToolsFramework::AssetSystem::GetHostAssetPlatform();
|
||||
}
|
||||
|
||||
QString platformPrepend = QString("%1/%2/").arg(platform, gameName);
|
||||
QString productNameWithPlatformAndGameName = productName;
|
||||
QString platformPrepend = QString("%1/").arg(platform);
|
||||
QString productNameWithPlatform = productName;
|
||||
|
||||
if (!productName.startsWith(platformPrepend, Qt::CaseInsensitive))
|
||||
{
|
||||
productNameWithPlatformAndGameName = productName = QString("%1/%2/%3").arg(platform, gameName, productName);
|
||||
productNameWithPlatform = productName = QString("%1/%2").arg(platform, productName);
|
||||
}
|
||||
|
||||
ProductDatabaseEntryContainer products;
|
||||
if (databaseConnection->GetProductsByProductName(productNameWithPlatformAndGameName, products))
|
||||
if (databaseConnection->GetProductsByProductName(productNameWithPlatform, products))
|
||||
{
|
||||
// if we find stuff, then return immediately, productName is already a productName.
|
||||
return productName;
|
||||
@@ -1535,24 +1410,9 @@ to ensure that the address is correct. Asset Processor won't be running in serve
|
||||
return productName;
|
||||
}
|
||||
|
||||
if (!databaseConnection->GetProductsLikeProductName(productNameWithPlatformAndGameName, AssetDatabaseConnection::LikeType::StartsWith, products))
|
||||
if (!databaseConnection->GetProductsLikeProductName(productNameWithPlatform, AssetDatabaseConnection::LikeType::StartsWith, products))
|
||||
{
|
||||
//if we are here it means that the asset database does not know about this product,
|
||||
//we will now remove the gameName and try again ,so now the path will only have $PLATFORM/ in front of it
|
||||
int gameNameIndex = productName.indexOf(gameName, 0, Qt::CaseInsensitive);
|
||||
|
||||
if (gameNameIndex != -1)
|
||||
{
|
||||
//we will now remove the gameName and the separator
|
||||
productName.remove(gameNameIndex, gameName.length() + 1);// adding one for the native separator
|
||||
}
|
||||
|
||||
//Search the database for this product
|
||||
if (!databaseConnection->GetProductsLikeProductName(productName, AssetDatabaseConnection::LikeType::StartsWith, products))
|
||||
{
|
||||
//return empty string if the database still does not have any idea about the product
|
||||
productName = QString();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
return productName.toLower();
|
||||
}
|
||||
|
||||
@@ -51,8 +51,7 @@ namespace AssetProcessor
|
||||
|
||||
namespace AssetUtilities
|
||||
{
|
||||
|
||||
inline constexpr char GameFolderOverrideParameter[] = "gamefolder";
|
||||
inline constexpr char ProjectPathOverrideParameter[] = "project-path";
|
||||
|
||||
//! Set precision fingerprint timestamps will be truncated to avoid mismatches across systems/packaging with different file timestamp precisions
|
||||
//! Timestamps default to milliseconds. A value of 1 will keep the default millisecond precision. A value of 1000 will reduce the precision to seconds
|
||||
@@ -79,10 +78,10 @@ namespace AssetUtilities
|
||||
|
||||
//! makes the file writable
|
||||
//! return true if operation is successful, otherwise return false
|
||||
bool MakeFileWritable(QString filename);
|
||||
bool MakeFileWritable(const QString& filename);
|
||||
|
||||
//! Check to see if we can Lock the file
|
||||
bool CheckCanLock(QString filename);
|
||||
bool CheckCanLock(const QString& filename);
|
||||
|
||||
//! Updates the branch token in the bootstrap file
|
||||
bool UpdateBranchToken();
|
||||
@@ -98,11 +97,14 @@ namespace AssetUtilities
|
||||
|
||||
bool ShouldUseFileHashing();
|
||||
|
||||
//! Determine the name of the current game - for example, SamplesProject
|
||||
//! Can be overridden by passing in a non-empty gameNameOverride
|
||||
//! The override will persist if the GameName wasn't set previously or
|
||||
//! Determine the name of the current project - for example, SamplesProject
|
||||
//! Can be overridden by passing in a non-empty projectNameOverride
|
||||
//! The override will persist if the project name wasn't set previously or
|
||||
//! force=true is supplied
|
||||
QString ComputeGameName(QString gameNameOverride = QString(), bool force = false);
|
||||
QString ComputeProjectName(QString projectNameOverride = QString(), bool force = false);
|
||||
|
||||
//! Determine the absolute path of the current project
|
||||
QString ComputeProjectPath();
|
||||
|
||||
//! Reads the allowed list directly from the bootstrap file
|
||||
QString ReadAllowedlistFromSettingsRegistry(QString initialFolder = QString());
|
||||
@@ -111,13 +113,7 @@ namespace AssetUtilities
|
||||
QString ReadRemoteIpFromSettingsRegistry(QString initialFolder = QString());
|
||||
|
||||
//! Writes the allowed list directly to the bootstrap file
|
||||
bool WriteAllowedlistToBootstrap(QStringList allowedList);
|
||||
|
||||
//! Writes the remote ip directly to the bootstrap file
|
||||
bool WriteRemoteIpToBootstrap(QString remoteIp);
|
||||
|
||||
//! Reads the game name directly from the bootstrap file
|
||||
QString ReadGameNameFromSettingsRegistry(QString initialFolder = QString());
|
||||
bool WriteAllowedlistToSettingsRegistry(const QStringList& allowedList);
|
||||
|
||||
//! Reads the listening port from the bootstrap file
|
||||
//! By default the listening port is 45643
|
||||
@@ -143,13 +139,24 @@ namespace AssetUtilities
|
||||
QString ComputeJobDescription(const AssetProcessor::AssetRecognizer* recognizer);
|
||||
|
||||
//! Compute the root of the cache for the current project.
|
||||
//! This is generally the "cache" folder, subfolder gamedir.
|
||||
//! This is generally the "<Project>/Cache" folder
|
||||
bool ComputeProjectCacheRoot(QDir& projectCacheRoot);
|
||||
|
||||
//! Compute the folder that will be used for fence files.
|
||||
bool ComputeFenceDirectory(QDir& fenceDir);
|
||||
|
||||
//! Converts all slashes to forward slashes, removes double slashes,
|
||||
//! Strips the first "asset platform" from the first path segment of a relative product path
|
||||
//! This is meant for removing the asset platform for paths such as "pc/MyAssetFolder/MyAsset.asset"
|
||||
//! Therefore the result here becomes "MyAssetFolder/MyAsset"
|
||||
//!
|
||||
//! Similarly invoking this function on relative path that begins with the "server" platform
|
||||
//! "server/AssetFolder/Server.asset2" -> "AssetFolder/Server.asset2"
|
||||
//! This function does not strip an asset platform from anywhere, but the first path segment
|
||||
//! Therefore invoking strip Asset on "MyProject/Cache/pc/MyAsset/MyAsset.asset"
|
||||
//! would return a copy of the relative path
|
||||
QString StripAssetPlatform(AZStd::string_view relativeProductPath);
|
||||
|
||||
//! Converts all slashes to forward slashes, removes double slashes,
|
||||
//! replaces all indirections such as '.' or '..' as appropriate.
|
||||
//! On windows, the drive letter (if present) is converted to uppercase.
|
||||
//! Besides that, all case is preserved.
|
||||
|
||||
Vendored
+1
-1
@@ -11,7 +11,7 @@ es3=enabled ; note - bad platform - its not one of the above ones!
|
||||
|
||||
; without a scan folder, it is an invalid file.
|
||||
[ScanFolder Game]
|
||||
watch=@ROOT@/@GAMENAME@
|
||||
watch=@PROJECTROOT@
|
||||
recursive=1
|
||||
order=0
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
; without a scan folder, it is an invalid file.
|
||||
[ScanFolder Game]
|
||||
watch=@ROOT@/@GAMENAME@
|
||||
watch=@PROJECTROOT@
|
||||
recursive=1
|
||||
order=0
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -8,7 +8,7 @@ tags=tools,renderer
|
||||
|
||||
; without a scan folder, it is an invalid file.
|
||||
[ScanFolder Game]
|
||||
watch=@ROOT@/@GAMENAME@
|
||||
watch=@PROJECTROOT@
|
||||
recursive=1
|
||||
order=0
|
||||
|
||||
|
||||
+4
-4
@@ -35,16 +35,16 @@ cbc=abc
|
||||
fbx.assetinfo=fbx
|
||||
|
||||
[ScanFolder Game]
|
||||
watch=@ROOT@/@GAMENAME@
|
||||
watch=@PROJECTROOT@
|
||||
; use a special display name here to make sure macros work
|
||||
display=@GAMENAME@ Scan Folder
|
||||
display=@PROJECTROOT@ Scan Folder
|
||||
recursive=1
|
||||
order=0
|
||||
|
||||
; this test makes sure that those macros make sense and are present as well as that order is preserved.
|
||||
; it also makes sure that the friendly name ("FeatureTests") is used, if no display is present and does not lose its case.
|
||||
[ScanFolder FeatureTests]
|
||||
watch=@ROOT@/@GAMENAME@FeatureTests
|
||||
watch=@PROJECTROOT@FeatureTests
|
||||
output=featuretestsoutputfolder
|
||||
recursive=0
|
||||
order=5000
|
||||
@@ -53,7 +53,7 @@ order=5000
|
||||
; (which is constructed from its name in the square brackets)
|
||||
; instead of other attributes such as the watch folder, or output prefix.
|
||||
[ScanFolder FeatureTests2]
|
||||
watch=@ROOT@/@GAMENAME@FeatureTests
|
||||
watch=@PROJECTROOT@FeatureTests
|
||||
output=featuretestsoutputfolder
|
||||
recursive=0
|
||||
order=6000
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ cbc=abc
|
||||
fbx.assetinfo=fbx
|
||||
|
||||
[ScanFolder Game]
|
||||
watch=@ROOT@/@GAMENAME@
|
||||
watch=@PROJECTROOT@
|
||||
display=gameoutput
|
||||
recursive=1
|
||||
order=0
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<RCC>
|
||||
<qresource prefix="testdata">
|
||||
<file>config_broken_badplatform/AssetProcessorPlatformConfig.ini</file>
|
||||
<file>config_broken_noscans/AssetProcessorPlatformConfig.ini</file>
|
||||
<file>config_broken_recognizers/AssetProcessorPlatformConfig.ini</file>
|
||||
<file>config_broken_noplatform/AssetProcessorPlatformConfig.ini</file>
|
||||
<file>config_regular/AssetProcessorPlatformConfig.ini</file>
|
||||
<file>config_regular_platform_scanfolder/AssetProcessorPlatformConfig.ini</file>
|
||||
<file>EmptyDummyProject/AssetProcessorGamePlatformConfig.ini</file>
|
||||
<file>DummyProject/AssetProcessorGamePlatformConfig.ini</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -16,7 +16,6 @@ add_subdirectory(AzTestRunner)
|
||||
add_subdirectory(CryCommonTools)
|
||||
add_subdirectory(CrySCompileServer)
|
||||
add_subdirectory(CryXML)
|
||||
add_subdirectory(GemRegistry)
|
||||
add_subdirectory(HLSLCrossCompiler)
|
||||
add_subdirectory(HLSLCrossCompilerMETAL)
|
||||
add_subdirectory(News)
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#include <AzQtComponents/Components/StyleManager.h>
|
||||
|
||||
#include <AzCore/PlatformDef.h>
|
||||
#include <AzCore/Component/ComponentApplication.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/std/typetraits/underlying_type.h>
|
||||
|
||||
#include <QString>
|
||||
@@ -82,7 +85,13 @@ namespace Lumberyard
|
||||
|
||||
AzQtComponents::StyleManager styleManager{ nullptr };
|
||||
QApplication app{ argCount, nullptr };
|
||||
styleManager.Initialize(&app);
|
||||
AZ::IO::FixedMaxPath engineRootPath;
|
||||
{
|
||||
AZ::ComponentApplication componentApplication;
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
settingsRegistry->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
}
|
||||
styleManager.initialize(&app, engineRootPath);
|
||||
|
||||
QString reportPath{ GetReportString(report.file_path.value()) };
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
ly_add_target(
|
||||
NAME GemRegistry.Static STATIC
|
||||
NAMESPACE AZ
|
||||
FILES_CMAKE
|
||||
gemregistry_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PUBLIC
|
||||
include
|
||||
BUILD_DEPENDENCIES
|
||||
PUBLIC
|
||||
AZ::AzCore
|
||||
AZ::AzFramework
|
||||
)
|
||||
|
||||
ly_add_target(
|
||||
NAME GemRegistry ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
|
||||
NAMESPACE AZ
|
||||
FILES_CMAKE
|
||||
gemregistry_shared_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PUBLIC
|
||||
include
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::GemRegistry.Static
|
||||
)
|
||||
|
||||
################################################################################
|
||||
# Tests
|
||||
################################################################################
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
|
||||
ly_add_target(
|
||||
NAME GemRegistry.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE AZ
|
||||
FILES_CMAKE
|
||||
gemregistry_test_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Tests
|
||||
source
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzTest
|
||||
AZ::GemRegistry.Static
|
||||
)
|
||||
ly_add_googletest(
|
||||
NAME AZ::GemRegistry.Tests
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
set(FILES
|
||||
include/GemRegistry/Version.h
|
||||
include/GemRegistry/IGemRegistry.h
|
||||
include/GemRegistry/Dependency.h
|
||||
source/ProjectSettings.h
|
||||
source/ProjectSettings.cpp
|
||||
source/GemDescription.h
|
||||
source/GemDescription.cpp
|
||||
)
|
||||
@@ -1,15 +0,0 @@
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the License). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an AS IS BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
set(FILES
|
||||
source/GemRegistry.h
|
||||
source/GemRegistry.cpp
|
||||
)
|
||||
@@ -1,21 +0,0 @@
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
set(FILES
|
||||
tests/main.cpp
|
||||
tests/VersionTest.cpp
|
||||
tests/DependencyTest.cpp
|
||||
tests/DescriptionTest.cpp
|
||||
tests/ProjectSettingsTest.cpp
|
||||
tests/RegistryTest.cpp
|
||||
source/GemRegistry.h
|
||||
source/GemRegistry.cpp
|
||||
)
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Math/Uuid.h>
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/string/regex.h>
|
||||
#include <AzFramework/Dependency/Dependency.h>
|
||||
|
||||
#include "GemRegistry/Version.h"
|
||||
|
||||
namespace Gems
|
||||
{
|
||||
using EngineSpecifier = AzFramework::Specifier<EngineVersion::parts_count>;
|
||||
using GemSpecifier = AzFramework::Specifier<GemVersion::parts_count>;
|
||||
using EngineDependency = AzFramework::Dependency<EngineVersion::parts_count>;
|
||||
using GemDependency = AzFramework::Dependency<GemVersion::parts_count>;
|
||||
} // namespace Gems
|
||||
@@ -1,399 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Math/Uuid.h>
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
#include <AzCore/std/smart_ptr/weak_ptr.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
|
||||
#include "GemRegistry/Dependency.h"
|
||||
#include "GemRegistry/Version.h"
|
||||
|
||||
namespace Gems
|
||||
{
|
||||
/// Describes how other Gems (and the final executable) will link against this Gem (valid only for Type::GameModule and Type::ServerModule).
|
||||
enum class LinkType
|
||||
{
|
||||
/// Do not link against this Gem, it is loaded as a Dynamic Library at runtime.
|
||||
Dynamic,
|
||||
/// Link against this Gem, it is also loaded as a Dynamic Library at runtime.
|
||||
DynamicStatic,
|
||||
/// Gem has no code, there is nothing to link against.
|
||||
NoCode,
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines a module produced by a Gem.
|
||||
*/
|
||||
struct ModuleDefinition
|
||||
{
|
||||
enum class Type
|
||||
{
|
||||
GameModule,
|
||||
EditorModule,
|
||||
StaticLib,
|
||||
Builder,
|
||||
Standalone,
|
||||
ServerModule,
|
||||
};
|
||||
|
||||
/// The type of module this represents
|
||||
Type m_type;
|
||||
/// The name of the module (for dll naming)
|
||||
AZStd::string m_name;
|
||||
/// If this module is type GameModule, how is it linked?
|
||||
LinkType m_linkType = LinkType::NoCode;
|
||||
/// The complete name of the file produced (for all Types but StaticLib)
|
||||
AZStd::string m_fileName;
|
||||
/// If the module extends another module, this points to that
|
||||
AZStd::weak_ptr<const ModuleDefinition> m_parent;
|
||||
/// All of the modules that extend this module
|
||||
AZStd::vector<AZStd::weak_ptr<const ModuleDefinition>> m_children;
|
||||
|
||||
// Construction and Destruction is trivial
|
||||
ModuleDefinition() = default;
|
||||
~ModuleDefinition() = default;
|
||||
|
||||
// Disallow copying and moving
|
||||
ModuleDefinition(const ModuleDefinition&) = delete;
|
||||
ModuleDefinition& operator=(const ModuleDefinition&) = delete;
|
||||
ModuleDefinition(ModuleDefinition&&) = delete;
|
||||
ModuleDefinition& operator=(ModuleDefinition&& rhs) = delete;
|
||||
};
|
||||
using ModuleDefinitionConstPtr = AZStd::shared_ptr<const ModuleDefinition>;
|
||||
using ModuleDefinitionVector = AZStd::vector<ModuleDefinitionConstPtr>;
|
||||
|
||||
/**
|
||||
* Describes an instance of a Gem.
|
||||
*/
|
||||
class IGemDescription
|
||||
{
|
||||
public:
|
||||
/// The ID of the Gem
|
||||
virtual const AZ::Uuid& GetID() const = 0;
|
||||
/// The name of the Gem
|
||||
virtual const AZStd::string& GetName() const = 0;
|
||||
/// The UI-friendly name of the Gem
|
||||
virtual const AZStd::string& GetDisplayName() const = 0;
|
||||
/// The version of the Gem
|
||||
virtual const GemVersion& GetVersion() const = 0;
|
||||
/// Relative path to the folder of this Gem
|
||||
virtual const AZStd::string& GetPath() const = 0;
|
||||
/// Absolute path to the folder of this Gem
|
||||
virtual const AZStd::string& GetAbsolutePath() const = 0;
|
||||
/// Summary description of the Gem
|
||||
virtual const AZStd::string& GetSummary() const = 0;
|
||||
/// Icon path of the gem
|
||||
virtual const AZStd::string& GetIconPath() const = 0;
|
||||
/// Tags to associated with the Gem
|
||||
virtual const AZStd::vector<AZStd::string>& GetTags() const = 0;
|
||||
/// Get the list of modules produced by the Gem
|
||||
virtual const ModuleDefinitionVector& GetModules() const = 0;
|
||||
/// Get all modules to be loaded for a given function
|
||||
/// This method traverses children to find the most derived module of each type per tree
|
||||
virtual const ModuleDefinitionVector& GetModulesOfType(ModuleDefinition::Type type) const = 0;
|
||||
/// The name of the engine module class to initialize
|
||||
virtual const AZStd::string& GetEngineModuleClass() const = 0;
|
||||
/// Get the Gem's other gems dependencies
|
||||
virtual const AZStd::vector<AZStd::shared_ptr<GemDependency> >& GetGemDependencies() const = 0;
|
||||
/// Get the Gem's engine dependency
|
||||
virtual const AZStd::shared_ptr<EngineDependency> GetEngineDependency() const = 0;
|
||||
/// Determine if this is a Game Gem
|
||||
virtual const bool IsGameGem() const = 0;
|
||||
/// Determine if this is a required Gem
|
||||
virtual const bool IsRequired() const = 0;
|
||||
|
||||
virtual ~IGemDescription() = default;
|
||||
};
|
||||
using IGemDescriptionConstPtr = AZStd::shared_ptr<const IGemDescription>;
|
||||
|
||||
/// A specific Gem known to a Project.
|
||||
/// The Gem is not used unless it is enabled.
|
||||
struct ProjectGemSpecifier
|
||||
: public GemSpecifier
|
||||
{
|
||||
/// Folder in which this specific Gem can be found.
|
||||
AZStd::string m_path;
|
||||
|
||||
ProjectGemSpecifier(const AZ::Uuid& id, const GemVersion& version, const AZStd::string& path)
|
||||
: GemSpecifier(id, version)
|
||||
, m_path(path)
|
||||
{}
|
||||
~ProjectGemSpecifier() override = default;
|
||||
};
|
||||
using ProjectGemSpecifierMap = AZStd::unordered_map<AZ::Uuid, ProjectGemSpecifier>;
|
||||
|
||||
/**
|
||||
* Stores project-specific settings, such as which Gems are enabled and which versions are required.
|
||||
*/
|
||||
class IProjectSettings
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initializes the ProjectSettings with a project name to load the settings from.
|
||||
*
|
||||
* \param[in] appRootFolder The application root folder where the project sub folder resides
|
||||
* \param[in] projectSubFolder The folder in which the project's assets reside (and the configuration file)
|
||||
*
|
||||
* \returns True on success, false on failure.
|
||||
*/
|
||||
virtual AZ::Outcome<void, AZStd::string> Initialize(const AZStd::string& appRootFolder, const AZStd::string& projectSubFolder) = 0;
|
||||
|
||||
/**
|
||||
* Enables the specified instance of a Gem.
|
||||
*
|
||||
* \param[in] spec The specific Gem to enable.
|
||||
*
|
||||
* \returns True on success, False on failure.
|
||||
*/
|
||||
virtual bool EnableGem(const ProjectGemSpecifier& spec) = 0;
|
||||
|
||||
/**
|
||||
* Disables the specified instance of a Gem.
|
||||
*
|
||||
* \param[in] spec The specific Gem to disable.
|
||||
*
|
||||
* \returns True on success, False on failure.
|
||||
*/
|
||||
virtual bool DisableGem(const GemSpecifier& spec) = 0;
|
||||
|
||||
/**
|
||||
* Checks if a Gem of the specified description is enabled.
|
||||
*
|
||||
* \param[in] spec The specific Gem to check.
|
||||
*
|
||||
* \returns True if the Gem is enabled, False if it is disabled.
|
||||
*/
|
||||
virtual bool IsGemEnabled(const GemSpecifier& spec) const = 0;
|
||||
|
||||
/**
|
||||
* Checks if a Gem of the specified ID and version constraints is enabled.
|
||||
*
|
||||
* \param[in] id The ID of the Gem to check.
|
||||
* \param[in] versionConstraints An array of strings, each of which is a condition using the gem dependency syntax
|
||||
*
|
||||
* \returns True if the Gem is enabled and passes every version constraint condition, False if it is disabled or does not match the version constraints.
|
||||
*/
|
||||
virtual bool IsGemEnabled(const AZ::Uuid& id, const AZStd::vector<AZStd::string>& versionConstraints) const = 0;
|
||||
|
||||
/**
|
||||
* Checks if a Gem of the specified dependency is enabled.
|
||||
*
|
||||
* \param[in] dep The dependency to validate.
|
||||
*
|
||||
* \returns True if the Gem is enabled, False if it is disabled.
|
||||
*/
|
||||
virtual bool IsGemDependencyMet(const AZStd::shared_ptr<GemDependency> dep) const = 0;
|
||||
|
||||
/**
|
||||
* Checks if the engine dependency is met.
|
||||
*
|
||||
* \param[in] dep The dependency to validate.
|
||||
* \param[in] againstVersion
|
||||
* The version of the engine to validate against
|
||||
*
|
||||
* \returns True if the Gem is enabled, False if it is disabled.
|
||||
*/
|
||||
virtual bool IsEngineDependencyMet(const AZStd::shared_ptr<EngineDependency> dep, const EngineVersion& againstVersion) const = 0;
|
||||
|
||||
/**
|
||||
* Gets the Gems known to this project.
|
||||
* Only enabled Gems are actually used at runtime.
|
||||
* A project can only reference one version of a Gem.
|
||||
*
|
||||
* \returns The vector of enabled Gems.
|
||||
*/
|
||||
virtual const ProjectGemSpecifierMap& GetGems() const = 0;
|
||||
|
||||
/**
|
||||
* Sets the Gem map to the passed in list. Used when resetting after a failed save.
|
||||
*/
|
||||
virtual void SetGems(const ProjectGemSpecifierMap& newGemMap) = 0;
|
||||
|
||||
/**
|
||||
* Checks that all installed Gems have their dependencies met.
|
||||
* Any unmet dependencies can be found via IGemRegistry::GetErrorMessage();
|
||||
*
|
||||
* \param[in] engineVersion
|
||||
* The version of the engine to validate against
|
||||
*
|
||||
* \returns Void on success, error message on failure.
|
||||
*/
|
||||
virtual AZ::Outcome<void, AZStd::string> ValidateDependencies(const EngineVersion& engineVersion = EngineVersion()) const = 0;
|
||||
|
||||
/**
|
||||
* Saves the current state of the project settings to it's project configuration file.
|
||||
*
|
||||
* \returns Void on success, error message on failure.
|
||||
*/
|
||||
virtual AZ::Outcome<void, AZStd::string> Save() const = 0;
|
||||
|
||||
/**
|
||||
* Get the project name that this project settings represents
|
||||
|
||||
* \returns The project name.
|
||||
*/
|
||||
virtual const AZStd::string& GetProjectName() const = 0;
|
||||
|
||||
/**
|
||||
* Get the app root folder for the project
|
||||
|
||||
* \returns The project root path.
|
||||
*/
|
||||
virtual const AZStd::string& GetProjectRootPath() const = 0;
|
||||
|
||||
virtual ~IProjectSettings() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines how to search for Gems.
|
||||
*/
|
||||
struct SearchPath
|
||||
{
|
||||
/// The root path to search
|
||||
AZStd::string m_path;
|
||||
/// The filter to apply to TOP LEVEL searching
|
||||
AZStd::string m_filter;
|
||||
|
||||
explicit SearchPath(const AZStd::string& path)
|
||||
: m_path(path)
|
||||
, m_filter("*")
|
||||
{ }
|
||||
SearchPath(const AZStd::string& path, const AZStd::string& filter)
|
||||
: m_path(path)
|
||||
, m_filter(filter)
|
||||
{ }
|
||||
};
|
||||
|
||||
inline bool operator==(const SearchPath& left, const SearchPath& right)
|
||||
{
|
||||
return left.m_path == right.m_path
|
||||
&& left.m_filter == right.m_filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages installed Gems.
|
||||
*/
|
||||
class IGemRegistry
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Add to the list of paths to search for Gems when calling LoadAllGemsFromDisk
|
||||
*
|
||||
* \param[in] searchPath The path to add
|
||||
* \param[in] loadGemsNow Load Gems from searchPath now
|
||||
*
|
||||
* \returns Void on success, error message on failure.
|
||||
*/
|
||||
virtual AZ::Outcome<void, AZStd::string> AddSearchPath(const SearchPath& searchPath, bool loadGemsNow) = 0;
|
||||
|
||||
/**
|
||||
* Scans the Gems/ folder for all installed Gems.
|
||||
*
|
||||
* In the event of an error loading a Gem, the error will be recorded,
|
||||
* the Gem will not be loaded, and false will be returned.
|
||||
*
|
||||
* Loaded Gems can be accessed via GetGemDescription() or GetAllGemDescriptions().
|
||||
*
|
||||
* \returns AZ::Success if the search succeeded
|
||||
* AZ::Failure with error message if any errors occurred.
|
||||
*/
|
||||
virtual AZ::Outcome<void, AZStd::string> LoadAllGemsFromDisk() = 0;
|
||||
|
||||
/**
|
||||
* Looks for a gems.json file in the given folder and returns a IGemDescriptionConstPtr if found
|
||||
*
|
||||
* \param[in] gemFolderRelPath A valid path on disk to a directory that contains a gem.json file relative to the engine root
|
||||
*
|
||||
* \returns IGemDescriptionConstPtr if a gem.json file could be parsed out of the gemFolderPath,
|
||||
* AZ::Failure with error message if any errors occurred.
|
||||
*/
|
||||
virtual AZ::Outcome<IGemDescriptionConstPtr, AZStd::string> ParseToGemDescriptionPtr(const AZStd::string& gemFolderRelPath, const char* absoluteFilePath) = 0;
|
||||
|
||||
/**
|
||||
* Loads Gems for the specified project.
|
||||
* May be called for multiple projects.
|
||||
*
|
||||
* \param[in] settings The project to load Gems for.
|
||||
* \param[in] resetPreviousProjects If true, reset any setting/gem descriptors that any previous calls to LoadProject may have added
|
||||
*
|
||||
* \returns Void on success, error message on failure.
|
||||
*/
|
||||
virtual AZ::Outcome<void, AZStd::string> LoadProject(const IProjectSettings& settings, bool resetPreviousProjects) = 0;
|
||||
|
||||
/**
|
||||
* Gets the description for a Gem.
|
||||
*
|
||||
* \param[in] spec The specific Gem to search for.
|
||||
*
|
||||
* \returns A pointer to the Gem's description if it exists, otherwise nullptr.
|
||||
*/
|
||||
virtual IGemDescriptionConstPtr GetGemDescription(const GemSpecifier& spec) const = 0;
|
||||
|
||||
/**
|
||||
* Gets the description for the latest version of a Gem.
|
||||
*
|
||||
* \param[in] uuid The specific uuid of the Gem to search for.
|
||||
*
|
||||
* \returns A pointer to the Gem's description highest version if it exists, otherwise nullptr.
|
||||
*/
|
||||
virtual IGemDescriptionConstPtr GetLatestGem(const AZ::Uuid& uuid) const = 0;
|
||||
|
||||
/**
|
||||
* Gets a list of all loaded Gem descriptions.
|
||||
*/
|
||||
virtual AZStd::vector<IGemDescriptionConstPtr> GetAllGemDescriptions() const = 0;
|
||||
|
||||
/**
|
||||
* Gets a list of all loaded required Gem descriptions.
|
||||
*/
|
||||
virtual AZStd::vector<IGemDescriptionConstPtr> GetAllRequiredGemDescriptions() const = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Get the project-specific gem description if any
|
||||
*/
|
||||
virtual IGemDescriptionConstPtr GetProjectGemDescription(const AZStd::string& projectName) const = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of IProjectSettings.
|
||||
*
|
||||
* \returns A new project settings object.
|
||||
*/
|
||||
virtual IProjectSettings* CreateProjectSettings() = 0;
|
||||
|
||||
/**
|
||||
* Destroys an instance of IProjectSettings.
|
||||
*
|
||||
* \params[in] settings The settings instance to destroy.
|
||||
*/
|
||||
virtual void DestroyProjectSettings(IProjectSettings* settings) = 0;
|
||||
|
||||
virtual ~IGemRegistry() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* The type of function exported for creating a new GemRegistry.
|
||||
*/
|
||||
using RegistryCreatorFunction = IGemRegistry * (*)();
|
||||
#define GEMS_REGISTRY_CREATOR_FUNCTION_NAME "CreateGemRegistry"
|
||||
|
||||
/**
|
||||
* The type of function exported for destroying a GemRegistry.
|
||||
*/
|
||||
using RegistryDestroyerFunction = void(*)(IGemRegistry*);
|
||||
#define GEMS_REGISTRY_DESTROYER_FUNCTION_NAME "DestroyGemRegistry"
|
||||
} // namespace Gems
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
#include <AzCore/std/hash.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzFramework/Dependency/Version.h>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <sstream>
|
||||
|
||||
namespace Gems
|
||||
{
|
||||
using GemVersion = AzFramework::SemanticVersion;
|
||||
|
||||
// /**
|
||||
// * Represents a version of the Lumberyard Game Engine
|
||||
// */
|
||||
using EngineVersion = AzFramework::Version<4>;
|
||||
} // namespace Gems
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
template <size_t N>
|
||||
struct hash<AzFramework::Version<N>>
|
||||
{
|
||||
size_t operator()(const AzFramework::Version<N>& ver) const
|
||||
{
|
||||
return AZStd::hash_range(ver.m_parts.begin(), ver.m_parts.end());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<AzFramework::SemanticVersion>
|
||||
: public hash<AzFramework::Version<3>>
|
||||
{ };
|
||||
} // namespace AZStd
|
||||
@@ -1,621 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include "GemDescription.h"
|
||||
#include "GemRegistry.h"
|
||||
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/JSON/error/en.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
// For LinkTypeFromString
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
namespace Gems
|
||||
{
|
||||
GemDescription::GemDescription()
|
||||
: m_id(AZ::Uuid::CreateNull())
|
||||
, m_name()
|
||||
, m_displayName()
|
||||
, m_version()
|
||||
, m_path()
|
||||
, m_absolutePath()
|
||||
, m_summary()
|
||||
, m_iconPath()
|
||||
, m_tags()
|
||||
, m_modules()
|
||||
, m_modulesByType()
|
||||
, m_engineModuleClass()
|
||||
, m_gemDependencies()
|
||||
, m_gameGem(false)
|
||||
, m_required(false)
|
||||
, m_engineDependency(nullptr)
|
||||
{
|
||||
m_modulesByType.emplace(ModuleDefinition::Type::GameModule);
|
||||
m_modulesByType.emplace(ModuleDefinition::Type::ServerModule);
|
||||
m_modulesByType.emplace(ModuleDefinition::Type::EditorModule);
|
||||
m_modulesByType.emplace(ModuleDefinition::Type::StaticLib);
|
||||
m_modulesByType.emplace(ModuleDefinition::Type::Builder);
|
||||
m_modulesByType.emplace(ModuleDefinition::Type::Standalone);
|
||||
}
|
||||
|
||||
GemDescription::GemDescription(const GemDescription& rhs)
|
||||
: m_id(rhs.m_id)
|
||||
, m_name(rhs.m_name)
|
||||
, m_displayName(rhs.m_displayName)
|
||||
, m_version(rhs.m_version)
|
||||
, m_path(rhs.m_path)
|
||||
, m_absolutePath(rhs.m_absolutePath)
|
||||
, m_summary(rhs.m_summary)
|
||||
, m_iconPath(rhs.m_iconPath)
|
||||
, m_tags(rhs.m_tags)
|
||||
, m_modules(rhs.m_modules)
|
||||
, m_modulesByType(rhs.m_modulesByType)
|
||||
, m_engineModuleClass(rhs.m_engineModuleClass)
|
||||
, m_gemDependencies(rhs.m_gemDependencies)
|
||||
, m_gameGem(rhs.m_gameGem)
|
||||
, m_required(rhs.m_required)
|
||||
, m_engineDependency(rhs.m_engineDependency)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
GemDescription::GemDescription(GemDescription&& rhs)
|
||||
: m_id(rhs.m_id)
|
||||
, m_name(AZStd::move(rhs.m_name))
|
||||
, m_displayName(AZStd::move(rhs.m_displayName))
|
||||
, m_path(AZStd::move(rhs.m_path))
|
||||
, m_absolutePath(AZStd::move(rhs.m_absolutePath))
|
||||
, m_summary(AZStd::move(rhs.m_summary))
|
||||
, m_iconPath(AZStd::move(rhs.m_iconPath))
|
||||
, m_tags(AZStd::move(rhs.m_tags))
|
||||
, m_version(rhs.m_version)
|
||||
, m_modules(AZStd::move(rhs.m_modules))
|
||||
, m_modulesByType(AZStd::move(rhs.m_modulesByType))
|
||||
, m_engineModuleClass(AZStd::move(rhs.m_engineModuleClass))
|
||||
, m_gemDependencies(AZStd::move(rhs.m_gemDependencies))
|
||||
, m_gameGem(AZStd::move(rhs.m_gameGem))
|
||||
, m_required(AZStd::move(rhs.m_required))
|
||||
, m_engineDependency(AZStd::move(rhs.m_engineDependency))
|
||||
{
|
||||
rhs.m_id = AZ::Uuid::CreateNull();
|
||||
rhs.m_version = GemVersion { 0, 0, 0 };
|
||||
}
|
||||
|
||||
// returns whether conversion was successful
|
||||
static bool LinkTypeFromString(const char* value, LinkType& linkTypeOut)
|
||||
{
|
||||
// static map for lookups
|
||||
static const std::unordered_map<std::string, LinkType> linkNameToType = {
|
||||
{ GPF_TAG_LINK_TYPE_DYNAMIC, LinkType::Dynamic },
|
||||
{ GPF_TAG_LINK_TYPE_DYNAMIC_STATIC, LinkType::DynamicStatic },
|
||||
{ GPF_TAG_LINK_TYPE_NO_CODE, LinkType::NoCode },
|
||||
};
|
||||
|
||||
auto found = linkNameToType.find(value);
|
||||
if (found != linkNameToType.end())
|
||||
{
|
||||
linkTypeOut = found->second;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ModuleTypeFromString(const char* value, ModuleDefinition::Type& moduleTypeOut)
|
||||
{
|
||||
static const std::unordered_map<const char*, ModuleDefinition::Type> moduleNameToType = {
|
||||
{ GPF_TAG_MODULE_TYPE_GAME_MODULE, ModuleDefinition::Type::GameModule },
|
||||
{ GPF_TAG_MODULE_TYPE_SERVER_MODULE, ModuleDefinition::Type::ServerModule },
|
||||
{ GPF_TAG_MODULE_TYPE_EDITOR_MODULE, ModuleDefinition::Type::EditorModule },
|
||||
{ GPF_TAG_MODULE_TYPE_STATIC_LIB, ModuleDefinition::Type::StaticLib },
|
||||
{ GPF_TAG_MODULE_TYPE_BUILDER, ModuleDefinition::Type::Builder },
|
||||
{ GPF_TAG_MODULE_TYPE_STANDALONE, ModuleDefinition::Type::Standalone },
|
||||
};
|
||||
|
||||
auto found = AZStd::find_if(moduleNameToType.begin(), moduleNameToType.end(), [&value](decltype(moduleNameToType)::const_reference pair) {
|
||||
return strcmp(pair.first, value) == 0;
|
||||
});
|
||||
|
||||
if (found != moduleNameToType.end())
|
||||
{
|
||||
moduleTypeOut = found->second;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Bring contents of file up to current version.
|
||||
AZ::Outcome<void, AZStd::string> UpgradeGemDescriptionJson(rapidjson::Document& descNode)
|
||||
{
|
||||
// get format version
|
||||
if (!RAPIDJSON_IS_VALID_MEMBER(descNode, GPF_TAG_FORMAT_VERSION, IsInt))
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_FORMAT_VERSION " int is required."));
|
||||
}
|
||||
|
||||
int gemFormatVersion = descNode[GPF_TAG_FORMAT_VERSION].GetInt();
|
||||
|
||||
// decline ancient and future versions
|
||||
if (gemFormatVersion < 2 || gemFormatVersion > GEM_DEF_FILE_VERSION)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(GPF_TAG_FORMAT_VERSION " is version %d, but %d is expected.",
|
||||
gemFormatVersion, GEM_DEF_FILE_VERSION));
|
||||
}
|
||||
|
||||
// upgrade v2 -> v3
|
||||
if (gemFormatVersion < 3)
|
||||
{
|
||||
// beginning in v3 Gems contain an AZ::Module, in the past they contained an IGem
|
||||
descNode.AddMember("IsLegacyIGem", true, descNode.GetAllocator());
|
||||
}
|
||||
|
||||
// upgrade v3 -> v4
|
||||
if (gemFormatVersion < 4)
|
||||
{
|
||||
// read link type, if not NoCode, migrate to GameModule
|
||||
if (!RAPIDJSON_IS_VALID_MEMBER(descNode, GPF_TAG_LINK_TYPE, IsString))
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_LINK_TYPE " string must not be empty."));
|
||||
}
|
||||
// Explicitly copy string so we can remove it from the object
|
||||
AZStd::string linkTypeString = descNode[GPF_TAG_LINK_TYPE].GetString();
|
||||
descNode.RemoveMember(GPF_TAG_LINK_TYPE);
|
||||
|
||||
LinkType linkType;
|
||||
if (!LinkTypeFromString(linkTypeString.c_str(), linkType))
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_LINK_TYPE " string is invalid."));
|
||||
}
|
||||
|
||||
// If no-code, don't make module definitions
|
||||
if (linkType != LinkType::NoCode)
|
||||
{
|
||||
// Create modules list
|
||||
rapidjson::Value modulesList{ rapidjson::kArrayType };
|
||||
|
||||
// Create module definition
|
||||
{
|
||||
rapidjson::Value gameModule{ rapidjson::kObjectType };
|
||||
gameModule.AddMember(GPF_TAG_MODULE_TYPE, GPF_TAG_MODULE_TYPE_GAME_MODULE, descNode.GetAllocator());
|
||||
gameModule.AddMember(GPF_TAG_LINK_TYPE, rapidjson::Value(linkTypeString.c_str(), descNode.GetAllocator()), descNode.GetAllocator());
|
||||
|
||||
modulesList.PushBack(AZStd::move(gameModule), descNode.GetAllocator());
|
||||
}
|
||||
|
||||
// Create server module definition
|
||||
if (RAPIDJSON_IS_VALID_MEMBER(descNode, GPF_TAG_MODULE_TYPE_SERVER_MODULE, IsBool) && descNode[GPF_TAG_MODULE_TYPE_SERVER_MODULE].GetBool())
|
||||
{
|
||||
rapidjson::Value serverModule{ rapidjson::kObjectType };
|
||||
serverModule.AddMember(GPF_TAG_MODULE_TYPE, GPF_TAG_MODULE_TYPE_SERVER_MODULE, descNode.GetAllocator());
|
||||
serverModule.AddMember(GPF_TAG_LINK_TYPE, rapidjson::Value(linkTypeString.c_str(), descNode.GetAllocator()), descNode.GetAllocator());
|
||||
serverModule.AddMember(GPF_TAG_MODULE_NAME, "Server", descNode.GetAllocator());
|
||||
modulesList.PushBack(AZStd::move(serverModule), descNode.GetAllocator());
|
||||
}
|
||||
|
||||
// Create editor module definition
|
||||
if (RAPIDJSON_IS_VALID_MEMBER(descNode, GPF_TAG_EDITOR_MODULE, IsBool) && descNode[GPF_TAG_EDITOR_MODULE].GetBool())
|
||||
{
|
||||
rapidjson::Value editorModule{ rapidjson::kObjectType };
|
||||
editorModule.AddMember(GPF_TAG_MODULE_TYPE, GPF_TAG_MODULE_TYPE_EDITOR_MODULE, descNode.GetAllocator());
|
||||
editorModule.AddMember(GPF_TAG_MODULE_NAME, "Editor", descNode.GetAllocator());
|
||||
editorModule.AddMember(GPF_TAG_MODULE_EXTENDS, "GameModule", descNode.GetAllocator());
|
||||
|
||||
modulesList.PushBack(AZStd::move(editorModule), descNode.GetAllocator());
|
||||
}
|
||||
descNode.RemoveMember(GPF_TAG_EDITOR_MODULE);
|
||||
|
||||
// Add modules list to
|
||||
descNode.AddMember(GPF_TAG_MODULES, modulesList, descNode.GetAllocator());
|
||||
}
|
||||
}
|
||||
|
||||
// file is now up to date
|
||||
descNode[GPF_TAG_FORMAT_VERSION] = GEM_DEF_FILE_VERSION;
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
AZ::Outcome<GemDescription, AZStd::string> GemDescription::CreateFromJson(
|
||||
rapidjson::Document& descNode,
|
||||
const AZStd::string& gemFolderPath,
|
||||
const AZStd::string& absoluteFilePath)
|
||||
{
|
||||
// gem to build
|
||||
GemDescription gem;
|
||||
|
||||
gem.m_path = gemFolderPath;
|
||||
gem.m_absolutePath = absoluteFilePath;
|
||||
AzFramework::StringFunc::Path::StripFullName(gem.m_absolutePath);
|
||||
AzFramework::StringFunc::RChop(gem.m_absolutePath, 1);
|
||||
|
||||
if (!descNode.IsObject())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Json root element must be an object."));
|
||||
}
|
||||
|
||||
// upgrade contents to current version
|
||||
auto upgradeOutcome = UpgradeGemDescriptionJson(descNode);
|
||||
if (!upgradeOutcome.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(upgradeOutcome.TakeError());
|
||||
}
|
||||
|
||||
// read name
|
||||
if (RAPIDJSON_IS_VALID_MEMBER(descNode, GPF_TAG_NAME, IsString))
|
||||
{
|
||||
gem.m_name = descNode[GPF_TAG_NAME].GetString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_NAME " string must not be empty."));
|
||||
}
|
||||
|
||||
// read display name
|
||||
if (RAPIDJSON_IS_VALID_MEMBER(descNode, GPF_TAG_DISPLAY_NAME, IsString))
|
||||
{
|
||||
gem.m_displayName = descNode[GPF_TAG_DISPLAY_NAME].GetString();
|
||||
}
|
||||
|
||||
// read id
|
||||
if (!RAPIDJSON_IS_VALID_MEMBER(descNode, GPF_TAG_UUID, IsString))
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_UUID " string is required."));
|
||||
}
|
||||
gem.m_id = AZ::Uuid::CreateString(descNode[GPF_TAG_UUID].GetString());
|
||||
if (gem.m_id.IsNull())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(GPF_TAG_UUID " string \"%s\" is invalid.", descNode[GPF_TAG_UUID].GetString()));
|
||||
}
|
||||
|
||||
// read version
|
||||
if (RAPIDJSON_IS_VALID_MEMBER(descNode, GPF_TAG_VERSION, IsString))
|
||||
{
|
||||
auto versionOutcome = GemVersion::ParseFromString(descNode[GPF_TAG_VERSION].GetString());
|
||||
|
||||
if (versionOutcome)
|
||||
{
|
||||
gem.m_version = versionOutcome.GetValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Failure(AZStd::string(versionOutcome.GetError()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_VERSION " string is required."));
|
||||
}
|
||||
|
||||
// To reduce the potential for user error, a Gem depending on the Lumberyard engine version
|
||||
// supports both arrays and strings.
|
||||
if (descNode.HasMember(GPF_TAG_LY_VERSION))
|
||||
{
|
||||
AZStd::vector<AZStd::string> versionConstraints;
|
||||
// read version constraints
|
||||
// Check if the version constraint is a string first.
|
||||
if(RAPIDJSON_IS_VALID_MEMBER(descNode, GPF_TAG_LY_VERSION, IsString))
|
||||
{
|
||||
AZStd::string constraintString = descNode[GPF_TAG_LY_VERSION].GetString();
|
||||
versionConstraints.push_back(constraintString);
|
||||
}
|
||||
// If it wasn't a string, make sure it's an array. If not, error out.
|
||||
else if (!RAPIDJSON_IS_VALID_MEMBER(descNode, GPF_TAG_LY_VERSION, IsArray))
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_LY_VERSION " array is required for engine version."));
|
||||
}
|
||||
// If it's an empty array, ignore it.
|
||||
// For ease of use editing the Gem.json files, we support empty arrays, so users
|
||||
// can leave the engine version key in without providing a value.
|
||||
else if (descNode[GPF_TAG_LY_VERSION].Size() > 0)
|
||||
{
|
||||
const auto& constraints = descNode[GPF_TAG_LY_VERSION];
|
||||
const auto& end = constraints.End();
|
||||
for (auto it = constraints.Begin(); it != end; ++it)
|
||||
{
|
||||
const auto& constraint = *it;
|
||||
if (!constraint.IsString())
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_LY_VERSION " array for engine version must contain strings."));
|
||||
}
|
||||
versionConstraints.push_back(constraint.GetString());
|
||||
}
|
||||
}
|
||||
// If constraints were actually provided, create the dependency.
|
||||
if(versionConstraints.size() > 0)
|
||||
{
|
||||
EngineDependency dep;
|
||||
dep.SetID(AZ::Uuid::CreateNull());
|
||||
AZ::Outcome<void, AZStd::string> outcome = dep.ParseVersions(versionConstraints);
|
||||
if (!outcome)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(GPF_TAG_LY_VERSION " for engine version is invalid. %s", outcome.GetError().c_str()));
|
||||
}
|
||||
gem.m_engineDependency = AZStd::make_shared<EngineDependency>(dep);
|
||||
}
|
||||
}
|
||||
|
||||
// dependencies
|
||||
if (descNode.HasMember(GPF_TAG_DEPENDENCIES))
|
||||
{
|
||||
if (!descNode[GPF_TAG_DEPENDENCIES].IsArray())
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_DEPENDENCIES " must be an array."));
|
||||
}
|
||||
|
||||
// List of descriptions of Gems we depend upon
|
||||
const rapidjson::Value& depsNode = descNode[GPF_TAG_DEPENDENCIES];
|
||||
const auto& end = depsNode.End();
|
||||
for (auto it = depsNode.Begin(); it != end; ++it)
|
||||
{
|
||||
const auto& depNode = *it;
|
||||
if (!depNode.IsObject())
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_DEPENDENCIES " must contain objects."));
|
||||
}
|
||||
|
||||
// read id
|
||||
if (!RAPIDJSON_IS_VALID_MEMBER(depNode, GPF_TAG_UUID, IsString))
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_UUID " string is required for dependency."));
|
||||
}
|
||||
|
||||
const char* idStr = depNode[GPF_TAG_UUID].GetString();
|
||||
AZ::Uuid id(idStr);
|
||||
if (id.IsNull())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(GPF_TAG_UUID " in dependency is invalid: %s.", idStr));
|
||||
}
|
||||
|
||||
// read version constraints
|
||||
if (!RAPIDJSON_IS_VALID_MEMBER(depNode, GPF_TAG_VERSION_CONSTRAINTS, IsArray))
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_VERSION_CONSTRAINTS " array is required for dependency."));
|
||||
}
|
||||
|
||||
// Make sure versions are specified
|
||||
if (depNode[GPF_TAG_VERSION_CONSTRAINTS].Size() < 1)
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_VERSION_CONSTRAINTS " must have at least 1 entry for dependency."));
|
||||
}
|
||||
|
||||
AZStd::vector<AZStd::string> versionConstraints;
|
||||
const auto& constraints = depNode[GPF_TAG_VERSION_CONSTRAINTS];
|
||||
const auto& constraintsEnd(constraints.End());
|
||||
for (auto constraintIt = constraints.Begin(); constraintIt != constraintsEnd; ++constraintIt)
|
||||
{
|
||||
const auto& constraint = *constraintIt;
|
||||
if (!constraint.IsString())
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_VERSION_CONSTRAINTS " array for dependency must contain strings."));
|
||||
}
|
||||
versionConstraints.push_back(constraint.GetString());
|
||||
}
|
||||
|
||||
// create Dependency
|
||||
GemDependency dep;
|
||||
dep.SetID(id);
|
||||
if (!dep.ParseVersions(versionConstraints))
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_VERSION_CONSTRAINTS " for dependency is invalid"));
|
||||
}
|
||||
gem.m_gemDependencies.push_back(AZStd::make_shared<GemDependency>(dep));
|
||||
}
|
||||
}
|
||||
|
||||
// Is Game Gem? flag
|
||||
if (RAPIDJSON_IS_VALID_MEMBER(descNode, GPF_TAG_IS_GAME_GEM, IsBool))
|
||||
{
|
||||
gem.m_gameGem = descNode[GPF_TAG_IS_GAME_GEM].GetBool();
|
||||
}
|
||||
else
|
||||
{
|
||||
gem.m_gameGem = false;
|
||||
}
|
||||
|
||||
// Is Required? flag
|
||||
if (RAPIDJSON_IS_VALID_MEMBER(descNode, GPF_TAG_IS_REQUIRED, IsBool))
|
||||
{
|
||||
gem.m_required = descNode[GPF_TAG_IS_REQUIRED].GetBool();
|
||||
}
|
||||
else
|
||||
{
|
||||
gem.m_required = false;
|
||||
}
|
||||
|
||||
// optional metadata
|
||||
if (RAPIDJSON_IS_VALID_MEMBER(descNode, GPF_TAG_SUMMARY, IsString))
|
||||
{
|
||||
gem.m_summary = descNode[GPF_TAG_SUMMARY].GetString();
|
||||
}
|
||||
|
||||
if (RAPIDJSON_IS_VALID_MEMBER(descNode, GPF_TAG_ICON_PATH, IsString))
|
||||
{
|
||||
gem.m_iconPath = descNode[GPF_TAG_ICON_PATH].GetString();
|
||||
}
|
||||
|
||||
if (descNode.HasMember(GPF_TAG_TAGS))
|
||||
{
|
||||
const rapidjson::Value& tags = descNode[GPF_TAG_TAGS];
|
||||
if (tags.IsArray())
|
||||
{
|
||||
const auto& end = tags.End();
|
||||
for (auto it = tags.Begin(); it != end; ++it)
|
||||
{
|
||||
const auto& tag = *it;
|
||||
gem.m_tags.push_back(tag.GetString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Value for key " GPF_TAG_TAGS " must be an array."));
|
||||
}
|
||||
}
|
||||
|
||||
// engine module class
|
||||
gem.m_engineModuleClass = RAPIDJSON_IS_VALID_MEMBER(descNode, GPF_TAG_MODULE_CLASS, IsString)
|
||||
? descNode[GPF_TAG_MODULE_CLASS].GetString()
|
||||
: gem.GetName() + AZStd::string("Gem");
|
||||
|
||||
// Cache constants
|
||||
char idStr[UUID_STR_BUF_LEN];
|
||||
gem.GetID().ToString(idStr, UUID_STR_BUF_LEN, false, false);
|
||||
AZStd::to_lower(idStr, idStr + strlen(idStr));
|
||||
|
||||
// Read the modules list
|
||||
if (RAPIDJSON_IS_VALID_MEMBER(descNode, GPF_TAG_MODULES, IsArray))
|
||||
{
|
||||
bool foundDefaultModule = false;
|
||||
AZStd::unordered_map<AZStd::string, AZStd::shared_ptr<ModuleDefinition>> modulesByName;
|
||||
AZStd::vector<AZStd::pair<AZStd::shared_ptr<ModuleDefinition>, AZStd::string>> dependencies;
|
||||
|
||||
const rapidjson::Value& modulesNode = descNode[GPF_TAG_MODULES];
|
||||
for (auto moduleObjPtr = modulesNode.Begin(); moduleObjPtr != modulesNode.End(); ++moduleObjPtr)
|
||||
{
|
||||
const rapidjson::Value& moduleObj = *moduleObjPtr;
|
||||
if (!moduleObj.IsObject())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Each object in " GPF_TAG_MODULES " must be an object!"));
|
||||
}
|
||||
|
||||
auto modulePtr = AZStd::make_shared<ModuleDefinition>();
|
||||
gem.m_modules.emplace_back(modulePtr);
|
||||
|
||||
// Get the module type
|
||||
if (!RAPIDJSON_IS_VALID_MEMBER(moduleObj, GPF_TAG_MODULE_TYPE, IsString))
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Each module requires a " GPF_TAG_MODULE_TYPE " field."));
|
||||
}
|
||||
const char* moduleTypeStr = moduleObj[GPF_TAG_MODULE_TYPE].GetString();
|
||||
if (!ModuleTypeFromString(moduleTypeStr, modulePtr->m_type))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Module type %s is invalid!", moduleTypeStr));
|
||||
}
|
||||
|
||||
// Get the module name (default to the type)
|
||||
if (RAPIDJSON_IS_VALID_MEMBER(moduleObj, GPF_TAG_MODULE_NAME, IsString))
|
||||
{
|
||||
modulePtr->m_name = moduleObj[GPF_TAG_MODULE_NAME].GetString();
|
||||
}
|
||||
else if (modulePtr->m_type == ModuleDefinition::Type::GameModule || modulePtr->m_type == ModuleDefinition::Type::ServerModule)
|
||||
{
|
||||
modulePtr->m_name = moduleTypeStr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Default \"" GPF_TAG_MODULE_NAME "\" field is only supported for modules of type \"GameModule\", not %s.", moduleTypeStr));
|
||||
}
|
||||
|
||||
// Check for duplicate names
|
||||
if (modulesByName.find(modulePtr->m_name) != modulesByName.end())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Module name \"%s\" is used more than once!", modulePtr->m_name.c_str()));
|
||||
}
|
||||
|
||||
// If the type is GameModule, omit name from file name (maintains functionality of v3)
|
||||
if (modulePtr->m_type == ModuleDefinition::Type::GameModule || modulePtr->m_type == ModuleDefinition::Type::ServerModule)
|
||||
{
|
||||
if (!foundDefaultModule)
|
||||
{
|
||||
foundDefaultModule = true;
|
||||
// if the module name for 'GameModule' type is specified, such as 'Private' then it needs to be appended into the gem name
|
||||
if (modulePtr->m_name != moduleTypeStr)
|
||||
{
|
||||
modulePtr->m_fileName = AZStd::string::format("Gem.%s.%s.%s.v%s", gem.GetName().c_str(), modulePtr->m_name.c_str(), idStr, gem.GetVersion().ToString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
modulePtr->m_fileName = AZStd::string::format("Gem.%s.%s.v%s", gem.GetName().c_str(), idStr, gem.GetVersion().ToString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// If LinkType is specified, read and validate it.
|
||||
if (RAPIDJSON_IS_VALID_MEMBER(moduleObj, GPF_TAG_LINK_TYPE, IsString))
|
||||
{
|
||||
const char* linkTypeStr = moduleObj[GPF_TAG_LINK_TYPE].GetString();
|
||||
if (!LinkTypeFromString(linkTypeStr, modulePtr->m_linkType))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(GPF_TAG_LINK_TYPE " specified (\"%s\") is invalid", linkTypeStr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the module needs a file name, populate it.
|
||||
if (modulePtr->m_fileName.empty() && modulePtr->m_type != ModuleDefinition::Type::StaticLib)
|
||||
{
|
||||
modulePtr->m_fileName = AZStd::string::format("Gem.%s.%s.%s.v%s", gem.GetName().c_str(), modulePtr->m_name.c_str(), idStr, gem.GetVersion().ToString().c_str());
|
||||
}
|
||||
|
||||
modulesByName.emplace(modulePtr->m_name, modulePtr);
|
||||
|
||||
// Populate extensions
|
||||
if (modulePtr->m_type != ModuleDefinition::Type::StaticLib &&
|
||||
RAPIDJSON_IS_VALID_MEMBER(moduleObj, GPF_TAG_MODULE_EXTENDS, IsString))
|
||||
{
|
||||
dependencies.emplace_back(modulePtr, AZStd::string(moduleObj[GPF_TAG_MODULE_EXTENDS].GetString()));
|
||||
}
|
||||
}
|
||||
|
||||
// Populate dependencies
|
||||
for (const auto& dependencyPair : dependencies)
|
||||
{
|
||||
auto dependencyIterator = modulesByName.find(dependencyPair.second);
|
||||
if (dependencyIterator == modulesByName.end())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Module \"%s\" depends on \"" GPF_TAG_MODULE_EXTENDS "\" invalid module \"%s\"", dependencyPair.first->m_name.c_str(), dependencyPair.second.c_str()));
|
||||
}
|
||||
|
||||
if (dependencyIterator->second->m_type != ModuleDefinition::Type::GameModule && dependencyIterator->second->m_type != ModuleDefinition::Type::ServerModule)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Modules may only \"" GPF_TAG_MODULE_EXTENDS "\" modules of type \"" GPF_TAG_MODULE_TYPE_GAME_MODULE "\", " GPF_TAG_MODULE_TYPE_SERVER_MODULE "\"."));
|
||||
}
|
||||
|
||||
dependencyPair.first->m_parent = dependencyIterator->second;
|
||||
dependencyIterator->second->m_children.emplace_back(dependencyPair.first);
|
||||
}
|
||||
|
||||
// Populate modulesByType
|
||||
for (const auto& modulePtr : gem.m_modules)
|
||||
{
|
||||
gem.m_modulesByType[modulePtr->m_type].emplace_back(modulePtr);
|
||||
|
||||
// If this module is a GameModule, and there is no Editor override, apply it to Editor as well.
|
||||
if (modulePtr->m_type == ModuleDefinition::Type::GameModule)
|
||||
{
|
||||
bool foundEditorModule = false;
|
||||
// Check children for editor modules
|
||||
for (const auto& childWeak : modulePtr->m_children)
|
||||
{
|
||||
auto child = childWeak.lock();
|
||||
AZ_Assert(child, "Child somehow out of scope already!");
|
||||
if (child->m_type == ModuleDefinition::Type::EditorModule)
|
||||
{
|
||||
foundEditorModule = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundEditorModule)
|
||||
{
|
||||
// If no children are for editor, add module to editor list
|
||||
gem.m_modulesByType[ModuleDefinition::Type::EditorModule].emplace_back(modulePtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AZ::Success(AZStd::move(gem));
|
||||
}
|
||||
} // namespace Gems
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "GemRegistry/IGemRegistry.h"
|
||||
|
||||
#include <AzCore/std/functional.h>
|
||||
#include <AzCore/JSON/document.h>
|
||||
|
||||
namespace Gems
|
||||
{
|
||||
class GemDescription
|
||||
: public IGemDescription
|
||||
{
|
||||
public:
|
||||
GemDescription(const GemDescription& rhs);
|
||||
GemDescription(GemDescription&& rhs);
|
||||
~GemDescription() override = default;
|
||||
|
||||
// IGemDescription
|
||||
const AZ::Uuid& GetID() const override { return m_id; }
|
||||
const AZStd::string& GetName() const override { return m_name; }
|
||||
const AZStd::string& GetDisplayName() const override { return m_displayName.empty() ? m_name : m_displayName; }
|
||||
const GemVersion& GetVersion() const override { return m_version; }
|
||||
const AZStd::string& GetPath() const override { return m_path; }
|
||||
const AZStd::string& GetAbsolutePath() const override { return m_absolutePath; }
|
||||
const AZStd::string& GetSummary() const override { return m_summary; }
|
||||
const AZStd::string& GetIconPath() const override { return m_iconPath; }
|
||||
const AZStd::vector<AZStd::string>& GetTags() const override { return m_tags; }
|
||||
const ModuleDefinitionVector& GetModules() const override { return m_modules; }
|
||||
const ModuleDefinitionVector& GetModulesOfType(ModuleDefinition::Type type) const override { return m_modulesByType.find(type)->second; }
|
||||
const AZStd::string& GetEngineModuleClass() const override { return m_engineModuleClass; }
|
||||
const AZStd::vector<AZStd::shared_ptr<GemDependency> >& GetGemDependencies() const override { return m_gemDependencies; }
|
||||
const bool IsGameGem() const override { return m_gameGem; }
|
||||
const bool IsRequired() const override { return m_required; }
|
||||
const AZStd::shared_ptr<EngineDependency> GetEngineDependency() const override { return m_engineDependency; }
|
||||
|
||||
// ~IGemDescription
|
||||
|
||||
// Internal methods
|
||||
|
||||
/// Create GemDescription from Json.
|
||||
///
|
||||
/// \param[in] json Json object to parse. json may be modified during parse.
|
||||
/// \param[in] gemFolderPath Relative path from engine root to Gem folder.
|
||||
///
|
||||
/// \returns If successful, the GemDescription.
|
||||
/// If unsuccessful, an explanation why parsing failed.
|
||||
static AZ::Outcome<GemDescription, AZStd::string> CreateFromJson(
|
||||
rapidjson::Document& json,
|
||||
const AZStd::string& gemFolderPath,
|
||||
const AZStd::string& absoluteFilePath);
|
||||
|
||||
private:
|
||||
// Outsiders may not create an empty GemDescription
|
||||
GemDescription();
|
||||
|
||||
/// The ID of the Gem
|
||||
AZ::Uuid m_id;
|
||||
/// The name of the Gem
|
||||
AZStd::string m_name;
|
||||
/// The UI-friendly name of the Gem
|
||||
AZStd::string m_displayName;
|
||||
/// The version of the Gem
|
||||
GemVersion m_version;
|
||||
/// Relative path to Gem folder
|
||||
AZStd::string m_path;
|
||||
/// Absolute path to Gem folder
|
||||
AZStd::string m_absolutePath;
|
||||
/// Summary description of the Gem
|
||||
AZStd::string m_summary;
|
||||
/// Icon path of the gem
|
||||
AZStd::string m_iconPath;
|
||||
/// Tags to associate with the Gem
|
||||
AZStd::vector<AZStd::string> m_tags;
|
||||
/// List of modules produced by the Gem
|
||||
ModuleDefinitionVector m_modules;
|
||||
/// All modules to be loaded for a given function
|
||||
AZStd::unordered_map<ModuleDefinition::Type, ModuleDefinitionVector> m_modulesByType;
|
||||
/// The name of the engine module class to initialize
|
||||
AZStd::string m_engineModuleClass;
|
||||
/// A Gem's dependencies
|
||||
AZStd::vector<AZStd::shared_ptr<GemDependency> > m_gemDependencies;
|
||||
/// Flag to indicate if this is a Game GEM
|
||||
bool m_gameGem;
|
||||
/// Flag to indicate that this is a required GEM
|
||||
bool m_required;
|
||||
/// A Gem's engine dependency
|
||||
AZStd::shared_ptr<EngineDependency> m_engineDependency = nullptr;
|
||||
};
|
||||
} // namespace Gems
|
||||
@@ -1,425 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include "ProjectSettings.h"
|
||||
#include "GemRegistry.h"
|
||||
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/JSON/document.h>
|
||||
#include <AzCore/JSON/error/en.h>
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
namespace Gems
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR_IMPL(GemRegistry, AZ::SystemAllocator, 0)
|
||||
|
||||
AZ::Outcome<void, AZStd::string> GemRegistry::AddSearchPath(const SearchPath& searchPathIn, bool loadGemsNow)
|
||||
{
|
||||
SearchPath searchPath = searchPathIn;
|
||||
|
||||
// Remove trailing slash if present
|
||||
char lastChar = *(searchPath.m_path.end() - 1);
|
||||
if (lastChar == '/' ||
|
||||
lastChar == '\\')
|
||||
{
|
||||
AzFramework::StringFunc::RChop(searchPath.m_path, 1);
|
||||
}
|
||||
|
||||
if (AZStd::find(m_searchPaths.begin(), m_searchPaths.end(), searchPath) == m_searchPaths.end())
|
||||
{
|
||||
m_searchPaths.emplace_back(searchPath);
|
||||
}
|
||||
|
||||
if (loadGemsNow)
|
||||
{
|
||||
return LoadGemsFromDir(searchPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Success();
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> GemRegistry::LoadAllGemsFromDisk()
|
||||
{
|
||||
AZStd::string errorString;
|
||||
for (const auto& searchPath : m_searchPaths)
|
||||
{
|
||||
auto pathOutcome = LoadGemsFromDir(searchPath);
|
||||
if (!pathOutcome.IsSuccess())
|
||||
{
|
||||
errorString += pathOutcome.GetError() + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (errorString.empty())
|
||||
{
|
||||
return AZ::Success();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove trailing \n
|
||||
return AZ::Failure(errorString.substr(0, errorString.length() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> GemRegistry::LoadProject(const IProjectSettings& settings, bool resetPreviousProjects)
|
||||
{
|
||||
if (resetPreviousProjects)
|
||||
{
|
||||
m_gemDescs.clear();
|
||||
}
|
||||
for (const auto& pair : settings.GetGems())
|
||||
{
|
||||
const char* absolutePath = nullptr;
|
||||
|
||||
// First priority goes to the project root's folder
|
||||
AZStd::string testGemPath = settings.GetProjectRootPath();
|
||||
if (AzFramework::StringFunc::Path::ConstructFull(settings.GetProjectRootPath().c_str(), pair.second.m_path.c_str(), testGemPath, true))
|
||||
{
|
||||
if (AzFramework::StringFunc::Path::Join(testGemPath.c_str(), GEM_DEF_FILE, testGemPath))
|
||||
{
|
||||
if (AZ::IO::SystemFile::Exists(testGemPath.c_str()))
|
||||
{
|
||||
absolutePath = testGemPath.c_str();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto loadOutcome = LoadGemDescription(pair.second.m_path, absolutePath);
|
||||
if (!loadOutcome.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(loadOutcome.GetError());
|
||||
}
|
||||
}
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
IGemDescriptionConstPtr GemRegistry::GetGemDescription(const GemSpecifier& spec) const
|
||||
{
|
||||
IGemDescriptionConstPtr result;
|
||||
|
||||
auto idIt = m_gemDescs.find(spec.m_id);
|
||||
if (idIt != m_gemDescs.end())
|
||||
{
|
||||
auto versionIt = idIt->second.find(spec.m_version);
|
||||
if (versionIt != idIt->second.end())
|
||||
{
|
||||
result = AZStd::static_pointer_cast<IGemDescriptionConstPtr::element_type>(versionIt->second);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
IGemDescriptionConstPtr GemRegistry::GetLatestGem(const AZ::Uuid& uuid) const
|
||||
{
|
||||
IGemDescriptionConstPtr result;
|
||||
auto idIt = m_gemDescs.find(uuid);
|
||||
if (idIt != m_gemDescs.end())
|
||||
{
|
||||
GemVersion latestVersion;
|
||||
GemDescriptionPtr desc;
|
||||
|
||||
for (const auto& pair : idIt->second)
|
||||
{
|
||||
if (pair.first > latestVersion)
|
||||
{
|
||||
latestVersion = pair.first;
|
||||
desc = pair.second;
|
||||
}
|
||||
}
|
||||
|
||||
if (!latestVersion.IsZero())
|
||||
{
|
||||
result = AZStd::static_pointer_cast<IGemDescriptionConstPtr::element_type>(desc);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
AZStd::vector<IGemDescriptionConstPtr> GemRegistry::GetAllGemDescriptions() const
|
||||
{
|
||||
AZStd::vector<IGemDescriptionConstPtr> results;
|
||||
|
||||
for (auto && idIt : m_gemDescs)
|
||||
{
|
||||
for (auto && versionIt : idIt.second)
|
||||
{
|
||||
results.push_back(AZStd::static_pointer_cast<IGemDescriptionConstPtr::element_type>(versionIt.second));
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
AZStd::vector<IGemDescriptionConstPtr> GemRegistry::GetAllRequiredGemDescriptions() const
|
||||
{
|
||||
AZStd::vector<IGemDescriptionConstPtr> results;
|
||||
|
||||
for (auto && idIt : m_gemDescs)
|
||||
{
|
||||
for (auto && versionIt : idIt.second)
|
||||
{
|
||||
if (versionIt.second->IsRequired())
|
||||
{
|
||||
results.push_back(AZStd::static_pointer_cast<IGemDescriptionConstPtr::element_type>(versionIt.second));
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
IGemDescriptionConstPtr GemRegistry::GetProjectGemDescription(const AZStd::string& projectName) const
|
||||
{
|
||||
IGemDescriptionConstPtr result;
|
||||
|
||||
// We know searchPaths[0] is the old engine root, so we'll just use that to avoid a search
|
||||
AZStd::string gemFolderPath = projectName + "/Gem";
|
||||
auto descOutcome = ParseToGemDescription(gemFolderPath, nullptr);
|
||||
if (descOutcome)
|
||||
{
|
||||
result = AZStd::make_shared<GemDescription>(descOutcome.TakeValue());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
IProjectSettings* GemRegistry::CreateProjectSettings()
|
||||
{
|
||||
return aznew ProjectSettings(this);
|
||||
}
|
||||
|
||||
void GemRegistry::DestroyProjectSettings(IProjectSettings* settings)
|
||||
{
|
||||
delete settings;
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> GemRegistry::LoadGemsFromDir(const SearchPath& searchPath)
|
||||
{
|
||||
AZ::IO::LocalFileIO fileIo;
|
||||
AZStd::string errorString;
|
||||
|
||||
// Handles each file and directory found
|
||||
// Safe to capture all by reference because the find will run sync
|
||||
AZ::IO::LocalFileIO::FindFilesCallbackType fileFinderCb;
|
||||
fileFinderCb = [&](const char* fullPath) -> bool
|
||||
{
|
||||
if (fileIo.IsDirectory(fullPath))
|
||||
{
|
||||
// recurse into subdirectory
|
||||
// "*" filter will match all files/directories except specials ('.', '..', etc.) with the fewest compares
|
||||
fileIo.FindFiles(fullPath, "*", fileFinderCb);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZStd::string fileName;
|
||||
AzFramework::StringFunc::Path::GetFullFileName(fullPath, fileName);
|
||||
if (0 == azstricmp(fileName.c_str(), GEM_DEF_FILE))
|
||||
{
|
||||
// need relative path to gem folder, so strip searchPath from front and Gem.json from back
|
||||
AZStd::string gemFolderRelPath = fullPath + searchPath.m_path.length();
|
||||
AzFramework::StringFunc::Path::StripFullName(gemFolderRelPath);
|
||||
AzFramework::StringFunc::RChop(gemFolderRelPath, 1); // Remove trailing '/'
|
||||
auto skipPathFirstSepIndex = gemFolderRelPath.find_first_not_of("/\\");
|
||||
gemFolderRelPath = gemFolderRelPath.substr(skipPathFirstSepIndex);
|
||||
|
||||
auto loadOutcome = LoadGemDescription(gemFolderRelPath, fullPath);
|
||||
if (loadOutcome.IsSuccess() == false)
|
||||
{
|
||||
errorString += AZStd::string::format("Fail to load Gems from path %s disk. %s\n", searchPath.m_path.c_str(), loadOutcome.GetError().c_str());
|
||||
}
|
||||
|
||||
// We found the Gem.json file but we have to keep looking to support nested gems
|
||||
}
|
||||
}
|
||||
|
||||
return true; // keep searching
|
||||
};
|
||||
|
||||
// Scans subdirectories
|
||||
fileIo.FindFiles(searchPath.m_path.c_str(), searchPath.m_filter.c_str(), fileFinderCb);
|
||||
|
||||
if (errorString.empty())
|
||||
{
|
||||
return AZ::Success();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove trailing \n
|
||||
return AZ::Failure(errorString.substr(0, errorString.length() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Outcome<IGemDescriptionConstPtr, AZStd::string> GemRegistry::LoadGemDescription(const AZStd::string& gemFolderPath, const char* absoluteFilePath)
|
||||
{
|
||||
auto descOutcome = ParseToGemDescription(gemFolderPath, absoluteFilePath);
|
||||
if (!descOutcome)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("An error occurred while parsing %s: %s", gemFolderPath.c_str(), descOutcome.GetError().c_str()));
|
||||
}
|
||||
|
||||
auto desc = AZStd::make_shared<GemDescription>(descOutcome.TakeValue());
|
||||
|
||||
// If the Gem hasn't been loaded yet, add it's id to the root map
|
||||
auto idIt = m_gemDescs.find(desc->GetID());
|
||||
if (idIt == m_gemDescs.end())
|
||||
{
|
||||
idIt = m_gemDescs.emplace(desc->GetID(), AZStd::unordered_map<GemVersion, GemDescriptionPtr>()).first;
|
||||
}
|
||||
|
||||
// If the Gem's version doesn't exist, add it, otherwise update it
|
||||
auto versionIt = idIt->second.find(desc->GetVersion());
|
||||
if (versionIt == idIt->second.end())
|
||||
{
|
||||
idIt->second.emplace(desc->GetVersion(), AZStd::move(desc));
|
||||
}
|
||||
else
|
||||
{
|
||||
versionIt->second = desc;
|
||||
}
|
||||
|
||||
return AZ::Success(AZStd::static_pointer_cast<IGemDescriptionConstPtr::element_type>(desc));
|
||||
}
|
||||
|
||||
AZ::Outcome<IGemDescriptionConstPtr, AZStd::string> GemRegistry::ParseToGemDescriptionPtr(const AZStd::string& gemFolderRelPath, const char* absoluteFilePath)
|
||||
{
|
||||
auto descOutcome = ParseToGemDescription(gemFolderRelPath, absoluteFilePath);
|
||||
if (!descOutcome)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("An error occurred while parsing %s: %s", gemFolderRelPath.c_str(), descOutcome.GetError().c_str()));
|
||||
}
|
||||
|
||||
auto desc = AZStd::make_shared<GemDescription>(descOutcome.TakeValue());
|
||||
|
||||
return AZ::Success(AZStd::static_pointer_cast<IGemDescriptionConstPtr::element_type>(desc));
|
||||
}
|
||||
|
||||
AZ::Outcome<GemDescription, AZStd::string> GemRegistry::ParseToGemDescription(const AZStd::string& gemFolderPath, const char* absoluteFilePath) const
|
||||
{
|
||||
// do we have a pluggable, engine-compatible fileIO? For things like tools, we may not be plugged
|
||||
// into a game engine, and thus, we may need to use raw file io.
|
||||
AZ::IO::FileIOBase* fileReader = AZ::IO::FileIOBase::GetInstance();
|
||||
|
||||
// build absolute path to gem file
|
||||
AZStd::string filePath;
|
||||
|
||||
if (absoluteFilePath)
|
||||
{
|
||||
filePath = absoluteFilePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& searchPath : m_searchPaths)
|
||||
{
|
||||
// Append relative path to search path
|
||||
AzFramework::StringFunc::Path::Join(searchPath.m_path.c_str(), gemFolderPath.c_str(), filePath);
|
||||
// Append file name to file path
|
||||
AzFramework::StringFunc::Path::Join(filePath.c_str(), GEM_DEF_FILE, filePath);
|
||||
// note that paths are case sensitive on some systems.
|
||||
|
||||
if (fileReader)
|
||||
{
|
||||
if (fileReader->Exists(filePath.c_str()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AZ::IO::SystemFile::Exists(filePath.c_str()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// read json
|
||||
AZStd::string fileBuf;
|
||||
if (fileReader)
|
||||
{
|
||||
// an engine compatible file reader has been attached, so use that.
|
||||
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
|
||||
AZ::u64 fileSize = 0;
|
||||
if (!fileReader->Open(filePath.c_str(), AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeBinary, fileHandle))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Failed to read %s - file read failed.", filePath.c_str()));
|
||||
}
|
||||
|
||||
if ((!fileReader->Size(fileHandle, fileSize)) || (fileSize == 0))
|
||||
{
|
||||
fileReader->Close(fileHandle);
|
||||
return AZ::Failure(AZStd::string::format("Failed to read %s - file read failed.", filePath.c_str()));
|
||||
}
|
||||
|
||||
fileBuf.resize(fileSize);
|
||||
if (!fileReader->Read(fileHandle, fileBuf.data(), fileSize, true))
|
||||
{
|
||||
fileReader->Close(fileHandle);
|
||||
return AZ::Failure(AZStd::string::format("Failed to read %s - file read failed.", filePath.c_str()));
|
||||
}
|
||||
fileReader->Close(fileHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we don't have an engine file io, use raw file IO.
|
||||
AZ::IO::SystemFile rawFile;
|
||||
if (!rawFile.Open(filePath.c_str(), AZ::IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Failed to read %s - file read failed.", filePath.c_str()));
|
||||
}
|
||||
fileBuf.resize(rawFile.Length());
|
||||
if (fileBuf.size() == 0)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Failed to read %s - file read failed.", filePath.c_str()));
|
||||
}
|
||||
if (rawFile.Read(fileBuf.size(), fileBuf.data()) != fileBuf.size())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Failed to read %s - file read failed.", filePath.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
rapidjson::Document document;
|
||||
document.Parse(fileBuf.data());
|
||||
if (document.HasParseError())
|
||||
{
|
||||
const char* errorStr = rapidjson::GetParseError_En(document.GetParseError());
|
||||
return AZ::Failure(AZStd::string::format("Failed to parse %s: %s", filePath.c_str(), errorStr));
|
||||
}
|
||||
|
||||
return GemDescription::CreateFromJson(document, gemFolderPath, filePath);
|
||||
}
|
||||
} // namespace Gems
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DLL Exported Functions
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef AZ_MONOLITHIC_BUILD // Module init functions, only required when building as a DLL.
|
||||
AZ_DECLARE_MODULE_INITIALIZATION
|
||||
#endif//AZ_MONOLITHIC_BUILD
|
||||
|
||||
extern "C" AZ_DLL_EXPORT Gems::IGemRegistry * CreateGemRegistry()
|
||||
{
|
||||
return aznew Gems::GemRegistry();
|
||||
}
|
||||
|
||||
extern "C" AZ_DLL_EXPORT void DestroyGemRegistry(Gems::IGemRegistry* reg)
|
||||
{
|
||||
delete reg;
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "GemRegistry/IGemRegistry.h"
|
||||
#include "GemDescription.h"
|
||||
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
|
||||
// Constants
|
||||
#define UUID_STR_BUF_LEN 64
|
||||
#define GEMS_ASSETS_FOLDER "Assets"
|
||||
#define GEM_DEF_FILE "gem.json"
|
||||
#define GEM_DEF_FILE_VERSION 4
|
||||
#define GEMS_PROJECT_FILE "gems.json"
|
||||
#define GEMS_PROJECT_FILE_VERSION 2
|
||||
#define PROJECT_CONFIG_FILE "project.json"
|
||||
|
||||
// Gem project file JSON tags
|
||||
#define GPF_TAG_FORMAT_VERSION "GemFormatVersion"
|
||||
#define GPF_TAG_LIST_FORMAT_VERSION "GemListFormatVersion"
|
||||
#define GPF_TAG_NAME "Name"
|
||||
#define GPF_TAG_DISPLAY_NAME "DisplayName"
|
||||
#define GPF_TAG_GEM_ARRAY "Gems"
|
||||
#define GPF_TAG_UUID "Uuid"
|
||||
#define GPF_TAG_LY_VERSION "LumberyardVersion"
|
||||
#define GPF_TAG_VERSION "Version"
|
||||
#define GPF_TAG_DEPENDENCIES "Dependencies"
|
||||
#define GPF_TAG_VERSION_CONSTRAINTS "VersionConstraints"
|
||||
#define GPF_TAG_PATH "Path"
|
||||
#define GPF_TAG_MODULE_CLASS "EngineModuleClass"
|
||||
#define GPF_TAG_EDITOR_MODULE "EditorModule"
|
||||
#define GPF_TAG_SUMMARY "Summary"
|
||||
#define GPF_TAG_ICON_PATH "IconPath"
|
||||
#define GPF_TAG_TAGS "Tags"
|
||||
#define GPF_TAG_LINK_TYPE "LinkType"
|
||||
#define GPF_TAG_LINK_TYPE_DYNAMIC "Dynamic"
|
||||
#define GPF_TAG_LINK_TYPE_DYNAMIC_STATIC "DynamicStatic"
|
||||
#define GPF_TAG_LINK_TYPE_NO_CODE "NoCode"
|
||||
#define GPF_TAG_MODULES "Modules"
|
||||
#define GPF_TAG_MODULE_NAME "Name"
|
||||
#define GPF_TAG_MODULE_TYPE "Type"
|
||||
#define GPF_TAG_MODULE_TYPE_GAME_MODULE "GameModule"
|
||||
#define GPF_TAG_MODULE_TYPE_SERVER_MODULE "ServerModule"
|
||||
#define GPF_TAG_MODULE_TYPE_EDITOR_MODULE "EditorModule"
|
||||
#define GPF_TAG_MODULE_TYPE_STATIC_LIB "StaticLib"
|
||||
#define GPF_TAG_MODULE_TYPE_BUILDER "Builder"
|
||||
#define GPF_TAG_MODULE_TYPE_STANDALONE "Standalone"
|
||||
#define GPF_TAG_MODULE_EXTENDS "Extends"
|
||||
#define GPF_TAG_IS_GAME_GEM "IsGameGem"
|
||||
#define GPF_TAG_IS_REQUIRED "IsRequired"
|
||||
#define GPF_TAG_COMMENT "_comment"
|
||||
|
||||
namespace Gems
|
||||
{
|
||||
class GemRegistry
|
||||
: public IGemRegistry
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR_DECL;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// IGemRegistry
|
||||
AZ::Outcome<void, AZStd::string> AddSearchPath(const SearchPath& searchPath, bool loadGemsNow) override;
|
||||
AZ::Outcome<void, AZStd::string> LoadAllGemsFromDisk() override;
|
||||
AZ::Outcome<void, AZStd::string> LoadProject(const IProjectSettings& settings, bool resetPreviousProjects) override;
|
||||
|
||||
AZ::Outcome<IGemDescriptionConstPtr, AZStd::string> ParseToGemDescriptionPtr(const AZStd::string& gemFolderRelPath, const char* absoluteFilePath) override;
|
||||
IGemDescriptionConstPtr GetGemDescription(const GemSpecifier& spec) const override;
|
||||
IGemDescriptionConstPtr GetLatestGem(const AZ::Uuid& uuid) const override;
|
||||
AZStd::vector<IGemDescriptionConstPtr> GetAllGemDescriptions() const override;
|
||||
AZStd::vector<IGemDescriptionConstPtr> GetAllRequiredGemDescriptions() const override;
|
||||
IGemDescriptionConstPtr GetProjectGemDescription(const AZStd::string& projectName) const override;
|
||||
|
||||
IProjectSettings* CreateProjectSettings() override;
|
||||
void DestroyProjectSettings(IProjectSettings* settings) override;
|
||||
|
||||
~GemRegistry() override = default;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private:
|
||||
using GemDescriptionPtr = AZStd::shared_ptr<GemDescription>;
|
||||
|
||||
AZ::Outcome<void, AZStd::string> LoadGemsFromDir(const SearchPath& searchPath);
|
||||
// Pass nullptr for absoluteFolderPath to do a search for the Gem
|
||||
AZ::Outcome<IGemDescriptionConstPtr, AZStd::string> LoadGemDescription(const AZStd::string& gemFolderPath, const char* absoluteFilePath);
|
||||
AZ::Outcome<GemDescription, AZStd::string> ParseToGemDescription(const AZStd::string& gemFolderPath, const char* absoluteFilePath) const;
|
||||
|
||||
AZStd::vector<SearchPath> m_searchPaths; // Explictly ordered so that AddSearchPath() order matters
|
||||
AZStd::unordered_map<AZ::Uuid, AZStd::unordered_map<GemVersion, GemDescriptionPtr> > m_gemDescs;
|
||||
};
|
||||
} // namespace Gems
|
||||
|
||||
extern "C" AZ_DLL_EXPORT Gems::IGemRegistry * CreateGemRegistry();
|
||||
extern "C" AZ_DLL_EXPORT void DestroyGemRegistry(Gems::IGemRegistry* reg);
|
||||
@@ -1,596 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include "ProjectSettings.h"
|
||||
#include "GemRegistry.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/sort.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/Debug/Trace.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzFramework/FileFunc/FileFunc.h>
|
||||
#include <AzCore/PlatformIncl.h>
|
||||
|
||||
#include <AzCore/JSON/stringbuffer.h>
|
||||
#include <AzCore/JSON/prettywriter.h>
|
||||
#include <AzCore/JSON/error/en.h>
|
||||
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
|
||||
#if defined(AZ_PLATFORM_ANDROID)
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#define MAX_ERROR_STRING_SIZE 512
|
||||
|
||||
namespace Gems
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR_IMPL(ProjectSettings, AZ::SystemAllocator, 0)
|
||||
|
||||
ProjectSettings::ProjectSettings(GemRegistry* registry)
|
||||
: m_registry(registry)
|
||||
, m_initialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> ProjectSettings::Initialize(const AZStd::string& appRootFolder, const AZStd::string& projectSubFolder)
|
||||
{
|
||||
AZ_Assert(!m_initialized, "ProjectSettings has been initialized already.");
|
||||
|
||||
// Initialize the app root folder
|
||||
m_projectRootPath = appRootFolder;
|
||||
|
||||
// Project gems file lives in (ProjectFolder)/gems.json - which might be @assets@/gems.json or an absolute path (in tools)
|
||||
m_gemsSettingsFilePath = appRootFolder;
|
||||
AzFramework::StringFunc::Path::Join(m_gemsSettingsFilePath.c_str(), projectSubFolder.c_str(), m_gemsSettingsFilePath);
|
||||
AzFramework::StringFunc::Path::Join(m_gemsSettingsFilePath.c_str(), GEMS_PROJECT_FILE, m_gemsSettingsFilePath);
|
||||
|
||||
// Project config file lives in (ProjectFolder)/project.json - which might be @assets@/project.json or an absolute path (in tools)
|
||||
m_projectSettingsFilePath = appRootFolder;
|
||||
AzFramework::StringFunc::Path::Join(m_projectSettingsFilePath.c_str(), projectSubFolder.c_str(), m_projectSettingsFilePath);
|
||||
AzFramework::StringFunc::Path::Join(m_projectSettingsFilePath.c_str(), PROJECT_CONFIG_FILE, m_projectSettingsFilePath);
|
||||
|
||||
auto loadOutcome = LoadSettings();
|
||||
m_initialized = loadOutcome.IsSuccess();
|
||||
return loadOutcome;
|
||||
}
|
||||
|
||||
bool ProjectSettings::EnableGem(const ProjectGemSpecifier& spec)
|
||||
{
|
||||
auto it = m_gems.find(spec.m_id);
|
||||
if (it != m_gems.end())
|
||||
{
|
||||
// If the Gem is already enabled, update the version and path of the entry.
|
||||
it->second.m_version = spec.m_version;
|
||||
it->second.m_path = spec.m_path;
|
||||
}
|
||||
else
|
||||
{
|
||||
// create entry based on data from registry
|
||||
m_gems.insert(AZStd::make_pair(spec.m_id, spec));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProjectSettings::DisableGem(const GemSpecifier& spec)
|
||||
{
|
||||
auto it = m_gems.find(spec.m_id);
|
||||
// If the Gem is enabled at the version specified, disable it.
|
||||
if (it != m_gems.end())
|
||||
{
|
||||
if (spec.m_version != it->second.m_version)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_gems.erase(it);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProjectSettings::IsGemEnabled(const GemSpecifier& spec) const
|
||||
{
|
||||
auto it = m_gems.find(spec.m_id);
|
||||
return it != m_gems.end()
|
||||
&& it->second.m_version == spec.m_version;
|
||||
}
|
||||
|
||||
bool ProjectSettings::IsGemEnabled(const AZ::Uuid& id, const AZStd::vector<AZStd::string>& versionConstraints) const
|
||||
{
|
||||
AZStd::shared_ptr<GemDependency> dependency = AZStd::make_shared<GemDependency>();
|
||||
dependency->SetID(id);
|
||||
auto parseOutcome = dependency->ParseVersions(versionConstraints);
|
||||
if (!parseOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Assert(false, parseOutcome.GetError().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsGemDependencyMet(dependency);
|
||||
}
|
||||
|
||||
bool ProjectSettings::IsGemDependencyMet(const AZStd::shared_ptr<GemDependency> dep) const
|
||||
{
|
||||
// Gems can depend on other Gems
|
||||
auto it = m_gems.find(dep->GetID());
|
||||
return it != m_gems.end()
|
||||
&& dep->IsFullfilledBy(it->second);
|
||||
}
|
||||
|
||||
bool ProjectSettings::IsEngineDependencyMet(const AZStd::shared_ptr<EngineDependency> dep, const EngineVersion& againstVersion) const
|
||||
{
|
||||
EngineSpecifier engineSpecifier(AZ::Uuid::CreateNull(), againstVersion);
|
||||
return dep->IsFullfilledBy(engineSpecifier);
|
||||
}
|
||||
|
||||
class GemDependencyInfo : public GemDependency
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
GemDependencyInfo(IGemDescriptionConstPtr gem)
|
||||
: GemDependency()
|
||||
, m_gem{gem}
|
||||
{
|
||||
}
|
||||
|
||||
IGemDescriptionConstPtr GetGem() const
|
||||
{
|
||||
return m_gem;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
IGemDescriptionConstPtr m_gem;
|
||||
|
||||
};
|
||||
|
||||
AZ::Outcome<void, AZStd::string> ProjectSettings::ValidateDependencies(const EngineVersion& engineVersion) const
|
||||
{
|
||||
AZStd::unordered_map<AZ::Uuid, GemDependencyInfo> globalDeps;
|
||||
|
||||
// Build list of required Gems
|
||||
for (const auto& pair : m_gems)
|
||||
{
|
||||
const ProjectGemSpecifier& spec = pair.second;
|
||||
|
||||
auto gem = m_registry->GetGemDescription(spec);
|
||||
if (!gem)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Gem with Id \"%s\" not found.", pair.first.ToString<AZStd::string>().c_str()));
|
||||
}
|
||||
|
||||
for (auto && gemDep : gem->GetGemDependencies())
|
||||
{
|
||||
const AZ::Uuid id = gemDep->GetID();
|
||||
GemDependency* dep;
|
||||
|
||||
// If the dependency isn't tracked globally, create a new one
|
||||
auto depIt = globalDeps.find(id);
|
||||
if (depIt == globalDeps.end())
|
||||
{
|
||||
globalDeps.insert(AZStd::make_pair(id, GemDependencyInfo(gem)));
|
||||
dep = &globalDeps.at(id);
|
||||
dep->m_id = id;
|
||||
}
|
||||
else
|
||||
{
|
||||
dep = &depIt->second;
|
||||
}
|
||||
|
||||
// These bounds should be normalized before verification to make sure there aren't conflicting bounds
|
||||
dep->m_bounds.insert(dep->m_bounds.end(), gemDep->GetBounds().begin(), gemDep->GetBounds().end());
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string errorString;
|
||||
bool isTreeValid = true;
|
||||
|
||||
// Verify all engine dependencies are met
|
||||
for(const auto& pair : m_gems)
|
||||
{
|
||||
const ProjectGemSpecifier& spec = pair.second;
|
||||
|
||||
auto gem = m_registry->GetGemDescription(spec);
|
||||
if (!gem)
|
||||
{
|
||||
errorString += AZStd::string::format("Gem with Id \"%s\" not found.", pair.first.ToString<AZStd::string>().c_str());
|
||||
isTreeValid = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// do not verify the engine version if input is default constructed
|
||||
if (engineVersion == EngineVersion())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check the Gem's engine dependency
|
||||
auto engineDepPtr = gem->GetEngineDependency();
|
||||
if (engineDepPtr && !IsEngineDependencyMet(engineDepPtr, engineVersion))
|
||||
{
|
||||
AZStd::string errmsg = AZStd::string::format("Gem with Id \"%s\" does not meet the Lumberyard engine version requirement.\n",
|
||||
pair.first.ToString<AZStd::string>().c_str());
|
||||
|
||||
// do not force an assertion to happen here, we are just printing the warning and letting the user
|
||||
// decide on how to handle it if the engine start up fails.
|
||||
errorString += errmsg;
|
||||
AZ_Warning("GemRegistry", false, errmsg.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// attempt to construct a complete gem registry for unmet dependency ID to name resolution
|
||||
GemRegistry completeRegistry;
|
||||
|
||||
const char* gemsSearchFilter = "Gems";
|
||||
|
||||
const char* appRoot = nullptr;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(appRoot, &AzFramework::ApplicationRequests::GetAppRoot);
|
||||
if (appRoot)
|
||||
{
|
||||
completeRegistry.AddSearchPath({ appRoot, gemsSearchFilter }, false);
|
||||
}
|
||||
|
||||
const char* engineRoot = nullptr;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot);
|
||||
if (engineRoot)
|
||||
{
|
||||
completeRegistry.AddSearchPath({ engineRoot, gemsSearchFilter }, false);
|
||||
}
|
||||
|
||||
completeRegistry.LoadAllGemsFromDisk();
|
||||
|
||||
// Verify all gems dependencies are all met
|
||||
for (auto && pair : globalDeps)
|
||||
{
|
||||
const GemDependencyInfo& dep = pair.second;
|
||||
|
||||
// Find candidate in project's listed gems
|
||||
auto candidateIt = m_gems.find(dep.GetID());
|
||||
if (candidateIt == m_gems.end())
|
||||
{
|
||||
// no candidate found
|
||||
char depIdStr[UUID_STR_BUF_LEN];
|
||||
dep.GetID().ToString(depIdStr, UUID_STR_BUF_LEN, true, true);
|
||||
|
||||
char gemIdStr[UUID_STR_BUF_LEN];
|
||||
dep.GetGem()->GetID().ToString(gemIdStr, UUID_STR_BUF_LEN, true, true);
|
||||
|
||||
// don't care about the version, just need the gem name
|
||||
IGemDescriptionConstPtr depDesc = completeRegistry.GetLatestGem(dep.GetID());
|
||||
if (depDesc)
|
||||
{
|
||||
errorString += AZStd::string::format(
|
||||
"Gem \"%s\" (%s) dependency on Gem \"%s\" (%s) is unmet.\n",
|
||||
dep.GetGem()->GetDisplayName().c_str(),
|
||||
gemIdStr,
|
||||
depDesc->GetDisplayName().c_str(),
|
||||
depIdStr
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
errorString += AZStd::string::format(
|
||||
"Gem \"%s\" (%s) dependency on unresolved Gem with ID %s is unmet.\n",
|
||||
dep.GetGem()->GetDisplayName().c_str(),
|
||||
gemIdStr,
|
||||
depIdStr
|
||||
);
|
||||
}
|
||||
|
||||
isTreeValid = false;
|
||||
}
|
||||
else if (!dep.IsFullfilledBy(candidateIt->second))
|
||||
{
|
||||
// candidate found, but it doesn't fulfill all dependency requirements
|
||||
AZStd::string boundsStr;
|
||||
for (auto && bound : dep.m_bounds)
|
||||
{
|
||||
if (boundsStr.length() == 0)
|
||||
{
|
||||
boundsStr = bound.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
boundsStr += ", " + bound.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
char depIdStr[UUID_STR_BUF_LEN];
|
||||
dep.GetID().ToString(depIdStr, UUID_STR_BUF_LEN, true, true);
|
||||
|
||||
char gemIdStr[UUID_STR_BUF_LEN];
|
||||
dep.GetGem()->GetID().ToString(gemIdStr, UUID_STR_BUF_LEN, true, true);
|
||||
|
||||
// don't care about the version, just need the gem name
|
||||
IGemDescriptionConstPtr depDesc = completeRegistry.GetLatestGem(dep.GetID());
|
||||
if (depDesc)
|
||||
{
|
||||
errorString += AZStd::string::format(
|
||||
"Gem \"%s\" (%s) dependency on Gem \"%s\" (%s) is unmet. It must fall within the following version bounds: [%s]\n",
|
||||
dep.GetGem()->GetDisplayName().c_str(),
|
||||
gemIdStr,
|
||||
depDesc->GetDisplayName().c_str(),
|
||||
depIdStr,
|
||||
boundsStr.c_str()
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
errorString += AZStd::string::format(
|
||||
"Gem \"%s\" (%s) dependency on unresolved Gem with ID %s is unmet. It must fall within the following version bounds: [%s]\n",
|
||||
dep.GetGem()->GetDisplayName().c_str(),
|
||||
gemIdStr,
|
||||
depIdStr,
|
||||
boundsStr.c_str()
|
||||
);
|
||||
}
|
||||
|
||||
isTreeValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isTreeValid)
|
||||
{
|
||||
return AZ::Failure(errorString);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> ProjectSettings::Save() const
|
||||
{
|
||||
using namespace AZ::IO;
|
||||
FileIOBase* fileIo = FileIOBase::GetInstance();
|
||||
|
||||
HandleType projectSettingsHandle = InvalidHandle;
|
||||
if (fileIo->Open(m_gemsSettingsFilePath.c_str(), OpenMode::ModeWrite | OpenMode::ModeText, projectSettingsHandle))
|
||||
{
|
||||
rapidjson::Document jsonRep = GetJsonRepresentation();
|
||||
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
|
||||
jsonRep.Accept(writer);
|
||||
|
||||
AZ::u64 bytesWritten = 0;
|
||||
if (!fileIo->Write(projectSettingsHandle, buffer.GetString(), buffer.GetSize(), &bytesWritten))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Failed to write Gems settings to file: %s", m_gemsSettingsFilePath.c_str()));
|
||||
}
|
||||
|
||||
if (bytesWritten != buffer.GetSize())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Failed to write complete Gems settings to file: %s", m_gemsSettingsFilePath.c_str()));
|
||||
}
|
||||
|
||||
fileIo->Close(projectSettingsHandle);
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
else
|
||||
{
|
||||
char errorBuffer[MAX_ERROR_STRING_SIZE];
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
|
||||
nullptr,
|
||||
GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
errorBuffer,
|
||||
MAX_ERROR_STRING_SIZE,
|
||||
nullptr);
|
||||
#else
|
||||
azstrerror_s(errorBuffer, MAX_ERROR_STRING_SIZE, errno);
|
||||
#endif // defined(AZ_PLATFORM_WINDOWS)
|
||||
return AZ::Failure(AZStd::string::format("Failed to open %s for write: %s", m_gemsSettingsFilePath.c_str(), errorBuffer));
|
||||
}
|
||||
}
|
||||
|
||||
const AZStd::string& ProjectSettings::GetProjectName() const
|
||||
{
|
||||
return m_projectName;
|
||||
}
|
||||
|
||||
const AZStd::string& ProjectSettings::GetProjectRootPath() const
|
||||
{
|
||||
return m_projectRootPath;
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> ProjectSettings::LoadSettings()
|
||||
{
|
||||
// an engine compatible file reader has been attached, so use that.
|
||||
AZ::IO::FileIOBase* fileReader = AZ::IO::FileIOBase::GetInstance();
|
||||
|
||||
// Read and parse the gems.json file
|
||||
{
|
||||
AZ::IO::Path gemsSettingsPath(m_gemsSettingsFilePath);
|
||||
auto readGemsJsonResult = AzFramework::FileFunc::ReadJsonFile(gemsSettingsPath, fileReader);
|
||||
if (!readGemsJsonResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Failed to read Json file %s: %s",
|
||||
m_gemsSettingsFilePath.c_str(), readGemsJsonResult.GetError().c_str()));
|
||||
}
|
||||
auto parseGemsJsonResult = ParseGemsJson(readGemsJsonResult.GetValue());
|
||||
if (!parseGemsJsonResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Failed to parse Json file %s: %s",
|
||||
m_gemsSettingsFilePath.c_str(), parseGemsJsonResult.GetError().c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
// Read and parse the project.json file
|
||||
{
|
||||
AZ::IO::Path projectSettingsPath(m_projectSettingsFilePath);
|
||||
auto readProjectJsonResult = AzFramework::FileFunc::ReadJsonFile(projectSettingsPath, fileReader);
|
||||
if (!readProjectJsonResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Failed to read Json file %s: %s",
|
||||
m_gemsSettingsFilePath.c_str(), readProjectJsonResult.GetError().c_str()));
|
||||
}
|
||||
auto parseProjectJsonResult = ParseProjectJson(readProjectJsonResult.GetValue());
|
||||
if (!parseProjectJsonResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Failed to parse Json file %s: %s",
|
||||
m_gemsSettingsFilePath.c_str(), parseProjectJsonResult.GetError().c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
rapidjson::Document ProjectSettings::GetJsonRepresentation() const
|
||||
{
|
||||
rapidjson::Document rootObj(rapidjson::kObjectType);
|
||||
rootObj.AddMember<int>(GPF_TAG_LIST_FORMAT_VERSION, GEMS_PROJECT_FILE_VERSION, rootObj.GetAllocator());
|
||||
|
||||
// We want to write out Gems in the same order each time.
|
||||
// Create vector for sorting.
|
||||
AZStd::vector<const ProjectGemSpecifier*> sortedGems(m_gems.size());
|
||||
auto transformFn = [](const ProjectGemSpecifierMap::value_type& pair) { return &pair.second; };
|
||||
AZStd::transform(m_gems.begin(), m_gems.end(), sortedGems.begin(), transformFn);
|
||||
|
||||
// we'll sort based on ID.
|
||||
AZStd::sort(sortedGems.begin(), sortedGems.end(), [](const ProjectGemSpecifier* a, const ProjectGemSpecifier* b) -> bool
|
||||
{
|
||||
return a->m_id < b->m_id;
|
||||
});
|
||||
|
||||
auto addMember = [&rootObj](rapidjson::Value& obj, const char* key, const char* str)
|
||||
{
|
||||
rapidjson::Value k(rapidjson::StringRef(key), rootObj.GetAllocator());
|
||||
rapidjson::Value v(rapidjson::StringRef(str), rootObj.GetAllocator());
|
||||
obj.AddMember(k.Move(), v.Move(), rootObj.GetAllocator());
|
||||
};
|
||||
|
||||
// build Gems array
|
||||
rapidjson::Value gemsArray(rapidjson::kArrayType);
|
||||
for (const ProjectGemSpecifier* gemSpec : sortedGems)
|
||||
{
|
||||
char idStr[UUID_STR_BUF_LEN];
|
||||
gemSpec->m_id.ToString(idStr, UUID_STR_BUF_LEN, false, false);
|
||||
AZStd::to_lower(idStr, idStr + strlen(idStr));
|
||||
|
||||
AZStd::string path = gemSpec->m_path;
|
||||
// Replace '\' with '/'
|
||||
AZStd::replace(path.begin(), path.end(), '\\', '/');
|
||||
// Remove trailing slash
|
||||
if (*path.rbegin() == '/')
|
||||
{
|
||||
path.pop_back();
|
||||
}
|
||||
|
||||
rapidjson::Value gemObj(rapidjson::kObjectType);
|
||||
addMember(gemObj, GPF_TAG_PATH, path.c_str());
|
||||
addMember(gemObj, GPF_TAG_UUID, idStr);
|
||||
addMember(gemObj, GPF_TAG_VERSION, gemSpec->m_version.ToString().c_str());
|
||||
|
||||
// write name in comment (if possible)
|
||||
if (IGemDescriptionConstPtr gemDesc = m_registry->GetGemDescription(*gemSpec))
|
||||
{
|
||||
addMember(gemObj, GPF_TAG_COMMENT, gemDesc->GetName().c_str());
|
||||
}
|
||||
|
||||
gemsArray.PushBack(gemObj, rootObj.GetAllocator());
|
||||
}
|
||||
rootObj.AddMember(GPF_TAG_GEM_ARRAY, gemsArray, rootObj.GetAllocator());
|
||||
|
||||
return rootObj;
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> ProjectSettings::ParseGemsJson(const rapidjson::Document& jsonRep)
|
||||
{
|
||||
// check version
|
||||
if (!RAPIDJSON_IS_VALID_MEMBER(jsonRep, GPF_TAG_LIST_FORMAT_VERSION, IsInt))
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_LIST_FORMAT_VERSION " number is required."));
|
||||
}
|
||||
|
||||
int gemListFormatVersion = jsonRep[GPF_TAG_LIST_FORMAT_VERSION].GetInt();
|
||||
if (gemListFormatVersion != GEMS_PROJECT_FILE_VERSION)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
GPF_TAG_LIST_FORMAT_VERSION " is version %d, but %d is expected.",
|
||||
gemListFormatVersion,
|
||||
GEMS_PROJECT_FILE_VERSION));
|
||||
}
|
||||
|
||||
// read gems
|
||||
if (!RAPIDJSON_IS_VALID_MEMBER(jsonRep, GPF_TAG_GEM_ARRAY, IsArray))
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_GEM_ARRAY " list is required"));
|
||||
}
|
||||
|
||||
const rapidjson::Value& gemList = jsonRep[GPF_TAG_GEM_ARRAY];
|
||||
const auto& end = gemList.End();
|
||||
for (auto it = gemList.Begin(); it != end; ++it)
|
||||
{
|
||||
const auto& elem = *it;
|
||||
|
||||
// gem id
|
||||
if (!RAPIDJSON_IS_VALID_MEMBER(elem, GPF_TAG_UUID, IsString))
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_UUID " string is required for Gem."));
|
||||
}
|
||||
const char* idStr = elem[GPF_TAG_UUID].GetString();
|
||||
AZ::Uuid id = AZ::Uuid::CreateString(idStr);
|
||||
if (id.IsNull())
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_UUID " string is invalid for Gem."));
|
||||
}
|
||||
|
||||
// gem version
|
||||
if (!RAPIDJSON_IS_VALID_MEMBER(elem, GPF_TAG_VERSION, IsString))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
GPF_TAG_VERSION " string is missing for Gem with ID %s.",
|
||||
idStr));
|
||||
}
|
||||
|
||||
auto versionOutcome = GemVersion::ParseFromString(elem[GPF_TAG_VERSION].GetString());
|
||||
if (!versionOutcome)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
GPF_TAG_VERSION " string is invalid for Gem with ID %s: %s",
|
||||
idStr, versionOutcome.GetError().c_str()));
|
||||
}
|
||||
GemVersion version = versionOutcome.GetValue();
|
||||
|
||||
// gem path
|
||||
if (!RAPIDJSON_IS_VALID_MEMBER(elem, GPF_TAG_PATH, IsString))
|
||||
{
|
||||
return AZ::Failure(AZStd::string(GPF_TAG_PATH " string is required for Gem"));
|
||||
}
|
||||
const char* path = elem[GPF_TAG_PATH].GetString();
|
||||
|
||||
EnableGem(ProjectGemSpecifier(id, version, path));
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> ProjectSettings::ParseProjectJson(const rapidjson::Document& json)
|
||||
{
|
||||
// For now, we only
|
||||
static const char* project_name_key = "project_name";
|
||||
if (!RAPIDJSON_IS_VALID_MEMBER(json, project_name_key, IsString))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Missing/Invalid key '%s' in project.json.", project_name_key));
|
||||
}
|
||||
m_projectName = json[project_name_key].GetString();
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
} // namespace Gems
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/JSON/document.h>
|
||||
|
||||
#include "GemRegistry.h"
|
||||
|
||||
namespace Gems
|
||||
{
|
||||
class ProjectSettings
|
||||
: public IProjectSettings
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR_DECL
|
||||
|
||||
ProjectSettings(GemRegistry* registry);
|
||||
~ProjectSettings() override = default;
|
||||
|
||||
// IProjectSettings
|
||||
AZ::Outcome<void, AZStd::string> Initialize(const AZStd::string& appRootFolder, const AZStd::string& projectSubFolder) override;
|
||||
|
||||
bool EnableGem(const ProjectGemSpecifier& spec) override;
|
||||
bool DisableGem(const GemSpecifier& spec) override;
|
||||
bool IsGemEnabled(const GemSpecifier& spec) const override;
|
||||
bool IsGemEnabled(const AZ::Uuid& id, const AZStd::vector<AZStd::string>& versionConstraints) const override;
|
||||
bool IsGemDependencyMet(const AZStd::shared_ptr<GemDependency> dep) const override;
|
||||
bool IsEngineDependencyMet(const AZStd::shared_ptr<EngineDependency> dep, const EngineVersion& againstVersion) const override;
|
||||
|
||||
const ProjectGemSpecifierMap& GetGems() const override { return m_gems; }
|
||||
void SetGems(const ProjectGemSpecifierMap& newGemMap) override { m_gems = newGemMap; }
|
||||
AZ::Outcome<void, AZStd::string> ValidateDependencies(const EngineVersion& engineVersion) const override;
|
||||
|
||||
AZ::Outcome<void, AZStd::string> Save() const override;
|
||||
const AZStd::string& GetProjectName() const override;
|
||||
const AZStd::string& GetProjectRootPath() const override;
|
||||
|
||||
// ~IProjectSettings
|
||||
|
||||
// Internal methods
|
||||
/// Loads settings from the path provided by m_settingsFilePath
|
||||
AZ::Outcome<void, AZStd::string> LoadSettings();
|
||||
/// Converts the ProjectGemSpecifierMap (m_gems) into it's Json representation for saving
|
||||
rapidjson::Document GetJsonRepresentation() const;
|
||||
/// Converts GEMS Json into the ProjectGemSpecifierMap (m_gems)
|
||||
AZ::Outcome<void, AZStd::string> ParseGemsJson(const rapidjson::Document& json);
|
||||
/// Reads from project.json to initialize project-specific values
|
||||
AZ::Outcome<void, AZStd::string> ParseProjectJson(const rapidjson::Document& json);
|
||||
|
||||
private:
|
||||
ProjectGemSpecifierMap m_gems;
|
||||
GemRegistry* m_registry;
|
||||
AZStd::string m_gemsSettingsFilePath;
|
||||
AZStd::string m_projectSettingsFilePath;
|
||||
AZStd::string m_projectName;
|
||||
AZStd::string m_projectRootPath;
|
||||
bool m_initialized;
|
||||
};
|
||||
} // namespace Gems
|
||||
@@ -1,408 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <GemRegistry/Dependency.h>
|
||||
|
||||
using namespace Gems;
|
||||
using GemComp = GemDependency::Bound::Comparison;
|
||||
using EngineComp = EngineDependency::Bound::Comparison;
|
||||
|
||||
class DependencyTest
|
||||
: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
enum class Bound
|
||||
{
|
||||
Upper,
|
||||
Lower
|
||||
};
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
m_errCopyCtorFailed = "Failed to copy Dependency instance.";
|
||||
m_errParseInvalidSucceeded = "Failed to report invalid version.";
|
||||
m_errParseFailed = "Failed to parse valid version string.";
|
||||
m_errIncorrectBoundCount = "Improper number of bounds generated.";
|
||||
m_errComparisonMismatch = "Comparison generated does not match ";
|
||||
m_errSpecFulfillsFailed = "Spec that fulfills dependency was reported as invalid.";
|
||||
m_errInvalidSpecFulfills = "Spec that does not fulfill dependency was reported as valid.";
|
||||
m_errToStringIncorrect = "ToString result is incorrect.";
|
||||
}
|
||||
|
||||
const char* ErrComparisonMismatch(const char* op)
|
||||
{
|
||||
return AZStd::string::format("Comparison generated does not match %s.", op).c_str();
|
||||
}
|
||||
|
||||
const char* ErrInvalidBound(Bound b)
|
||||
{
|
||||
return AZStd::string::format("Version generated does not match %s bound.", b == Bound::Upper ? "upper" : "lower").c_str();
|
||||
}
|
||||
|
||||
const char* m_errCopyCtorFailed;
|
||||
const char* m_errParseInvalidSucceeded;
|
||||
const char* m_errParseFailed;
|
||||
const char* m_errIncorrectBoundCount;
|
||||
const char* m_errComparisonMismatch;
|
||||
const char* m_errSpecFulfillsFailed;
|
||||
const char* m_errInvalidSpecFulfills;
|
||||
const char* m_errToStringIncorrect;
|
||||
};
|
||||
|
||||
TEST_F(DependencyTest, MiscTests)
|
||||
{
|
||||
{
|
||||
GemDependency dep1;
|
||||
dep1.m_id = AZ::Uuid::CreateRandom();
|
||||
dep1.m_bounds.push_back(GemDependency::Bound{});
|
||||
|
||||
GemDependency dep2{
|
||||
dep1
|
||||
};
|
||||
EXPECT_EQ(dep2.m_id, dep1.m_id) << m_errCopyCtorFailed;
|
||||
EXPECT_EQ(dep2.m_bounds.size(), dep1.m_bounds.size()) << m_errCopyCtorFailed;
|
||||
}
|
||||
|
||||
{
|
||||
EngineDependency dep1;
|
||||
dep1.m_id = AZ::Uuid::CreateRandom();
|
||||
dep1.m_bounds.push_back(EngineDependency::Bound{});
|
||||
|
||||
EngineDependency dep2 {
|
||||
dep1
|
||||
};
|
||||
EXPECT_EQ(dep2.m_id, dep1.m_id) << m_errCopyCtorFailed;
|
||||
EXPECT_EQ(dep2.m_bounds.size(), dep1.m_bounds.size()) << m_errCopyCtorFailed;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DependencyTest, FailureTest)
|
||||
{
|
||||
{
|
||||
GemDependency dep;
|
||||
|
||||
EXPECT_FALSE(dep.ParseVersions({ "Not a version requirement!" }).IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
EXPECT_FALSE(dep.ParseVersions({ "~>1" }).IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
EXPECT_FALSE(dep.ParseVersions({ "~>1.invalid" }).IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
}
|
||||
|
||||
{
|
||||
EngineDependency dep;
|
||||
|
||||
EXPECT_FALSE(dep.ParseVersions({ "Not a version requirement!" }).IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
EXPECT_FALSE(dep.ParseVersions({ "~>1" }).IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
EXPECT_FALSE(dep.ParseVersions({ "~>1.invalid" }).IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DependencyTest, TwiddleWakkaTest)
|
||||
{
|
||||
{
|
||||
GemDependency dep;
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ "~>1.0" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 1) << m_errIncorrectBoundCount;
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetComparison(), GemComp::TwiddleWakka) << ErrComparisonMismatch("Twiddle Wakka");
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetVersion(), GemVersion(1, 0, 0)) << ErrInvalidBound(Bound::Lower);
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ "~>1.0.1" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 1) << m_errIncorrectBoundCount;
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetComparison(), GemComp::TwiddleWakka) << ErrComparisonMismatch("Twiddle Wakka");
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetVersion(), GemVersion(1, 0, 1)) << ErrInvalidBound(Bound::Lower);
|
||||
}
|
||||
|
||||
{
|
||||
EngineDependency dep;
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ "~>1.0" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 1) << m_errIncorrectBoundCount;
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetComparison(), EngineComp::TwiddleWakka) << ErrComparisonMismatch("Twiddle Wakka");
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetVersion(), EngineVersion({ 1, 0, 0, 0 })) << ErrInvalidBound(Bound::Lower);
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ "~>1.0.1" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 1) << m_errIncorrectBoundCount;
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetComparison(), EngineComp::TwiddleWakka) << ErrComparisonMismatch("Twiddle Wakka");
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetVersion(), EngineVersion({ 1, 0, 1, 0 })) << ErrInvalidBound(Bound::Lower);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DependencyTest, SingleVersionTest)
|
||||
{
|
||||
{
|
||||
GemDependency dep;
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ ">=1.0.0" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 1) << m_errIncorrectBoundCount;
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetComparison(), GemComp::GreaterThan | GemComp::EqualTo) << ErrComparisonMismatch(">=");
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetVersion(), GemVersion(1, 0, 0)) << ErrInvalidBound(Bound::Lower);
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ ">2.20.0" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 1) << m_errIncorrectBoundCount;
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetComparison(), GemComp::GreaterThan) << ErrComparisonMismatch(">");
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetVersion(), GemVersion(2, 20, 0)) << ErrInvalidBound(Bound::Lower);
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ "==3.4.0" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 1) << m_errIncorrectBoundCount;
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetComparison(), GemComp::EqualTo) << ErrComparisonMismatch("==");
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetVersion(), GemVersion(3, 4, 0)) << ErrInvalidBound(Bound::Lower);
|
||||
}
|
||||
|
||||
{
|
||||
EngineDependency dep;
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ ">=1.0.0.0" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 1) << m_errIncorrectBoundCount;
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetComparison(), EngineComp::GreaterThan | EngineComp::EqualTo) << ErrComparisonMismatch(">=");
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetVersion(), EngineVersion({ 1, 0, 0, 0 })) << ErrInvalidBound(Bound::Lower);
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ ">2.20.0.0" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 1) << m_errIncorrectBoundCount;
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetComparison(), EngineComp::GreaterThan) << ErrComparisonMismatch(">");
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetVersion(), EngineVersion({ 2, 20, 0, 0 })) << ErrInvalidBound(Bound::Lower);
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ "==3.4.0.0" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 1) << m_errIncorrectBoundCount;
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetComparison(), EngineComp::EqualTo) << ErrComparisonMismatch("==");
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetVersion(), EngineVersion({ 3, 4, 0, 0 })) << ErrInvalidBound(Bound::Lower);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DependencyTest, DoubleVersionTest)
|
||||
{
|
||||
{
|
||||
GemDependency dep;
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ ">=1.0.0", "<2.0.0" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 2) << m_errIncorrectBoundCount;
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetComparison(), GemComp::GreaterThan | GemComp::EqualTo) << ErrComparisonMismatch(">=");
|
||||
EXPECT_EQ(dep.GetBounds()[1].GetComparison(), GemComp::LessThan) << ErrComparisonMismatch("<");
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetVersion(), GemVersion(1, 0, 0)) << ErrInvalidBound(Bound::Lower);
|
||||
EXPECT_EQ(dep.GetBounds()[1].GetVersion(), GemVersion(2, 0, 0)) << ErrInvalidBound(Bound::Upper);
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ ">2.20.0", "<=3" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 2) << m_errIncorrectBoundCount;
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetComparison(), GemComp::GreaterThan) << ErrComparisonMismatch(">");
|
||||
EXPECT_EQ(dep.GetBounds()[1].GetComparison(), GemComp::LessThan | GemComp::EqualTo) << ErrComparisonMismatch("<=");
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetVersion(), GemVersion(2, 20, 0)) << ErrInvalidBound(Bound::Lower);
|
||||
EXPECT_EQ(dep.GetBounds()[1].GetVersion(), GemVersion(3, 0, 0)) << ErrInvalidBound(Bound::Upper);
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ "<3.4.0", ">=20.1" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 2) << m_errIncorrectBoundCount;
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetComparison(), GemComp::LessThan) << ErrComparisonMismatch("<");
|
||||
EXPECT_EQ(dep.GetBounds()[1].GetComparison(), GemComp::GreaterThan | GemComp::EqualTo) << ErrComparisonMismatch(">=");
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetVersion(), GemVersion(3, 4, 0)) << ErrInvalidBound(Bound::Lower);
|
||||
EXPECT_EQ(dep.GetBounds()[1].GetVersion(), GemVersion(20, 1, 0)) << ErrInvalidBound(Bound::Upper);
|
||||
}
|
||||
|
||||
{
|
||||
EngineDependency dep;
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ ">=1.0.0.0", "<2.0.0.0" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 2) << m_errIncorrectBoundCount;
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetComparison(), EngineComp::GreaterThan | EngineComp::EqualTo) << ErrComparisonMismatch(">=");
|
||||
EXPECT_EQ(dep.GetBounds()[1].GetComparison(), EngineComp::LessThan) << ErrComparisonMismatch("<");
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetVersion(), EngineVersion({ 1, 0, 0, 0 })) << ErrInvalidBound(Bound::Lower);
|
||||
EXPECT_EQ(dep.GetBounds()[1].GetVersion(), EngineVersion({ 2, 0, 0, 0 })) << ErrInvalidBound(Bound::Upper);
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ ">2.20.0.0", "<=3" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 2) << m_errIncorrectBoundCount;
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetComparison(), EngineComp::GreaterThan) << ErrComparisonMismatch(">");
|
||||
EXPECT_EQ(dep.GetBounds()[1].GetComparison(), EngineComp::LessThan | EngineComp::EqualTo) << ErrComparisonMismatch("<=");
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetVersion(), EngineVersion({ 2, 20, 0, 0 })) << ErrInvalidBound(Bound::Lower);
|
||||
EXPECT_EQ(dep.GetBounds()[1].GetVersion(), EngineVersion({ 3, 0, 0, 0 })) << ErrInvalidBound(Bound::Upper);
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ "<3.4.0.0", ">=20.1" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 2) << m_errIncorrectBoundCount;
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetComparison(), EngineComp::LessThan) << ErrComparisonMismatch("<");
|
||||
EXPECT_EQ(dep.GetBounds()[1].GetComparison(), EngineComp::GreaterThan | EngineComp::EqualTo) << ErrComparisonMismatch(">=");
|
||||
EXPECT_EQ(dep.GetBounds()[0].GetVersion(), EngineVersion({ 3, 4, 0, 0 })) << ErrInvalidBound(Bound::Lower);
|
||||
EXPECT_EQ(dep.GetBounds()[1].GetVersion(), EngineVersion({ 20, 1, 0, 0 })) << ErrInvalidBound(Bound::Upper);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DependencyTest, FullfillmentTest)
|
||||
{
|
||||
{
|
||||
AZ::Uuid gemId = AZ::Uuid::CreateRandom();
|
||||
|
||||
GemDependency dep;
|
||||
dep.SetID(gemId);
|
||||
|
||||
GemSpecifier spec1 = { gemId, GemVersion(1, 0, 0) };
|
||||
GemSpecifier specR = { AZ::Uuid::CreateRandom(), GemVersion(0, 0, 0) };
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions(AZStd::vector<AZStd::string>()).IsSuccess())<< m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 0) << m_errIncorrectBoundCount;
|
||||
EXPECT_TRUE(dep.IsFullfilledBy(spec1)) << m_errSpecFulfillsFailed;
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ ">=1" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 1) << m_errIncorrectBoundCount;
|
||||
EXPECT_TRUE(dep.IsFullfilledBy(spec1)) << m_errSpecFulfillsFailed;
|
||||
EXPECT_FALSE(dep.IsFullfilledBy(specR)) << m_errInvalidSpecFulfills;
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ ">0", "<1.1" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 2) << m_errIncorrectBoundCount;
|
||||
EXPECT_TRUE(dep.IsFullfilledBy(spec1)) << m_errSpecFulfillsFailed;
|
||||
EXPECT_FALSE(dep.IsFullfilledBy(specR)) << m_errInvalidSpecFulfills;
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ ">1", "<2", "==1.2" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 3) << m_errIncorrectBoundCount;
|
||||
EXPECT_FALSE(dep.IsFullfilledBy(spec1)) << m_errInvalidSpecFulfills;
|
||||
EXPECT_FALSE(dep.IsFullfilledBy(specR)) << m_errInvalidSpecFulfills;
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ "~>1.0" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 1) << m_errIncorrectBoundCount;
|
||||
EXPECT_TRUE(dep.IsFullfilledBy({ gemId, GemVersion(1, 0, 0) })) << m_errSpecFulfillsFailed;
|
||||
EXPECT_TRUE(dep.IsFullfilledBy({ gemId, GemVersion(1, 1, 0) })) << m_errSpecFulfillsFailed;
|
||||
EXPECT_FALSE(dep.IsFullfilledBy({ gemId, GemVersion(2, 0, 0) })) << m_errSpecFulfillsFailed;
|
||||
EXPECT_FALSE(dep.IsFullfilledBy({ gemId, GemVersion(0, 0, 1) })) << m_errSpecFulfillsFailed;
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ "~>1.0.1" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 1) << m_errIncorrectBoundCount;
|
||||
EXPECT_TRUE(dep.IsFullfilledBy({ gemId, GemVersion(1, 0, 1) })) << m_errSpecFulfillsFailed;
|
||||
EXPECT_FALSE(dep.IsFullfilledBy({ gemId, GemVersion(1, 1, 0) })) << m_errSpecFulfillsFailed;
|
||||
EXPECT_FALSE(dep.IsFullfilledBy({ gemId, GemVersion(1, 0, 0) })) << m_errSpecFulfillsFailed;
|
||||
}
|
||||
|
||||
{
|
||||
AZ::Uuid engineId = AZ::Uuid::CreateRandom();
|
||||
|
||||
EngineDependency dep;
|
||||
dep.SetID(engineId);
|
||||
|
||||
EngineSpecifier spec1 = { engineId, EngineVersion({ 1, 0, 0, 0 }) };
|
||||
EngineSpecifier specR = { AZ::Uuid::CreateRandom(), EngineVersion({ 0, 0, 0, 0 }) };
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions(AZStd::vector<AZStd::string>()).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 0) << m_errIncorrectBoundCount;
|
||||
EXPECT_TRUE(dep.IsFullfilledBy(spec1)) << m_errSpecFulfillsFailed;
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ ">=1" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 1) << m_errIncorrectBoundCount;
|
||||
EXPECT_TRUE(dep.IsFullfilledBy(spec1)) << m_errSpecFulfillsFailed;
|
||||
EXPECT_FALSE(dep.IsFullfilledBy(specR)) << m_errInvalidSpecFulfills;
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ ">0", "<1.1" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 2) << m_errIncorrectBoundCount;
|
||||
EXPECT_TRUE(dep.IsFullfilledBy(spec1)) << m_errSpecFulfillsFailed;
|
||||
EXPECT_FALSE(dep.IsFullfilledBy(specR)) << m_errInvalidSpecFulfills;
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ ">1", "<2", "==1.2" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 3) << m_errIncorrectBoundCount;
|
||||
EXPECT_FALSE(dep.IsFullfilledBy(spec1)) << m_errInvalidSpecFulfills;
|
||||
EXPECT_FALSE(dep.IsFullfilledBy(specR)) << m_errInvalidSpecFulfills;
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ "~>1.0" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 1) << m_errIncorrectBoundCount;
|
||||
EXPECT_TRUE(dep.IsFullfilledBy({ engineId, EngineVersion({ 1, 0, 0, 0 }) })) << m_errSpecFulfillsFailed;
|
||||
EXPECT_TRUE(dep.IsFullfilledBy({ engineId, EngineVersion({ 1, 1, 0, 0 }) })) << m_errSpecFulfillsFailed;
|
||||
EXPECT_FALSE(dep.IsFullfilledBy({ engineId, EngineVersion({ 2, 0, 0, 0 }) })) << m_errSpecFulfillsFailed;
|
||||
EXPECT_FALSE(dep.IsFullfilledBy({ engineId, EngineVersion({ 0, 0, 1, 0 }) })) << m_errSpecFulfillsFailed;
|
||||
|
||||
dep.m_bounds.clear();
|
||||
|
||||
ASSERT_TRUE(dep.ParseVersions({ "~>1.0.1" }).IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_EQ(dep.GetBounds().size(), 1) << m_errIncorrectBoundCount;
|
||||
EXPECT_TRUE(dep.IsFullfilledBy({ engineId, EngineVersion({ 1, 0, 1, 0 }) })) << m_errSpecFulfillsFailed;
|
||||
EXPECT_FALSE(dep.IsFullfilledBy({ engineId, EngineVersion({ 1, 1, 0, 0 }) })) << m_errSpecFulfillsFailed;
|
||||
EXPECT_FALSE(dep.IsFullfilledBy({ engineId, EngineVersion({ 1, 0, 0, 0 }) })) << m_errSpecFulfillsFailed;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DependencyTest, BoundToStringTest)
|
||||
{
|
||||
{
|
||||
GemVersion v1 {
|
||||
1, 0, 0
|
||||
};
|
||||
|
||||
GemDependency::Bound bnd;
|
||||
bnd.SetVersion(v1);
|
||||
|
||||
bnd.SetComparison(GemComp::EqualTo);
|
||||
EXPECT_STREQ(bnd.ToString().c_str(), "==1.0.0") << m_errToStringIncorrect;
|
||||
|
||||
bnd.SetComparison(GemComp::GreaterThan);
|
||||
EXPECT_STREQ(bnd.ToString().c_str(), ">1.0.0") << m_errToStringIncorrect;
|
||||
|
||||
bnd.SetComparison(GemComp::LessThan);
|
||||
EXPECT_STREQ(bnd.ToString().c_str(), "<1.0.0") << m_errToStringIncorrect;
|
||||
|
||||
bnd.SetComparison(GemComp::GreaterThan | GemComp::EqualTo);
|
||||
EXPECT_STREQ(bnd.ToString().c_str(), ">=1.0.0") << m_errToStringIncorrect;
|
||||
|
||||
bnd.SetComparison(GemComp::LessThan | GemComp::EqualTo);
|
||||
EXPECT_STREQ(bnd.ToString().c_str(), "<=1.0.0") << m_errToStringIncorrect;
|
||||
}
|
||||
|
||||
{
|
||||
EngineVersion v1{
|
||||
1, 0, 0, 0
|
||||
};
|
||||
|
||||
EngineDependency::Bound bnd;
|
||||
bnd.SetVersion(v1);
|
||||
|
||||
bnd.SetComparison(EngineComp::EqualTo);
|
||||
EXPECT_STREQ(bnd.ToString().c_str(), "==1.0.0.0") << m_errToStringIncorrect;
|
||||
|
||||
bnd.SetComparison(EngineComp::GreaterThan);
|
||||
EXPECT_STREQ(bnd.ToString().c_str(), ">1.0.0.0") << m_errToStringIncorrect;
|
||||
|
||||
bnd.SetComparison(EngineComp::LessThan);
|
||||
EXPECT_STREQ(bnd.ToString().c_str(), "<1.0.0.0") << m_errToStringIncorrect;
|
||||
|
||||
bnd.SetComparison(EngineComp::GreaterThan | EngineComp::EqualTo);
|
||||
EXPECT_STREQ(bnd.ToString().c_str(), ">=1.0.0.0") << m_errToStringIncorrect;
|
||||
|
||||
bnd.SetComparison(EngineComp::LessThan | EngineComp::EqualTo);
|
||||
EXPECT_STREQ(bnd.ToString().c_str(), "<=1.0.0.0") << m_errToStringIncorrect;
|
||||
}
|
||||
}
|
||||
@@ -1,378 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include <AzTest/AzTest.h>
|
||||
#include "GemDescription.h"
|
||||
|
||||
using Gems::GemDescription;
|
||||
using Gems::ModuleDefinition;
|
||||
|
||||
class DescriptionTest
|
||||
: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// Helper to parse json text to a GemDescription result
|
||||
static AZ::Outcome<GemDescription, AZStd::string> CreateFromString(const char* text)
|
||||
{
|
||||
rapidjson::Document document;
|
||||
document.Parse(text);
|
||||
EXPECT_FALSE(document.HasParseError());
|
||||
|
||||
return GemDescription::CreateFromJson(document, "", "");
|
||||
}
|
||||
|
||||
/// Helper to parse json text (that should compile) to a GemDescription
|
||||
static GemDescription ParseString(const char* text)
|
||||
{
|
||||
auto result = CreateFromString(text);
|
||||
EXPECT_TRUE(result.IsSuccess());
|
||||
|
||||
return AZStd::move(result.GetValue());
|
||||
}
|
||||
};
|
||||
|
||||
// Helper asserts
|
||||
#define EXPECT_MODULE_COUNT(desc, expectedCount) EXPECT_EQ(expectedCount, desc.GetModules().size());
|
||||
#define EXPECT_MODULE_TYPE_COUNT(desc, type, expectedCount) EXPECT_EQ(expectedCount, desc.GetModulesOfType(ModuleDefinition::Type::type).size())
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Success tests
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Test for parsing V3 Gem Descriptions
|
||||
TEST_F(DescriptionTest, ParseJson_V3_GameModule)
|
||||
{
|
||||
static const char* description = R"JSON(
|
||||
{
|
||||
"GemFormatVersion": 3,
|
||||
"Uuid": "ff06785f7145416b9d46fde39098cb0c",
|
||||
"Name": "LmbrCentral",
|
||||
"Version": "0.1.0",
|
||||
"LinkType": "Dynamic",
|
||||
"Summary": "Required LmbrCentral Engine Gem.",
|
||||
"Tags": ["Untagged"],
|
||||
"IconPath": "preview.png",
|
||||
"IsRequired": true
|
||||
}
|
||||
)JSON";
|
||||
|
||||
GemDescription desc = ParseString(description);
|
||||
|
||||
EXPECT_MODULE_COUNT(desc, 1);
|
||||
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, GameModule, 1);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, EditorModule, 1);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, StaticLib, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, Builder, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, Standalone, 0);
|
||||
}
|
||||
|
||||
// Test for parsing V3 Gem Descriptions with an editor module
|
||||
TEST_F(DescriptionTest, ParseJson_V3_EditorModule)
|
||||
{
|
||||
static const char* description = R"JSON(
|
||||
{
|
||||
"GemFormatVersion": 3,
|
||||
"Uuid": "ff06785f7145416b9d46fde39098cb0c",
|
||||
"Name": "LmbrCentral",
|
||||
"Version": "0.1.0",
|
||||
"LinkType": "Dynamic",
|
||||
"Summary": "Required LmbrCentral Engine Gem.",
|
||||
"Tags": ["Untagged"],
|
||||
"IconPath": "preview.png",
|
||||
"EditorModule": true,
|
||||
"IsRequired": true
|
||||
}
|
||||
)JSON";
|
||||
|
||||
GemDescription desc = ParseString(description);
|
||||
|
||||
EXPECT_MODULE_COUNT(desc, 2);
|
||||
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, GameModule, 1);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, EditorModule, 1);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, StaticLib, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, Builder, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, Standalone, 0);
|
||||
}
|
||||
|
||||
// Test for parsing V4 Gem Descriptions with a game module
|
||||
TEST_F(DescriptionTest, ParseJson_V4_GameModule)
|
||||
{
|
||||
static const char* description = R"JSON(
|
||||
{
|
||||
"GemFormatVersion": 4,
|
||||
"Uuid": "f910686b6725452fbfc4671f95f733c6",
|
||||
"Name": "Camera",
|
||||
"Version": "0.1.0",
|
||||
"DisplayName": "Camera",
|
||||
"Tags": ["Camera"],
|
||||
"Summary": "The Camera Gem includes a basic camera component that defines a frustum for runtime rendering.",
|
||||
"IconPath": "preview.png",
|
||||
"Modules": [
|
||||
{
|
||||
"Type": "GameModule"
|
||||
}
|
||||
]
|
||||
}
|
||||
)JSON";
|
||||
|
||||
GemDescription desc = ParseString(description);
|
||||
|
||||
EXPECT_MODULE_COUNT(desc, 1);
|
||||
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, GameModule, 1);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, EditorModule, 1);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, StaticLib, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, Builder, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, Standalone, 0);
|
||||
}
|
||||
|
||||
// Test for parsing V4 Gem Descriptions with an editor module
|
||||
TEST_F(DescriptionTest, ParseJson_V4_EditorModule)
|
||||
{
|
||||
static const char* description = R"JSON(
|
||||
{
|
||||
"GemFormatVersion": 4,
|
||||
"Uuid": "f910686b6725452fbfc4671f95f733c6",
|
||||
"Name": "Camera",
|
||||
"Version": "0.1.0",
|
||||
"DisplayName": "Camera",
|
||||
"Tags": ["Camera"],
|
||||
"Summary": "The Camera Gem includes a basic camera component that defines a frustum for runtime rendering.",
|
||||
"IconPath": "preview.png",
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "Editor",
|
||||
"Type": "EditorModule"
|
||||
}
|
||||
]
|
||||
}
|
||||
)JSON";
|
||||
|
||||
GemDescription desc = ParseString(description);
|
||||
|
||||
EXPECT_MODULE_COUNT(desc, 1);
|
||||
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, GameModule, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, EditorModule, 1);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, StaticLib, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, Builder, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, Standalone, 0);
|
||||
}
|
||||
|
||||
// Test for parsing V4 Gem Descriptions with a game and an editor module (that extends the editor module
|
||||
TEST_F(DescriptionTest, ParseJson_V4_EditorModuleExtends)
|
||||
{
|
||||
static const char* description = R"JSON(
|
||||
{
|
||||
"GemFormatVersion": 4,
|
||||
"Uuid": "f910686b6725452fbfc4671f95f733c6",
|
||||
"Name": "Camera",
|
||||
"Version": "0.1.0",
|
||||
"DisplayName": "Camera",
|
||||
"Tags": ["Camera"],
|
||||
"Summary": "The Camera Gem includes a basic camera component that defines a frustum for runtime rendering.",
|
||||
"IconPath": "preview.png",
|
||||
"Modules": [
|
||||
{
|
||||
"Type": "GameModule"
|
||||
},
|
||||
{
|
||||
"Name": "Editor",
|
||||
"Type": "EditorModule",
|
||||
"Extends": "GameModule"
|
||||
}
|
||||
]
|
||||
}
|
||||
)JSON";
|
||||
|
||||
GemDescription desc = ParseString(description);
|
||||
|
||||
EXPECT_MODULE_COUNT(desc, 2);
|
||||
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, GameModule, 1);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, EditorModule, 1);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, StaticLib, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, Builder, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, Standalone, 0);
|
||||
}
|
||||
|
||||
// Test for parsing V4 Gem Descriptions with a static lib
|
||||
TEST_F(DescriptionTest, ParseJson_V4_StaticLib)
|
||||
{
|
||||
static const char* description = R"JSON(
|
||||
{
|
||||
"GemFormatVersion": 4,
|
||||
"Uuid": "f910686b6725452fbfc4671f95f733c6",
|
||||
"Name": "Camera",
|
||||
"Version": "0.1.0",
|
||||
"DisplayName": "Camera",
|
||||
"Tags": ["Camera"],
|
||||
"Summary": "The Camera Gem includes a basic camera component that defines a frustum for runtime rendering.",
|
||||
"IconPath": "preview.png",
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "CameraHelper",
|
||||
"Type": "StaticLib"
|
||||
}
|
||||
]
|
||||
}
|
||||
)JSON";
|
||||
|
||||
GemDescription desc = ParseString(description);
|
||||
|
||||
EXPECT_MODULE_COUNT(desc, 1);
|
||||
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, GameModule, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, EditorModule, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, StaticLib, 1);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, Builder, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, Standalone, 0);
|
||||
}
|
||||
|
||||
// Test for parsing V4 Gem Descriptions with a static lib
|
||||
TEST_F(DescriptionTest, ParseJson_V4_Standalone)
|
||||
{
|
||||
static const char* description = R"JSON(
|
||||
{
|
||||
"GemFormatVersion": 4,
|
||||
"Uuid": "f910686b6725452fbfc4671f95f733c6",
|
||||
"Name": "Camera",
|
||||
"Version": "0.1.0",
|
||||
"DisplayName": "Camera",
|
||||
"Tags": ["Camera"],
|
||||
"Summary": "The Camera Gem includes a basic camera component that defines a frustum for runtime rendering.",
|
||||
"IconPath": "preview.png",
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "CameraHelper",
|
||||
"Type": "Standalone"
|
||||
}
|
||||
]
|
||||
}
|
||||
)JSON";
|
||||
|
||||
GemDescription desc = ParseString(description);
|
||||
|
||||
EXPECT_MODULE_COUNT(desc, 1);
|
||||
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, GameModule, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, EditorModule, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, StaticLib, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, Builder, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, Standalone, 1);
|
||||
}
|
||||
|
||||
// Test for parsing V4 Gem Descriptions with a builder module
|
||||
TEST_F(DescriptionTest, ParseJson_V4_BuilderModule)
|
||||
{
|
||||
static const char* description = R"JSON(
|
||||
{
|
||||
"GemFormatVersion": 4,
|
||||
"Uuid": "f910686b6725452fbfc4671f95f733c6",
|
||||
"Name": "Camera",
|
||||
"Version": "0.1.0",
|
||||
"DisplayName": "Camera",
|
||||
"Tags": ["Camera"],
|
||||
"Summary": "The Camera Gem includes a basic camera component that defines a frustum for runtime rendering.",
|
||||
"IconPath": "preview.png",
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "CameraBuilder",
|
||||
"Type": "Builder"
|
||||
}
|
||||
]
|
||||
}
|
||||
)JSON";
|
||||
|
||||
GemDescription desc = ParseString(description);
|
||||
|
||||
EXPECT_MODULE_COUNT(desc, 1);
|
||||
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, GameModule, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, EditorModule, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, StaticLib, 0);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, Builder, 1);
|
||||
EXPECT_MODULE_TYPE_COUNT(desc, Standalone, 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Failure tests
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Test for parsing V4 Gem Descriptions with an invalid extends
|
||||
TEST_F(DescriptionTest, ParseJson_V4_ExtendsNonExistantModule)
|
||||
{
|
||||
static const char* description = R"JSON(
|
||||
{
|
||||
"GemFormatVersion": 4,
|
||||
"Uuid": "f910686b6725452fbfc4671f95f733c6",
|
||||
"Name": "Camera",
|
||||
"Version": "0.1.0",
|
||||
"DisplayName": "Camera",
|
||||
"Tags": ["Camera"],
|
||||
"Summary": "The Camera Gem includes a basic camera component that defines a frustum for runtime rendering.",
|
||||
"IconPath": "preview.png",
|
||||
"Modules": [
|
||||
{
|
||||
"Type": "GameModule"
|
||||
},
|
||||
{
|
||||
"Name": "Editor",
|
||||
"Type": "EditorModule",
|
||||
"Extends": "ModuleThatDoesntExist"
|
||||
}
|
||||
]
|
||||
}
|
||||
)JSON";
|
||||
|
||||
EXPECT_FALSE(CreateFromString(description).IsSuccess());
|
||||
}
|
||||
|
||||
// Test for parsing V4 Gem Descriptions with an invalid extends
|
||||
TEST_F(DescriptionTest, ParseJson_V4_ExtendsStaticLib)
|
||||
{
|
||||
static const char* description = R"JSON(
|
||||
{
|
||||
"GemFormatVersion": 4,
|
||||
"Uuid": "f910686b6725452fbfc4671f95f733c6",
|
||||
"Name": "Camera",
|
||||
"Version": "0.1.0",
|
||||
"DisplayName": "Camera",
|
||||
"Tags": ["Camera"],
|
||||
"Summary": "The Camera Gem includes a basic camera component that defines a frustum for runtime rendering.",
|
||||
"IconPath": "preview.png",
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "CameraHelper",
|
||||
"Type": "StaticLib"
|
||||
},
|
||||
{
|
||||
"Name": "Editor",
|
||||
"Type": "EditorModule",
|
||||
"Extends": "CameraHelper"
|
||||
}
|
||||
]
|
||||
}
|
||||
)JSON";
|
||||
|
||||
EXPECT_FALSE(CreateFromString(description).IsSuccess());
|
||||
}
|
||||
@@ -1,247 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include <AzTest/AzTest.h>
|
||||
#include "ProjectSettings.h"
|
||||
#include "GemDescription.h"
|
||||
|
||||
using Gems::ProjectSettings;
|
||||
using Gems::ProjectGemSpecifier;
|
||||
using AzFramework::Specifier;
|
||||
using Gems::GemVersion;
|
||||
|
||||
class ProjectSettingsTest
|
||||
: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
m_registry = aznew Gems::GemRegistry();
|
||||
m_errCreationFailed = "Failed to create a ProjectSettings instance.";
|
||||
m_errNonEmptyEnabledGems = "EnabledGems should start empty.";
|
||||
m_errEnableGemFailed = "Failed to enable valid Gem Spec.";
|
||||
m_errIsGemEnabledFailed = "Failed to accurately determine if a gem was enabled or not";
|
||||
m_errDescriptionParseFailed = "Failed to parse valid Description: ";
|
||||
m_errInvalidDescriptionParseSucceeded = "Parsing of invalid Description succeeded.";
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
delete m_registry;
|
||||
}
|
||||
|
||||
enum GenerateJsonFlags : AZ::u8
|
||||
{
|
||||
GJF_IncludeID = 1 << 0,
|
||||
GJF_IncludeVersion = 1 << 1,
|
||||
GJF_IncludePath = 1 << 2,
|
||||
GJF_All = GJF_IncludeID | GJF_IncludeVersion | GJF_IncludePath,
|
||||
};
|
||||
|
||||
rapidjson::Document ParseFromString(AZStd::string document)
|
||||
{
|
||||
rapidjson::Document root(rapidjson::kObjectType);
|
||||
root.Parse(document.c_str());
|
||||
return root;
|
||||
}
|
||||
|
||||
void GenerateJson(rapidjson::Document& rootObj, AZ::Uuid id, GemVersion version, const AZStd::string& path, AZ::u8 flags)
|
||||
{
|
||||
rootObj.SetObject();
|
||||
rootObj.AddMember(GPF_TAG_LIST_FORMAT_VERSION, GEMS_PROJECT_FILE_VERSION, rootObj.GetAllocator());
|
||||
|
||||
rapidjson::Value gemsArray(rapidjson::kArrayType);
|
||||
|
||||
if (flags != 0)
|
||||
{
|
||||
// build a gem
|
||||
rapidjson::Value gemObj(rapidjson::kObjectType);
|
||||
|
||||
if (flags & GJF_IncludeID)
|
||||
{
|
||||
char idstr[UUID_STR_BUF_LEN];
|
||||
id.ToString(idstr, UUID_STR_BUF_LEN, false, false);
|
||||
rapidjson::Value v(idstr, rootObj.GetAllocator());
|
||||
gemObj.AddMember(GPF_TAG_UUID, v.Move(), rootObj.GetAllocator());
|
||||
}
|
||||
|
||||
if (flags & GJF_IncludeVersion)
|
||||
{
|
||||
rapidjson::Value v(version.ToString().c_str(), rootObj.GetAllocator());
|
||||
gemObj.AddMember(GPF_TAG_VERSION, v.Move(), rootObj.GetAllocator());
|
||||
}
|
||||
|
||||
if (flags & GJF_IncludePath)
|
||||
{
|
||||
rapidjson::Value v(path.c_str(), rootObj.GetAllocator());
|
||||
gemObj.AddMember(GPF_TAG_PATH, v.Move(), rootObj.GetAllocator());
|
||||
}
|
||||
|
||||
gemsArray.PushBack(gemObj.Move(), rootObj.GetAllocator()); // copy into array
|
||||
}
|
||||
|
||||
rootObj.AddMember(GPF_TAG_GEM_ARRAY, gemsArray.Move(), rootObj.GetAllocator()); // copy into root object
|
||||
}
|
||||
|
||||
Gems::GemRegistry* m_registry;
|
||||
const char* m_errCreationFailed;
|
||||
const char* m_errNonEmptyEnabledGems;
|
||||
const char* m_errEnableGemFailed;
|
||||
const char* m_errIsGemEnabledFailed;
|
||||
const char* m_errDescriptionParseFailed;
|
||||
const char* m_errInvalidDescriptionParseSucceeded;
|
||||
};
|
||||
|
||||
TEST_F(ProjectSettingsTest, CreateAndDestroyTest)
|
||||
{
|
||||
Gems::IProjectSettings* ps = m_registry->CreateProjectSettings();
|
||||
EXPECT_NE(ps, nullptr) << m_errCreationFailed;
|
||||
m_registry->DestroyProjectSettings(ps);
|
||||
}
|
||||
|
||||
TEST_F(ProjectSettingsTest, EnableDisableTest)
|
||||
{
|
||||
ProjectSettings ps {
|
||||
m_registry
|
||||
};
|
||||
AZ::Uuid id = AZ::Uuid::CreateRandom();
|
||||
GemVersion v1 {
|
||||
1, 0, 0
|
||||
};
|
||||
AZStd::string path {
|
||||
"some\\path"
|
||||
};
|
||||
ProjectGemSpecifier s0 {
|
||||
id, v1, path
|
||||
};
|
||||
|
||||
EXPECT_TRUE(ps.EnableGem(s0)) << m_errEnableGemFailed;
|
||||
EXPECT_TRUE(ps.IsGemEnabled(s0)) << m_errEnableGemFailed;
|
||||
|
||||
EXPECT_TRUE(ps.DisableGem(s0)) << m_errEnableGemFailed;
|
||||
EXPECT_FALSE(ps.IsGemEnabled(s0)) << m_errEnableGemFailed;
|
||||
}
|
||||
|
||||
TEST_F(ProjectSettingsTest, IsEnabledTest)
|
||||
{
|
||||
ProjectSettings ps {
|
||||
m_registry
|
||||
};
|
||||
ProjectGemSpecifier enabledGemSpecifier {
|
||||
AZ::Uuid::CreateRandom(), GemVersion {
|
||||
1, 0, 0
|
||||
}, "some\\path"
|
||||
};
|
||||
|
||||
EXPECT_TRUE(ps.EnableGem(enabledGemSpecifier)) << m_errEnableGemFailed;
|
||||
|
||||
// Compare 1.0.0 to 1.0.0
|
||||
EXPECT_FALSE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { "<1.0.0" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_TRUE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { "<=1.0.0" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_TRUE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { "==1.0.0" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_TRUE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { ">=1.0.0" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_FALSE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { ">1.0.0" })) << m_errIsGemEnabledFailed;
|
||||
|
||||
// Compare 1.0.0 to 1.0.1
|
||||
EXPECT_TRUE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { "<1.0.1" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_TRUE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { "<=1.0.1" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_FALSE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { "==1.0.1" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_FALSE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { ">=1.0.1" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_FALSE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { ">1.0.1" })) << m_errIsGemEnabledFailed;
|
||||
|
||||
// Compare 1.0.0 to 0.1.1
|
||||
EXPECT_FALSE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { "<0.1.1" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_FALSE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { "<=0.1.1" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_FALSE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { "==0.1.1" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_TRUE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { ">=0.1.1" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_TRUE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { ">0.1.1" })) << m_errIsGemEnabledFailed;
|
||||
|
||||
// Test ranges around the enabled gem
|
||||
EXPECT_TRUE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { ">=0.1.0", "<=1.1.0" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_TRUE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { "<=1.1.0", ">=0.1.0" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_TRUE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { "~>1.0.0" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_TRUE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { "~>1.0" })) << m_errIsGemEnabledFailed;
|
||||
|
||||
// Test ranges at or above the enabled gem
|
||||
EXPECT_TRUE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { ">=1.0.0", "<=1.1.0" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_FALSE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { ">1.0.0", "<=1.1.0" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_FALSE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { "~>1.1.0" })) << m_errIsGemEnabledFailed;
|
||||
|
||||
//// Test ranges at or below the enabled gem
|
||||
EXPECT_FALSE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { ">=0.1.0", "<1.0.0" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_TRUE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { ">=0.1.0", "<=1.0.0" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_FALSE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { "~>0.1.0" })) << m_errIsGemEnabledFailed;
|
||||
EXPECT_FALSE(ps.IsGemEnabled(enabledGemSpecifier.m_id, { "~>0.1" })) << m_errIsGemEnabledFailed;
|
||||
}
|
||||
|
||||
TEST_F(ProjectSettingsTest, ParseTest)
|
||||
{
|
||||
ProjectSettings ps {
|
||||
m_registry
|
||||
};
|
||||
AZ::Uuid id = AZ::Uuid::CreateRandom();
|
||||
GemVersion v1 {
|
||||
1, 0, 0
|
||||
};
|
||||
AZStd::string path("Some\\Path");
|
||||
|
||||
rapidjson::Document json(rapidjson::kObjectType);
|
||||
GenerateJson(json, id, v1, path, GJF_All);
|
||||
AZ::Outcome<void, AZStd::string> outcome = ps.ParseGemsJson(json);
|
||||
ASSERT_TRUE(outcome.IsSuccess()) << m_errDescriptionParseFailed << outcome.GetError().c_str();
|
||||
auto gemMap = ps.GetGems();
|
||||
EXPECT_EQ(gemMap.size(), 1) << m_errDescriptionParseFailed << outcome.GetError().c_str();
|
||||
EXPECT_EQ(gemMap.begin()->second.m_id, id) << m_errDescriptionParseFailed << outcome.GetError().c_str();
|
||||
EXPECT_EQ(gemMap.begin()->second.m_version, v1) << m_errDescriptionParseFailed << outcome.GetError().c_str();
|
||||
|
||||
GenerateJson(json, id, v1, path, GJF_IncludeVersion | GJF_IncludePath);
|
||||
EXPECT_FALSE(ps.ParseGemsJson(json)) << m_errInvalidDescriptionParseSucceeded;
|
||||
|
||||
GenerateJson(json, id, v1, path, GJF_IncludeID | GJF_IncludePath);
|
||||
EXPECT_FALSE(ps.ParseGemsJson(json)) << m_errInvalidDescriptionParseSucceeded;
|
||||
|
||||
GenerateJson(json, id, v1, path, GJF_IncludeVersion | GJF_IncludeID);
|
||||
EXPECT_FALSE(ps.ParseGemsJson(json)) << m_errInvalidDescriptionParseSucceeded;
|
||||
|
||||
GenerateJson(json, id, v1, path, GJF_IncludeID | GJF_IncludeVersion | GJF_IncludePath);
|
||||
EXPECT_TRUE(ps.ParseGemsJson(json)) << m_errDescriptionParseFailed;
|
||||
}
|
||||
|
||||
TEST_F(ProjectSettingsTest, SaveTest)
|
||||
{
|
||||
ProjectSettings ps {
|
||||
m_registry
|
||||
};
|
||||
AZ::Uuid id = AZ::Uuid::CreateRandom();
|
||||
char idStr[UUID_STR_BUF_LEN];
|
||||
id.ToString(idStr, UUID_STR_BUF_LEN, false, false);
|
||||
GemVersion v1 {
|
||||
1, 0, 0
|
||||
};
|
||||
AZStd::string path("Some\\Path");
|
||||
|
||||
rapidjson::Document json(rapidjson::kObjectType);
|
||||
GenerateJson(json, id, v1, path, GJF_All);
|
||||
ASSERT_TRUE(ps.ParseGemsJson(json)) << m_errDescriptionParseFailed;
|
||||
EXPECT_TRUE(json.HasMember(GPF_TAG_LIST_FORMAT_VERSION)) << m_errDescriptionParseFailed;
|
||||
EXPECT_TRUE(json[GPF_TAG_LIST_FORMAT_VERSION].IsInt()) << m_errDescriptionParseFailed;
|
||||
EXPECT_EQ(json[GPF_TAG_LIST_FORMAT_VERSION].GetInt(), GEMS_PROJECT_FILE_VERSION) << m_errDescriptionParseFailed;
|
||||
ASSERT_TRUE(json.HasMember(GPF_TAG_GEM_ARRAY)) << m_errDescriptionParseFailed;
|
||||
ASSERT_TRUE(json[GPF_TAG_GEM_ARRAY].IsArray()) << m_errDescriptionParseFailed;
|
||||
ASSERT_EQ(json[GPF_TAG_GEM_ARRAY].Size(), 1) << m_errDescriptionParseFailed;
|
||||
EXPECT_STRCASEEQ(json[GPF_TAG_GEM_ARRAY][0][GPF_TAG_UUID].GetString(), idStr) << m_errDescriptionParseFailed;
|
||||
EXPECT_STRCASEEQ(json[GPF_TAG_GEM_ARRAY][0][GPF_TAG_VERSION].GetString(), v1.ToString().c_str()) << m_errDescriptionParseFailed;
|
||||
EXPECT_STRCASEEQ(json[GPF_TAG_GEM_ARRAY][0][GPF_TAG_PATH].GetString(), path.c_str()) << m_errDescriptionParseFailed;
|
||||
|
||||
ps.DisableGem({ id, v1 });
|
||||
json = ps.GetJsonRepresentation();
|
||||
ASSERT_EQ(json[GPF_TAG_GEM_ARRAY].Size(), 0);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include <AzTest/AzTest.h>
|
||||
#include "GemRegistry.h"
|
||||
|
||||
using Gems::GemRegistry;
|
||||
|
||||
class RegistryTest
|
||||
: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
m_errCreationFailed = "Failed to create a GemRegistry instance.";
|
||||
m_errInvalidErrorMessage = "Error message is not what it was set to.";
|
||||
}
|
||||
|
||||
const char* m_errCreationFailed;
|
||||
const char* m_errInvalidErrorMessage;
|
||||
};
|
||||
|
||||
TEST_F(RegistryTest, CreateAndDestroyTest)
|
||||
{
|
||||
Gems::IGemRegistry* gr = CreateGemRegistry();
|
||||
ASSERT_NE(gr, nullptr) << m_errCreationFailed;
|
||||
DestroyGemRegistry(gr);
|
||||
}
|
||||
@@ -1,465 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <GemRegistry/Version.h>
|
||||
|
||||
using Gems::GemVersion;
|
||||
using Gems::EngineVersion;
|
||||
|
||||
class VersionTest
|
||||
: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
m_errParseFailed = "Failed to parse valid version string.";
|
||||
m_errParseInvalidSucceeded = "Parsing invalid version string succeeded.";
|
||||
m_errParseInvalid = "ParseFromString resulted in incorrect version.";
|
||||
m_errCompareIncorrect = "Result of Compare is incorrect.";
|
||||
m_errToStringIncorrect = "ToString result is incorrect.";
|
||||
m_errHasherIncorrect = "Did not get back the same value from hash map.";
|
||||
}
|
||||
|
||||
const char* m_errParseFailed;
|
||||
const char* m_errParseInvalidSucceeded;
|
||||
const char* m_errParseInvalid;
|
||||
const char* m_errCompareIncorrect;
|
||||
const char* m_errToStringIncorrect;
|
||||
const char* m_errHasherIncorrect;
|
||||
};
|
||||
|
||||
TEST_F(VersionTest, InitializerListConstructor_ValidValues_ReturnSameValues)
|
||||
{
|
||||
GemVersion v0 = { 1, 2, 3 };
|
||||
|
||||
ASSERT_EQ(v0.m_parts[0], 1) << m_errParseInvalid;
|
||||
ASSERT_EQ(v0.m_parts[1], 2) << m_errParseInvalid;
|
||||
ASSERT_EQ(v0.m_parts[2], 3) << m_errParseInvalid;
|
||||
|
||||
EngineVersion v1 = { 1, 2, 3, 4 };
|
||||
|
||||
ASSERT_EQ(v1.m_parts[0], 1) << m_errParseInvalid;
|
||||
ASSERT_EQ(v1.m_parts[1], 2) << m_errParseInvalid;
|
||||
ASSERT_EQ(v1.m_parts[2], 3) << m_errParseInvalid;
|
||||
ASSERT_EQ(v1.m_parts[3], 4) << m_errParseInvalid;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, ParseFromString_ValidString_ReturnSuccessOutcomeWithCorrectValues)
|
||||
{
|
||||
auto version0Outcome = GemVersion::ParseFromString("1.2.3");
|
||||
ASSERT_TRUE(version0Outcome.IsSuccess()) << m_errParseFailed;
|
||||
|
||||
GemVersion v0 = version0Outcome.GetValue();
|
||||
|
||||
ASSERT_EQ(v0.GetMajor(), 1) << m_errParseInvalid;
|
||||
ASSERT_EQ(v0.GetMinor(), 2) << m_errParseInvalid;
|
||||
ASSERT_EQ(v0.GetPatch(), 3) << m_errParseInvalid;
|
||||
|
||||
auto version1Outcome = EngineVersion::ParseFromString("1.2.3.4");
|
||||
ASSERT_TRUE(version1Outcome.IsSuccess()) << m_errParseFailed;
|
||||
|
||||
EngineVersion v1 = version1Outcome.GetValue();
|
||||
|
||||
ASSERT_EQ(v1.m_parts[0], 1) << m_errParseInvalid;
|
||||
ASSERT_EQ(v1.m_parts[1], 2) << m_errParseInvalid;
|
||||
ASSERT_EQ(v1.m_parts[2], 3) << m_errParseInvalid;
|
||||
ASSERT_EQ(v1.m_parts[3], 4) << m_errParseInvalid;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, ParseFromString_EmptyString_ReturnFailureOutcome)
|
||||
{
|
||||
ASSERT_FALSE(GemVersion::ParseFromString("").IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
ASSERT_FALSE(EngineVersion::ParseFromString("").IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, ParseFromString_InvalidPartSize_ReturnFailureOutcome)
|
||||
{
|
||||
ASSERT_FALSE(GemVersion::ParseFromString("1.2").IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
ASSERT_FALSE(GemVersion::ParseFromString("1.2.3.4").IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
|
||||
ASSERT_FALSE(EngineVersion::ParseFromString("1.4.2.1.1").IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
ASSERT_FALSE(EngineVersion::ParseFromString("1.2.3").IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, ParseFromString_InvalidCharactersString_ReturnFailureOutcome)
|
||||
{
|
||||
ASSERT_FALSE(GemVersion::ParseFromString("NotAVersion").IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
ASSERT_FALSE(GemVersion::ParseFromString("NotAVersion.2.3").IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
ASSERT_FALSE(GemVersion::ParseFromString("1.NotAVersion.3").IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
ASSERT_FALSE(GemVersion::ParseFromString("1.2.NotAVersion").IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
|
||||
ASSERT_FALSE(EngineVersion::ParseFromString("NotBVersion").IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
ASSERT_FALSE(EngineVersion::ParseFromString("NotBVersion.2.3").IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
ASSERT_FALSE(EngineVersion::ParseFromString("1.NotBVersion.3").IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
ASSERT_FALSE(EngineVersion::ParseFromString("1.2.NotBVersion").IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, ParseFromString_InvalidSeparator_ReturnFailureOutcome)
|
||||
{
|
||||
ASSERT_FALSE(GemVersion::ParseFromString("1,2,3").IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
|
||||
ASSERT_FALSE(EngineVersion::ParseFromString("1,2,3,4").IsSuccess()) << m_errParseInvalidSucceeded;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, Compare_DifferentMajor_ReturnLesserThanZero)
|
||||
{
|
||||
GemVersion v1 = { 1, 0, 0 };
|
||||
GemVersion v2 = { 2, 0, 0 };
|
||||
|
||||
ASSERT_LT(GemVersion::Compare(v1, v2), 0) << m_errCompareIncorrect;
|
||||
|
||||
EngineVersion v3 = { 1, 0, 0, 0 };
|
||||
EngineVersion v4 = { 2, 0, 0, 0 };
|
||||
|
||||
ASSERT_LT(EngineVersion::Compare(v3, v4), 0) << m_errCompareIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, Compare_DifferentMinor_ReturnLesserThanZero)
|
||||
{
|
||||
GemVersion v1 = { 1, 0, 0 };
|
||||
GemVersion v2 = { 1, 1, 0 };
|
||||
|
||||
ASSERT_LT(GemVersion::Compare(v1, v2), 0) << m_errCompareIncorrect;
|
||||
|
||||
EngineVersion v3 = { 1, 0, 0, 0 };
|
||||
EngineVersion v4 = { 1, 1, 0, 0 };
|
||||
|
||||
ASSERT_LT(EngineVersion::Compare(v3, v4), 0) << m_errCompareIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, Compare_DifferentMajorAndMinor_ReturnGreaterThanZero)
|
||||
{
|
||||
GemVersion v1 = { 2, 0, 0 };
|
||||
GemVersion v2 = { 1, 1, 0 };
|
||||
|
||||
ASSERT_GT(GemVersion::Compare(v1, v2), 0) << m_errCompareIncorrect;
|
||||
|
||||
EngineVersion v3 = { 2, 0, 0, 0 };
|
||||
EngineVersion v4 = { 1, 1, 0, 0 };
|
||||
|
||||
ASSERT_GT(EngineVersion::Compare(v3, v4), 0) << m_errCompareIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, Compare_SameValue_ReturnZero)
|
||||
{
|
||||
GemVersion v1 = { 1, 1, 0 };
|
||||
GemVersion v2 = { 1, 1, 0 };
|
||||
|
||||
ASSERT_EQ(GemVersion::Compare(v1, v2), 0) << m_errCompareIncorrect;
|
||||
|
||||
EngineVersion v3 = { 1, 1, 0, 0 };
|
||||
EngineVersion v4 = { 1, 1, 0, 0 };
|
||||
|
||||
ASSERT_EQ(EngineVersion::Compare(v3, v4), 0) << m_errCompareIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, CompareLesserThan_DifferentMajor_ReturnTrue)
|
||||
{
|
||||
GemVersion v1 = { 1, 0, 0 };
|
||||
GemVersion v2 = { 2, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v1 < v2) << m_errCompareIncorrect;
|
||||
|
||||
EngineVersion v3 = { 1, 0, 0, 0 };
|
||||
EngineVersion v4 = { 2, 0, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v3 < v4) << m_errCompareIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, CompareLesserThan_SameMajor_ReturnTrue)
|
||||
{
|
||||
GemVersion v1 = { 1, 0, 0 };
|
||||
GemVersion v2 = { 1, 1, 0 };
|
||||
|
||||
ASSERT_TRUE(v1 < v2) << m_errCompareIncorrect;
|
||||
|
||||
EngineVersion v3 = { 1, 0, 0, 0 };
|
||||
EngineVersion v4 = { 1, 1, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v3 < v4) << m_errCompareIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, CompareLesserEqualsTo_DifferentMajor_ReturnTrue)
|
||||
{
|
||||
GemVersion v1 = { 1, 0, 0 };
|
||||
GemVersion v2 = { 2, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v1 <= v2) << m_errCompareIncorrect;
|
||||
|
||||
EngineVersion v3 = { 1, 0, 0, 0 };
|
||||
EngineVersion v4 = { 2, 0, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v3 <= v4) << m_errCompareIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, CompareLesserEqualsTo_SameMajorSmallerMinor_ReturnTrue)
|
||||
{
|
||||
GemVersion v1 = { 1, 0, 0 };
|
||||
GemVersion v2 = { 1, 1, 0 };
|
||||
|
||||
ASSERT_TRUE(v1 <= v2) << m_errCompareIncorrect;
|
||||
|
||||
EngineVersion v3 = { 1, 0, 0, 0 };
|
||||
EngineVersion v4 = { 1, 1, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v3 <= v4) << m_errCompareIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, CompareLesserEqualsTo_Samevalues_ReturnTrue)
|
||||
{
|
||||
GemVersion v1 = { 1, 0, 0 };
|
||||
GemVersion v2 = { 1, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v1 <= v2) << m_errCompareIncorrect;
|
||||
|
||||
EngineVersion v3 = { 1, 0, 0, 0 };
|
||||
EngineVersion v4 = { 1, 0, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v3 <= v4) << m_errCompareIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, CompareGreaterThan_DifferentMajor_ReturnTrue)
|
||||
{
|
||||
GemVersion v1 = { 2, 0, 0 };
|
||||
GemVersion v2 = { 1, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v1 > v2) << m_errCompareIncorrect;
|
||||
|
||||
EngineVersion v3 = { 2, 0, 0, 0 };
|
||||
EngineVersion v4 = { 1, 0, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v3 > v4) << m_errCompareIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, CompareGreaterThan_SameMajor_ReturnTrue)
|
||||
{
|
||||
GemVersion v1 = { 1, 1, 0 };
|
||||
GemVersion v2 = { 1, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v1 > v2) << m_errCompareIncorrect;
|
||||
|
||||
EngineVersion v3 = { 1, 1, 0, 0 };
|
||||
EngineVersion v4 = { 1, 0, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v3 > v4) << m_errCompareIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, CompareGreaterEqualsTo_DifferentMajor_ReturnTrue)
|
||||
{
|
||||
GemVersion v1 = { 2, 0, 0 };
|
||||
GemVersion v2 = { 1, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v1 >= v2) << m_errCompareIncorrect;
|
||||
|
||||
EngineVersion v3 = { 2, 0, 0, 0 };
|
||||
EngineVersion v4 = { 1, 0, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v3 >= v4) << m_errCompareIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, CompareGreaterEqualsTo_SameMajorSmallerMinor_ReturnTrue)
|
||||
{
|
||||
GemVersion v1 = { 1, 1, 0 };
|
||||
GemVersion v2 = { 1, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v1 >= v2) << m_errCompareIncorrect;
|
||||
|
||||
EngineVersion v3 = { 1, 1, 0, 0 };
|
||||
EngineVersion v4 = { 1, 0, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v3 >= v4) << m_errCompareIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, CompareGreaterEqualsTo_Samevalues_ReturnTrue)
|
||||
{
|
||||
GemVersion v1 = { 1, 0, 0 };
|
||||
GemVersion v2 = { 1, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v1 >= v2) << m_errCompareIncorrect;
|
||||
|
||||
EngineVersion v3 = { 1, 0, 0, 0 };
|
||||
EngineVersion v4 = { 1, 0, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v3 >= v4) << m_errCompareIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, CompareEqualsTo_SameValue_ReturnTrue)
|
||||
{
|
||||
GemVersion v1 = { 1, 1, 0 };
|
||||
GemVersion v2 = { 1, 1, 0 };
|
||||
|
||||
ASSERT_TRUE(v1 == v2) << m_errCompareIncorrect;
|
||||
|
||||
EngineVersion v3 = { 1, 1, 0, 0 };
|
||||
EngineVersion v4 = { 1, 1, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v3 == v4) << m_errCompareIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, CompareEqualsTo_DifferentValue_ReturnFalse)
|
||||
{
|
||||
GemVersion v1 = { 1, 1, 0 };
|
||||
GemVersion v2 = { 0, 1, 0 };
|
||||
|
||||
ASSERT_FALSE(v1 == v2) << m_errCompareIncorrect;
|
||||
|
||||
EngineVersion v3 = { 1, 1, 0, 0 };
|
||||
EngineVersion v4 = { 0, 1, 0, 0 };
|
||||
|
||||
ASSERT_FALSE(v3 == v4) << m_errCompareIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, CompareNotEqualsTo_DifferentValues_ReturnTrue)
|
||||
{
|
||||
GemVersion v1 = { 1, 0, 0 };
|
||||
GemVersion v2 = { 2, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v1 != v2) << m_errCompareIncorrect;
|
||||
|
||||
EngineVersion v3 = { 1, 0, 0, 0 };
|
||||
EngineVersion v4 = { 2, 0, 0, 0 };
|
||||
|
||||
ASSERT_TRUE(v3 != v4) << m_errCompareIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, CompareNotEqualsTo_SameValues_ReturnFalse)
|
||||
{
|
||||
GemVersion v1 = { 2, 0, 1 };
|
||||
GemVersion v2 = { 2, 0, 1 };
|
||||
|
||||
ASSERT_FALSE(v1 != v2) << m_errCompareIncorrect;
|
||||
|
||||
EngineVersion v3 = { 2, 0, 1, 0 };
|
||||
EngineVersion v4 = { 2, 0, 1, 0 };
|
||||
|
||||
ASSERT_FALSE(v3 != v4) << m_errCompareIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, ToString_VariousValues_ReturnsCorrectStringOutput)
|
||||
{
|
||||
GemVersion v1 = { 1, 0, 0 };
|
||||
GemVersion v2 = { 2, 0, 0 };
|
||||
GemVersion v3 = { 1, 1, 0 };
|
||||
GemVersion v4 = { 1, 1, 0 };
|
||||
|
||||
ASSERT_STREQ(v1.ToString().c_str(), "1.0.0") << m_errToStringIncorrect;
|
||||
ASSERT_STREQ(v2.ToString().c_str(), "2.0.0") << m_errToStringIncorrect;
|
||||
ASSERT_STREQ(v3.ToString().c_str(), "1.1.0") << m_errToStringIncorrect;
|
||||
ASSERT_STREQ(v4.ToString().c_str(), "1.1.0") << m_errToStringIncorrect;
|
||||
|
||||
|
||||
EngineVersion v5 = { 1, 0, 0, 0 };
|
||||
EngineVersion v6 = { 2, 0, 0, 0 };
|
||||
EngineVersion v7 = { 1, 1, 0, 0 };
|
||||
EngineVersion v8 = { 1, 1, 0, 0 };
|
||||
|
||||
ASSERT_STREQ(v5.ToString().c_str(), "1.0.0.0") << m_errToStringIncorrect;
|
||||
ASSERT_STREQ(v6.ToString().c_str(), "2.0.0.0") << m_errToStringIncorrect;
|
||||
ASSERT_STREQ(v7.ToString().c_str(), "1.1.0.0") << m_errToStringIncorrect;
|
||||
ASSERT_STREQ(v8.ToString().c_str(), "1.1.0.0") << m_errToStringIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, ToString_ToStringFromParseStringValue_ReturnSameStringFromInput)
|
||||
{
|
||||
const char* version0String = "1.2.3";
|
||||
|
||||
auto version0Outcome = GemVersion::ParseFromString(version0String);
|
||||
GemVersion v0 = version0Outcome.GetValue();
|
||||
|
||||
ASSERT_TRUE(version0Outcome.IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_STREQ(v0.ToString().c_str(), version0String) << m_errToStringIncorrect;
|
||||
|
||||
|
||||
const char* version1String = "1.2.3.4";
|
||||
|
||||
auto version1Outcome = EngineVersion::ParseFromString(version1String);
|
||||
EngineVersion v1 = version1Outcome.GetValue();
|
||||
|
||||
ASSERT_TRUE(version1Outcome.IsSuccess()) << m_errParseFailed;
|
||||
ASSERT_STREQ(v1.ToString().c_str(), version1String) << m_errToStringIncorrect;
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, Hasher_DifferentValues_GetBackSameValue)
|
||||
{
|
||||
{
|
||||
AZStd::unordered_map<GemVersion, int> unorderedMap;
|
||||
GemVersion v1 = { 1, 0, 0 };
|
||||
int v1value = 1;
|
||||
GemVersion v2 = { 2, 0, 0 };
|
||||
int v2value = 2;
|
||||
GemVersion v3 = { 1, 1, 0 };
|
||||
int v3value = 3;
|
||||
GemVersion v4 = { 1, 1, 1 };
|
||||
int v4value = 4;
|
||||
|
||||
unorderedMap[v1] = v1value;
|
||||
unorderedMap[v2] = v2value;
|
||||
unorderedMap[v3] = v3value;
|
||||
unorderedMap[v4] = v4value;
|
||||
|
||||
ASSERT_EQ(unorderedMap[v1], v1value) << m_errHasherIncorrect;
|
||||
ASSERT_EQ(unorderedMap[v2], v2value) << m_errHasherIncorrect;
|
||||
ASSERT_EQ(unorderedMap[v3], v3value) << m_errHasherIncorrect;
|
||||
ASSERT_EQ(unorderedMap[v4], v4value) << m_errHasherIncorrect;
|
||||
}
|
||||
|
||||
{
|
||||
AZStd::unordered_map<EngineVersion, int> unorderedMap;
|
||||
EngineVersion v1 = { 1, 0, 0, 0 };
|
||||
int v1value = 1;
|
||||
EngineVersion v2 = { 2, 0, 0, 0 };
|
||||
int v2value = 2;
|
||||
EngineVersion v3 = { 1, 1, 0, 0 };
|
||||
int v3value = 3;
|
||||
EngineVersion v4 = { 1, 1, 1, 0 };
|
||||
int v4value = 4;
|
||||
|
||||
unorderedMap[v1] = v1value;
|
||||
unorderedMap[v2] = v2value;
|
||||
unorderedMap[v3] = v3value;
|
||||
unorderedMap[v4] = v4value;
|
||||
|
||||
ASSERT_EQ(unorderedMap[v1], v1value) << m_errHasherIncorrect;
|
||||
ASSERT_EQ(unorderedMap[v2], v2value) << m_errHasherIncorrect;
|
||||
ASSERT_EQ(unorderedMap[v3], v3value) << m_errHasherIncorrect;
|
||||
ASSERT_EQ(unorderedMap[v4], v4value) << m_errHasherIncorrect;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(VersionTest, Hasher_SameValues_GetBackSameValue)
|
||||
{
|
||||
{
|
||||
AZStd::unordered_map<GemVersion, int> unorderedMap;
|
||||
GemVersion v1 = { 1, 1, 1 };
|
||||
GemVersion v2 = { 1, 1, 1 };
|
||||
int v12value = 1;
|
||||
|
||||
unorderedMap[v1] = v12value;
|
||||
|
||||
ASSERT_EQ(unorderedMap.at(v1), v12value) << m_errHasherIncorrect;
|
||||
ASSERT_EQ(unorderedMap.at(v2), v12value) << m_errHasherIncorrect;
|
||||
}
|
||||
|
||||
{
|
||||
AZStd::unordered_map<EngineVersion, int> unorderedMap;
|
||||
EngineVersion v1 = { 1, 1, 1, 0 };
|
||||
EngineVersion v2 = { 1, 1, 1, 0 };
|
||||
int v12value = 1;
|
||||
|
||||
unorderedMap[v1] = v12value;
|
||||
|
||||
ASSERT_EQ(unorderedMap.at(v1), v12value) << m_errHasherIncorrect;
|
||||
ASSERT_EQ(unorderedMap.at(v2), v12value) << m_errHasherIncorrect;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <AzCore/Memory/AllocatorManager.h>
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
class GemRegistryTestEnvironment
|
||||
: public AZ::Test::ITestEnvironment
|
||||
{
|
||||
public:
|
||||
virtual ~GemRegistryTestEnvironment()
|
||||
{}
|
||||
|
||||
protected:
|
||||
void SetupEnvironment() override
|
||||
{
|
||||
AZ::AllocatorInstance<AZ::OSAllocator>::Create();
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Create();
|
||||
}
|
||||
|
||||
void TeardownEnvironment() override
|
||||
{
|
||||
AZ::AllocatorInstance<AZ::OSAllocator>::Destroy();
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
|
||||
}
|
||||
};
|
||||
|
||||
AZ_UNIT_TEST_HOOK(new GemRegistryTestEnvironment);
|
||||
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/PlatformDef.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include "NewsBuilder.h"
|
||||
#include "Qt/ArticleDetails.h"
|
||||
#include "Qt/BuilderArticleViewContainer.h"
|
||||
@@ -39,8 +40,7 @@ AZ_POP_DISABLE_WARNING
|
||||
|
||||
namespace News
|
||||
{
|
||||
|
||||
NewsBuilder::NewsBuilder(QWidget* parent)
|
||||
NewsBuilder::NewsBuilder(QWidget* parent, const AZ::IO::PathView& engineRootPath)
|
||||
: QMainWindow(parent)
|
||||
, m_ui(new Ui::NewsBuilderClass())
|
||||
, m_manifest(new BuilderResourceManifest(
|
||||
@@ -51,15 +51,16 @@ namespace News
|
||||
, m_articleViewContainer(new BuilderArticleViewContainer(this, *m_manifest))
|
||||
, m_logContainer(new LogContainer(this))
|
||||
{
|
||||
|
||||
AzQtComponents::StyleManager* m_styleSheet = new AzQtComponents::StyleManager(this);
|
||||
m_styleSheet->initialize(qApp);
|
||||
m_styleSheet->initialize(qApp, engineRootPath);
|
||||
|
||||
m_ui->setupUi(this);
|
||||
|
||||
QDir rootDir(AzQtComponents::FindEngineRootDir(qApp));
|
||||
QDir rootDir = QString::fromUtf8(engineRootPath.Native().data(), aznumeric_cast<int>(engineRootPath.Native().size()));
|
||||
const auto pathOnDisk = rootDir.absoluteFilePath("Code/Tools/News/NewsBuilder/Resources");
|
||||
const auto qrcPath = QStringLiteral(":/NewsBuilder");
|
||||
AzQtComponents::StyleManager::addSearchPaths("newsbuilder", pathOnDisk, qrcPath);
|
||||
AzQtComponents::StyleManager::addSearchPaths("newsbuilder", pathOnDisk, qrcPath, engineRootPath);
|
||||
|
||||
AzQtComponents::StyleManager::setStyleSheet(this, QStringLiteral("newsbuilder:NewsBuilder.qss"));
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
#include "NewsShared/LogType.h"
|
||||
#include "NewsShared/ErrorCodes.h"
|
||||
|
||||
#include <AzCore/IO/Path/Path_fwd.h>
|
||||
#endif
|
||||
|
||||
class QSignalMapper;
|
||||
@@ -41,7 +43,7 @@ namespace News
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NewsBuilder(QWidget* parent = nullptr);
|
||||
explicit NewsBuilder(QWidget* parent, const AZ::IO::PathView& engineRootPath);
|
||||
~NewsBuilder();
|
||||
|
||||
private:
|
||||
|
||||
@@ -15,19 +15,26 @@
|
||||
|
||||
#include "Qt/NewsBuilder.h"
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/Component/ComponentApplication.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
// Must be set before QApplication is initialized, so that we support HighDpi monitors, like the Retina displays
|
||||
// on Windows 10
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
|
||||
QApplication a(argc, argv);
|
||||
|
||||
News::NewsBuilder w;
|
||||
AZ::IO::FixedMaxPath engineRootPath;
|
||||
{
|
||||
AZ::ComponentApplication componentApplication;
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
settingsRegistry->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
}
|
||||
News::NewsBuilder w(nullptr, engineRootPath);
|
||||
w.show();
|
||||
return a.exec();
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2012
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProfVis", "ProfVis\ProfVis.csproj", "{3AF4C1C6-4DF2-4F3E-9997-B2844D461DF8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{3AF4C1C6-4DF2-4F3E-9997-B2844D461DF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3AF4C1C6-4DF2-4F3E-9997-B2844D461DF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3AF4C1C6-4DF2-4F3E-9997-B2844D461DF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3AF4C1C6-4DF2-4F3E-9997-B2844D461DF8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
||||
</startup>
|
||||
</configuration>
|
||||
-162
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ProfVis
|
||||
{
|
||||
partial class Form1
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
|
||||
this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.reloadToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.progressBar1 = new System.Windows.Forms.ProgressBar();
|
||||
this.searchTextBox = new System.Windows.Forms.TextBox();
|
||||
this.dataGridView_Prof = new ProfVis.DGW();
|
||||
this.menuStrip1.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.dataGridView_Prof)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// menuStrip1
|
||||
//
|
||||
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.fileToolStripMenuItem,
|
||||
this.reloadToolStripMenuItem});
|
||||
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
|
||||
this.menuStrip1.Name = "menuStrip1";
|
||||
this.menuStrip1.Size = new System.Drawing.Size(869, 24);
|
||||
this.menuStrip1.TabIndex = 6;
|
||||
this.menuStrip1.Text = "menuStrip1";
|
||||
//
|
||||
// fileToolStripMenuItem
|
||||
//
|
||||
this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.openToolStripMenuItem});
|
||||
this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
|
||||
this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
|
||||
this.fileToolStripMenuItem.Text = "File";
|
||||
//
|
||||
// openToolStripMenuItem
|
||||
//
|
||||
this.openToolStripMenuItem.Name = "openToolStripMenuItem";
|
||||
this.openToolStripMenuItem.Size = new System.Drawing.Size(112, 22);
|
||||
this.openToolStripMenuItem.Text = "&Open...";
|
||||
this.openToolStripMenuItem.Click += new System.EventHandler(this.openToolStripMenuItem_Click);
|
||||
//
|
||||
// reloadToolStripMenuItem
|
||||
//
|
||||
this.reloadToolStripMenuItem.Name = "reloadToolStripMenuItem";
|
||||
this.reloadToolStripMenuItem.Size = new System.Drawing.Size(55, 20);
|
||||
this.reloadToolStripMenuItem.Text = "Reload";
|
||||
this.reloadToolStripMenuItem.Click += new System.EventHandler(this.reloadToolStripMenuItem_Click);
|
||||
//
|
||||
// progressBar1
|
||||
//
|
||||
this.progressBar1.Dock = System.Windows.Forms.DockStyle.Top;
|
||||
this.progressBar1.Location = new System.Drawing.Point(0, 24);
|
||||
this.progressBar1.MarqueeAnimationSpeed = 5;
|
||||
this.progressBar1.Name = "progressBar1";
|
||||
this.progressBar1.Size = new System.Drawing.Size(869, 23);
|
||||
this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
|
||||
this.progressBar1.TabIndex = 7;
|
||||
this.progressBar1.Visible = false;
|
||||
//
|
||||
// searchTextBox
|
||||
//
|
||||
this.searchTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.searchTextBox.Location = new System.Drawing.Point(646, 2);
|
||||
this.searchTextBox.Name = "searchTextBox";
|
||||
this.searchTextBox.Size = new System.Drawing.Size(221, 20);
|
||||
this.searchTextBox.TabIndex = 8;
|
||||
this.searchTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.searchTextBox_KeyUp);
|
||||
//
|
||||
// dataGridView_Prof
|
||||
//
|
||||
this.dataGridView_Prof.AllowUserToAddRows = false;
|
||||
this.dataGridView_Prof.AllowUserToDeleteRows = false;
|
||||
this.dataGridView_Prof.AllowUserToResizeColumns = false;
|
||||
this.dataGridView_Prof.AllowUserToResizeRows = false;
|
||||
this.dataGridView_Prof.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
||||
this.dataGridView_Prof.Location = new System.Drawing.Point(37, 29);
|
||||
this.dataGridView_Prof.Name = "dataGridView_Prof";
|
||||
this.dataGridView_Prof.ReadOnly = true;
|
||||
this.dataGridView_Prof.RowTemplate.ReadOnly = true;
|
||||
this.dataGridView_Prof.ShowCellToolTips = false;
|
||||
this.dataGridView_Prof.Size = new System.Drawing.Size(408, 351);
|
||||
this.dataGridView_Prof.TabIndex = 5;
|
||||
this.dataGridView_Prof.CellPainting += new System.Windows.Forms.DataGridViewCellPaintingEventHandler(this.dataGridView_Prof_CellPainting);
|
||||
this.dataGridView_Prof.MouseMove += new System.Windows.Forms.MouseEventHandler(this.dataGridView_Prof_MouseMove);
|
||||
//
|
||||
// Form1
|
||||
//
|
||||
this.AllowDrop = true;
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(869, 548);
|
||||
this.Controls.Add(this.searchTextBox);
|
||||
this.Controls.Add(this.progressBar1);
|
||||
this.Controls.Add(this.dataGridView_Prof);
|
||||
this.Controls.Add(this.menuStrip1);
|
||||
this.MainMenuStrip = this.menuStrip1;
|
||||
this.Name = "Form1";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
|
||||
this.Load += new System.EventHandler(this.Form1_Load);
|
||||
this.DragDrop += new System.Windows.Forms.DragEventHandler(this.Form1_DragDrop);
|
||||
this.DragEnter += new System.Windows.Forms.DragEventHandler(this.Form1_DragEnter);
|
||||
this.menuStrip1.ResumeLayout(false);
|
||||
this.menuStrip1.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.dataGridView_Prof)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.MenuStrip menuStrip1;
|
||||
private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem openToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem reloadToolStripMenuItem;
|
||||
public DGW dataGridView_Prof;
|
||||
private System.Windows.Forms.ProgressBar progressBar1;
|
||||
private System.Windows.Forms.TextBox searchTextBox;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,891 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using System.Xml;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
|
||||
namespace ProfVis
|
||||
{
|
||||
public partial class Form1 : Form
|
||||
{
|
||||
public Form1(string[] args)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Show();
|
||||
|
||||
if (args.Length >= 1)
|
||||
{
|
||||
OpenSession(args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public static Color[] s_clrBack = { Color.FromArgb(235, 235, 235), Color.White };
|
||||
public static Color[] s_clrBars = { Color.FromArgb(140, 198, 63),
|
||||
Color.FromArgb(247, 148, 30),
|
||||
Color.FromArgb(37, 170, 225),
|
||||
Color.FromArgb(122, 45, 24),
|
||||
Color.FromArgb(239, 62, 54),
|
||||
};
|
||||
|
||||
|
||||
private ToolTip m_tooltip; //TODO: kill this and use some custom panel to prevent flickering
|
||||
public List<Brush> m_BarsBrushes;
|
||||
public Font m_barFont;
|
||||
|
||||
private static Random m_random;
|
||||
|
||||
// session
|
||||
private string m_sessionName;
|
||||
public List<CThreadTag> m_threadsData;
|
||||
|
||||
public static string m_filter = "";
|
||||
|
||||
private BackgroundWorker m_loadingWorker;
|
||||
|
||||
private void Form1_Load(object sender, EventArgs e)
|
||||
{
|
||||
//DoubleBuffered = true;
|
||||
|
||||
dataGridView_Prof.Dock = DockStyle.Fill;
|
||||
|
||||
dataGridView_Prof.AutoGenerateColumns = false;
|
||||
dataGridView_Prof.ColumnCount = 2;
|
||||
//AdjustDataGridViewSizing();
|
||||
dataGridView_Prof.Columns[0].Name = "Thread";
|
||||
dataGridView_Prof.Columns[1].Name = "Blocks";
|
||||
|
||||
dataGridView_Prof.Columns[0].Frozen = true;
|
||||
|
||||
dataGridView_Prof.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells;
|
||||
dataGridView_Prof.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
|
||||
//dataGridView_Prof.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
|
||||
//dataGridView_Prof.Columns[1].Width = 500;
|
||||
|
||||
dataGridView_Prof.Columns[0].SortMode = DataGridViewColumnSortMode.NotSortable;
|
||||
dataGridView_Prof.Columns[1].SortMode = DataGridViewColumnSortMode.NotSortable;
|
||||
|
||||
dataGridView_Prof.ShowCellToolTips = false;
|
||||
|
||||
dataGridView_Prof.MouseWheel += new MouseEventHandler(dataGridView_Prof_MouseWheel);
|
||||
dataGridView_Prof.MouseClick += new MouseEventHandler(dataGridView_Prof_MouseClick);
|
||||
|
||||
//dataGridView_Prof.AllowUserToResizeRows = true;
|
||||
|
||||
|
||||
m_barFont = (Font)dataGridView_Prof.DefaultCellStyle.Font.Clone();
|
||||
|
||||
|
||||
m_tooltip = new ToolTip();
|
||||
m_tooltip.ShowAlways = false;
|
||||
m_tooltip.UseAnimation = false;
|
||||
m_tooltip.UseFading = false;
|
||||
m_tooltip.AutomaticDelay = 0;
|
||||
m_tooltip.AutoPopDelay = 0;
|
||||
|
||||
m_BarsBrushes = new List<Brush>();
|
||||
for(int i = 0; i < Form1.s_clrBars.Length; ++i)
|
||||
{
|
||||
m_BarsBrushes.Add(new SolidBrush(s_clrBars[i]));
|
||||
}
|
||||
|
||||
m_loadingWorker = new BackgroundWorker();
|
||||
m_loadingWorker.WorkerReportsProgress = true;
|
||||
m_loadingWorker.ProgressChanged += (object progressSender, ProgressChangedEventArgs progressArgs) =>
|
||||
{
|
||||
DataGridViewRow row = progressArgs.UserState as DataGridViewRow;
|
||||
if (row != null)
|
||||
dataGridView_Prof.Rows.Add(row);
|
||||
};
|
||||
|
||||
m_loadingWorker.RunWorkerCompleted += (object progressSender, RunWorkerCompletedEventArgs progressArgs) =>
|
||||
{
|
||||
progressBar1.Visible = false;
|
||||
progressBar1.Enabled = false;
|
||||
menuStrip1.Enabled = true;
|
||||
this.Text = m_sessionName;
|
||||
};
|
||||
|
||||
m_loadingWorker.DoWork += (object progressSender, DoWorkEventArgs doWorkArgs) =>
|
||||
{
|
||||
Graphics g = this.CreateGraphics();
|
||||
try
|
||||
{
|
||||
m_hoveredData = null;
|
||||
if (m_threadsData != null)
|
||||
m_threadsData.Clear();
|
||||
|
||||
string filename = doWorkArgs.Argument as string;
|
||||
|
||||
XmlDocument xmlDoc = new XmlDocument();
|
||||
xmlDoc.Load(filename);
|
||||
|
||||
m_sessionName = filename;
|
||||
|
||||
m_threadsData = new List<CThreadTag>();
|
||||
XmlNode rootNode = xmlDoc.SelectSingleNode("root");
|
||||
int threadIndex = 0;
|
||||
int num_childs = rootNode.ChildNodes.Count;
|
||||
foreach (XmlNode threadNode in rootNode)
|
||||
{
|
||||
string threadName = threadNode.Attributes.GetNamedItem("name").Value;
|
||||
float threadTimeMS = Single.Parse(threadNode.Attributes.GetNamedItem("totalTimeMS").Value, System.Globalization.CultureInfo.InvariantCulture);
|
||||
long threadStart = Convert.ToInt64(threadNode.Attributes.GetNamedItem("startTime").Value);
|
||||
long threadEnd = Convert.ToInt64(threadNode.Attributes.GetNamedItem("stopTime").Value);
|
||||
long threadTime = threadEnd - threadStart;
|
||||
|
||||
m_threadsData.Add(new CThreadTag());
|
||||
|
||||
CThreadTag threadData = m_threadsData[threadIndex];
|
||||
threadData.m_Times = new CBlockData(threadName, threadTimeMS, threadStart, threadEnd, 0, "");
|
||||
|
||||
DataGridViewRow row = new DataGridViewRow();
|
||||
DataGridViewCell cell0 = new DataGridViewTextBoxCell();
|
||||
cell0.Value = threadName;
|
||||
row.Cells.Add(cell0);
|
||||
row.Tag = threadData;
|
||||
m_loadingWorker.ReportProgress(0, row);
|
||||
|
||||
getBlocksFromXML(threadNode, 0, threadIndex);
|
||||
threadIndex++;
|
||||
|
||||
// create rows
|
||||
for (int i = 0; i < threadData.m_StackEnties.Count; ++i)
|
||||
{
|
||||
CBlockTag rowInfo = threadData.m_StackEnties[i];
|
||||
|
||||
// used for DrawFrectangles()
|
||||
for (int j = 0; j < rowInfo.blocks.Count; ++j)
|
||||
{
|
||||
CBlockData data = rowInfo.blocks[j];
|
||||
//rowInfo.blocksByColor[data.colorIdx].Add(data);
|
||||
data.m_labelSize = g.MeasureString(data.name, m_barFont);
|
||||
}
|
||||
|
||||
DataGridViewRow stackedRow = new DataGridViewRow();
|
||||
DataGridViewCell stackedCell = new DataGridViewTextBoxCell();
|
||||
stackedCell.Value = i;
|
||||
stackedRow.Cells.Add(stackedCell);
|
||||
stackedRow.Tag = rowInfo;
|
||||
m_loadingWorker.ReportProgress(0, stackedRow);
|
||||
}
|
||||
}// foreach (XmlNode threadNode in rootNode)
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
m_sessionName = string.Empty;
|
||||
MessageBox.Show(ex.Message);
|
||||
}
|
||||
g.Dispose();
|
||||
};
|
||||
}
|
||||
|
||||
private void OpenSession(string filename)
|
||||
{
|
||||
if (m_loadingWorker.IsBusy)
|
||||
return;
|
||||
|
||||
// 0 seed for same colors
|
||||
m_random = new Random(0);
|
||||
|
||||
dataGridView_Prof.Rows.Clear();
|
||||
|
||||
|
||||
DataGridViewRow timeRow = new DataGridViewRow();
|
||||
DataGridViewCell textCell = new DataGridViewTextBoxCell();
|
||||
textCell.Value = "Time";
|
||||
timeRow.Cells.Add(textCell);
|
||||
timeRow.Height = 40;
|
||||
timeRow.Tag = new CTimeTag();
|
||||
timeRow.Frozen = true;
|
||||
dataGridView_Prof.Rows.Add(timeRow);
|
||||
|
||||
progressBar1.Value = 0;
|
||||
progressBar1.Visible = true;
|
||||
progressBar1.Enabled = true;
|
||||
menuStrip1.Enabled = false;
|
||||
|
||||
m_loadingWorker.RunWorkerAsync(filename);
|
||||
}
|
||||
private void getBlocksFromXML(XmlNode node, int depth, int threadIndex)
|
||||
{
|
||||
//if (depth == 3)
|
||||
// return;
|
||||
|
||||
if (node.SelectSingleNode("block")!=null)
|
||||
{
|
||||
CThreadTag threadData = m_threadsData[threadIndex];
|
||||
if (depth >= threadData.m_StackEnties.Count)
|
||||
{
|
||||
threadData.m_StackEnties.Add(new CBlockTag()); //new stack level
|
||||
threadData.m_StackEnties[depth].threadIndex = threadIndex;
|
||||
}
|
||||
|
||||
int prevClrIndex = 0;
|
||||
foreach (XmlNode blockNode in node)
|
||||
{
|
||||
string name = blockNode.Attributes.GetNamedItem("name").Value;
|
||||
float totalTimeMS = Single.Parse(blockNode.Attributes.GetNamedItem("totalTimeMS").Value, System.Globalization.CultureInfo.InvariantCulture);
|
||||
long startTime = Convert.ToInt64(blockNode.Attributes.GetNamedItem("startTime").Value);
|
||||
long stopTime = Convert.ToInt64(blockNode.Attributes.GetNamedItem("stopTime").Value);
|
||||
XmlNode argsNode = blockNode.Attributes.GetNamedItem("args");
|
||||
string args = "";
|
||||
if(argsNode != null)
|
||||
args = argsNode.Value;
|
||||
|
||||
int clrIndex = m_random.Next(0, s_clrBars.Length);
|
||||
if (clrIndex == prevClrIndex)
|
||||
{
|
||||
clrIndex++;
|
||||
if (clrIndex == s_clrBars.Length)
|
||||
clrIndex = 0;
|
||||
}
|
||||
|
||||
threadData.m_StackEnties[depth].blocks.Add(new CBlockData(name, totalTimeMS, startTime, stopTime, clrIndex, args));
|
||||
prevClrIndex = clrIndex;
|
||||
|
||||
getBlocksFromXML(blockNode, depth+1, threadIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void dataGridView_Prof_MouseWheel(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (ModifierKeys == Keys.Control)
|
||||
{
|
||||
if (!dataGridView_Prof.IsOffsetSet())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int oldOffset = dataGridView_Prof.HorizontalScrollingOffset;
|
||||
|
||||
dataGridView_Prof.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
|
||||
double zoomFactor = Math.Pow(2, (double)e.Delta * 0.001);
|
||||
int newWidth = (int)(dataGridView_Prof.Columns[1].Width * zoomFactor);
|
||||
|
||||
bool clampWidth = newWidth < 0 || newWidth >= 65535;
|
||||
newWidth = newWidth < 65535 ? newWidth : 65535;
|
||||
newWidth = newWidth >= 0 ? newWidth : 0;
|
||||
|
||||
dataGridView_Prof.Columns[1].Width = newWidth;
|
||||
|
||||
int visibleColumn1Width = dataGridView_Prof.Width - dataGridView_Prof.Columns[0].Width;
|
||||
int mouseColumn1Offset = e.X - dataGridView_Prof.Columns[0].Width;
|
||||
double mousePrecentage = (double)mouseColumn1Offset / (double)visibleColumn1Width;
|
||||
|
||||
int newOffset = clampWidth ? oldOffset : ((int)((double)oldOffset * zoomFactor) + (int)(visibleColumn1Width * mousePrecentage * (double)e.Delta * 0.001));
|
||||
newOffset = newOffset >= 0 ? newOffset : 0;
|
||||
dataGridView_Prof.SetOffset(newOffset);
|
||||
}
|
||||
}
|
||||
|
||||
private void dataGridView_Prof_MouseClick(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (ModifierKeys == Keys.Control)
|
||||
{
|
||||
dataGridView_Prof.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
|
||||
}
|
||||
|
||||
if (e.Button == MouseButtons.Right)
|
||||
{
|
||||
if (m_hoveredData != null)
|
||||
{
|
||||
ContextMenu m = new ContextMenu();
|
||||
MenuItem copyName = new MenuItem("Copy name");
|
||||
copyName.Click += new System.EventHandler(
|
||||
delegate(object o, EventArgs click_args)
|
||||
{
|
||||
Clipboard.SetText(m_hoveredData.name);
|
||||
}
|
||||
);
|
||||
m.MenuItems.Add(copyName);
|
||||
MenuItem copyArgs = new MenuItem("Copy arguments");
|
||||
copyArgs.Click += new System.EventHandler(
|
||||
delegate(object o, EventArgs click_args)
|
||||
{
|
||||
Clipboard.SetText(m_hoveredData.args);
|
||||
}
|
||||
);
|
||||
m.MenuItems.Add(copyArgs);
|
||||
m.Show(dataGridView_Prof, e.Location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void dataGridView_Prof_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
|
||||
{
|
||||
if (e.RowIndex < 0 || e.ColumnIndex < 0)
|
||||
return;
|
||||
|
||||
if (e.ColumnIndex != 1)
|
||||
return;
|
||||
|
||||
|
||||
object tag = dataGridView_Prof.Rows[e.RowIndex].Tag;
|
||||
if (tag != null)
|
||||
{
|
||||
IRowTag rowTag = tag as IRowTag;
|
||||
if (rowTag != null)
|
||||
rowTag.Draw(ref e, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
//DRAW timeline
|
||||
}
|
||||
}
|
||||
|
||||
private int binarySearch(List<CBlockData> list, int value)
|
||||
{
|
||||
int min = 0;
|
||||
int max = list.Count - 1;
|
||||
|
||||
while(min <= max)
|
||||
{
|
||||
int mid = min + (max - min)/2;
|
||||
|
||||
if (value >= list[mid].drawXStart)
|
||||
{
|
||||
if (value <= list[mid].drawXEnd)
|
||||
return mid;
|
||||
else
|
||||
min = mid + 1;
|
||||
}
|
||||
else
|
||||
max = mid - 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private Point m_PrevMousePos;
|
||||
private CBlockData m_hoveredData;
|
||||
private void dataGridView_Prof_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (m_loadingWorker.IsBusy)
|
||||
return;
|
||||
if (m_PrevMousePos != e.Location)
|
||||
{
|
||||
m_PrevMousePos = e.Location;
|
||||
|
||||
DataGridView.HitTestInfo hittest = dataGridView_Prof.HitTest(e.Location.X, e.Location.Y);
|
||||
|
||||
m_tooltip.RemoveAll();
|
||||
if (hittest.ColumnIndex == 1 && hittest.RowIndex >= 0 && hittest.RowIndex < dataGridView_Prof.RowCount)
|
||||
{
|
||||
object tag = dataGridView_Prof.Rows[hittest.RowIndex].Tag;
|
||||
if (tag != null)
|
||||
{
|
||||
CBlockTag info = tag as CBlockTag;
|
||||
if (info != null)
|
||||
{
|
||||
List<CBlockData> filteredBlocks = info.blocks.Where(x => x.visible).ToList();
|
||||
int idx = binarySearch(filteredBlocks, e.Location.X);
|
||||
if (idx != -1)
|
||||
{
|
||||
m_hoveredData = filteredBlocks[idx];
|
||||
m_tooltip.Hide(dataGridView_Prof);
|
||||
|
||||
Rectangle columnRect = info.cellBounds;
|
||||
|
||||
float threadMS = m_threadsData[0].m_Times.timeMS;
|
||||
float mouseRel = ((e.X - columnRect.X) * 100.0f) / columnRect.Width;
|
||||
float mouseRelMS = (mouseRel / 100.0f) * threadMS;
|
||||
|
||||
string text;
|
||||
|
||||
m_tooltip.ToolTipTitle = filteredBlocks[idx].name;
|
||||
if (filteredBlocks[idx].args.Length > 0)
|
||||
{
|
||||
text = string.Format("totalTime = {0} ms\ncursorAbsolute = {1} ms ({2:##.##} %)\nargs = {3}",
|
||||
filteredBlocks[idx].timeMS, mouseRelMS, mouseRel, filteredBlocks[idx].args);
|
||||
}
|
||||
else
|
||||
{
|
||||
text = string.Format("totalTime = {0} ms\ncursorAbsolute = {1} ms ({2:##.##} %)",
|
||||
filteredBlocks[idx].timeMS, mouseRelMS, mouseRel);
|
||||
}
|
||||
|
||||
m_tooltip.Show(text, dataGridView_Prof, e.X + 20, e.Y + 20);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
m_hoveredData = null;
|
||||
m_tooltip.Hide(dataGridView_Prof);
|
||||
}
|
||||
}
|
||||
CThreadTag td = tag as CThreadTag;
|
||||
if (td != null)
|
||||
{
|
||||
|
||||
m_tooltip.Hide(dataGridView_Prof);
|
||||
m_tooltip.Show(string.Format("time = {0} ms", td.m_Times.timeMS), dataGridView_Prof, e.X + 20, e.Y + 20);
|
||||
m_tooltip.ToolTipTitle = td.m_Times.name;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_hoveredData = null;
|
||||
m_tooltip.Hide(dataGridView_Prof);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_hoveredData = null;
|
||||
m_tooltip.Hide(dataGridView_Prof);
|
||||
}
|
||||
}
|
||||
|
||||
// base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
private void Form1_DragDrop(object sender, DragEventArgs e)
|
||||
{
|
||||
if (e.Data.GetDataPresent(DataFormats.FileDrop))
|
||||
{
|
||||
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
|
||||
|
||||
|
||||
if (files.Length == 1)
|
||||
{
|
||||
OpenSession(files[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Form1_DragEnter(object sender, DragEventArgs e)
|
||||
{
|
||||
if (e.Data.GetDataPresent(DataFormats.FileDrop))
|
||||
{
|
||||
e.Effect = DragDropEffects.Move;
|
||||
}
|
||||
}
|
||||
|
||||
private void openToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
OpenFileDialog myDialog = new OpenFileDialog();
|
||||
|
||||
myDialog.Filter = "All files (*.xml)|*.xml";
|
||||
myDialog.CheckFileExists = true;
|
||||
myDialog.Multiselect = false;
|
||||
|
||||
if (myDialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
OpenSession(myDialog.FileName);
|
||||
}
|
||||
}
|
||||
|
||||
private void reloadToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (m_sessionName != null)
|
||||
OpenSession(m_sessionName);
|
||||
}
|
||||
|
||||
private void searchTextBox_KeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.KeyCode == Keys.Enter)
|
||||
{
|
||||
m_filter = searchTextBox.Text;
|
||||
|
||||
for (int i = 0; i < m_threadsData.Count; ++i)
|
||||
{
|
||||
m_threadsData[i].UpdateFilter();
|
||||
}
|
||||
|
||||
dataGridView_Prof.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DGW : DataGridView
|
||||
{
|
||||
private bool reapplyOffset = false;
|
||||
private int deferredOffset = 0;
|
||||
|
||||
public DGW()
|
||||
{
|
||||
this.DoubleBuffered = true;
|
||||
}
|
||||
|
||||
protected override void OnPaintBackground(PaintEventArgs pevent)
|
||||
{
|
||||
if (reapplyOffset)
|
||||
{
|
||||
HorizontalScrollingOffset = deferredOffset;
|
||||
reapplyOffset = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
{
|
||||
if (reapplyOffset)
|
||||
{
|
||||
HorizontalScrollingOffset = deferredOffset;
|
||||
reapplyOffset = false;
|
||||
Invalidate(true);
|
||||
}
|
||||
|
||||
base.OnPaint(e);
|
||||
}
|
||||
|
||||
public bool IsOffsetSet()
|
||||
{
|
||||
return !reapplyOffset;
|
||||
}
|
||||
|
||||
public void SetOffset(int offset)
|
||||
{
|
||||
if (reapplyOffset)
|
||||
{
|
||||
deferredOffset += offset;
|
||||
return;
|
||||
}
|
||||
|
||||
reapplyOffset = true;
|
||||
deferredOffset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
public class CBlockData
|
||||
{
|
||||
public string name;
|
||||
public float timeMS;
|
||||
public long timeStart;
|
||||
public long timeEnd;
|
||||
public long time;
|
||||
|
||||
public string args;
|
||||
|
||||
public int drawXStart;
|
||||
public int drawXEnd;
|
||||
public int colorIdx;
|
||||
|
||||
public bool visible;
|
||||
|
||||
public SizeF m_labelSize;
|
||||
public CBlockData(string n, float t, long start, long end, int clrIdx, string a)
|
||||
{
|
||||
name = n;
|
||||
timeMS = t;
|
||||
timeStart = start;
|
||||
timeEnd = end;
|
||||
time = end - start;
|
||||
|
||||
args = a;
|
||||
|
||||
colorIdx = clrIdx;
|
||||
|
||||
visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class IRowTag
|
||||
{
|
||||
public abstract void Draw(ref DataGridViewCellPaintingEventArgs e, Form1 form);
|
||||
}
|
||||
|
||||
public class CBlockTag : IRowTag
|
||||
{
|
||||
public int threadIndex;
|
||||
public List<CBlockData> blocks;
|
||||
public Rectangle cellBounds;
|
||||
public CBlockTag()
|
||||
{
|
||||
blocks = new List<CBlockData>();
|
||||
}
|
||||
|
||||
public void UpdateFilter()
|
||||
{
|
||||
for (int i = 0; i < blocks.Count; i++)
|
||||
{
|
||||
CBlockData blockData = blocks[i];
|
||||
blockData.visible = Form1.m_filter == ""
|
||||
|| (blockData.name.ToLower().Contains(Form1.m_filter.ToLower())
|
||||
|| blockData.args.ToLower().Contains(Form1.m_filter.ToLower()));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Draw(ref DataGridViewCellPaintingEventArgs e, Form1 form)
|
||||
{
|
||||
e.Handled = true;
|
||||
Graphics g = e.Graphics;
|
||||
cellBounds = e.CellBounds;
|
||||
|
||||
const int offset = 2;
|
||||
Rectangle cellBound = new Rectangle(e.CellBounds.X, e.CellBounds.Y + offset,
|
||||
e.CellBounds.Width, e.CellBounds.Height - offset - 1);
|
||||
|
||||
using (Brush backBrush = new SolidBrush(Form1.s_clrBack[e.RowIndex % 2]))
|
||||
{
|
||||
using (Pen borderPen = new Pen(Color.Black, 1))
|
||||
{
|
||||
g.FillRectangle(backBrush, e.CellBounds);
|
||||
g.DrawRectangle(borderPen, e.CellBounds);
|
||||
|
||||
borderPen.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
CThreadTag threadData = form.m_threadsData[threadIndex];
|
||||
CBlockData threadTime = threadData.m_Times;
|
||||
long ttime = threadTime.time; // 100%
|
||||
|
||||
bool smallBlock = false;
|
||||
int smallBlockPos = 0;
|
||||
|
||||
for (int i = 0; i < blocks.Count; i++)
|
||||
{
|
||||
CBlockData blockData = blocks[i];
|
||||
if (!blockData.visible)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
long blockWidth = e.CellBounds.Width * blockData.time / ttime;
|
||||
cellBound.Width = (int)blockWidth;
|
||||
|
||||
long blockStart = (blockData.timeStart - threadTime.timeStart) * e.CellBounds.Width / ttime;
|
||||
cellBound.X = e.CellBounds.X + (int)blockStart;
|
||||
|
||||
blockData.drawXStart = cellBound.X;
|
||||
blockData.drawXEnd = cellBound.X + cellBound.Width;
|
||||
|
||||
if (smallBlock && smallBlockPos != cellBound.X)
|
||||
{
|
||||
Rectangle rec = new Rectangle(smallBlockPos, e.CellBounds.Y + offset,
|
||||
1, e.CellBounds.Height - offset - 1);
|
||||
|
||||
g.FillRectangle(form.m_BarsBrushes[blocks[i - 1].colorIdx], rec);
|
||||
|
||||
smallBlock = false;
|
||||
}
|
||||
|
||||
if (cellBound.Width < 1)
|
||||
{
|
||||
//cellBound.Width = 1;
|
||||
|
||||
smallBlockPos = cellBound.X;
|
||||
smallBlock = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((cellBound.X < e.ClipBounds.X + e.ClipBounds.Width) &&
|
||||
(cellBound.X + cellBound.Width > e.ClipBounds.X))
|
||||
{
|
||||
g.FillRectangle(form.m_BarsBrushes[blockData.colorIdx], cellBound);
|
||||
|
||||
SizeF textSize = blockData.m_labelSize;
|
||||
if (textSize.Width < cellBound.Width - 3)
|
||||
{
|
||||
StringFormat formatter = new StringFormat();
|
||||
formatter.LineAlignment = StringAlignment.Center;
|
||||
formatter.Alignment = StringAlignment.Center;
|
||||
float tx = cellBound.X + cellBound.Width / 2;
|
||||
float ty = cellBound.Y + cellBound.Height / 2;
|
||||
g.DrawString(blockData.name, form.m_barFont, Brushes.White, tx, ty, formatter);
|
||||
}
|
||||
}
|
||||
} //for (int i = 0; i < info.blocks.Count; i++)
|
||||
|
||||
if (smallBlock)
|
||||
{
|
||||
Rectangle rec = new Rectangle(smallBlockPos, e.CellBounds.Y + offset,
|
||||
1, e.CellBounds.Height - offset - 1);
|
||||
|
||||
g.FillRectangle(form.m_BarsBrushes[blocks[blocks.Count - 1].colorIdx], rec);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class CThreadTag : IRowTag
|
||||
{
|
||||
public CBlockData m_Times;
|
||||
public List<CBlockTag> m_StackEnties; //each stack level have a list of blocks
|
||||
public CThreadTag()
|
||||
{
|
||||
m_StackEnties = new List<CBlockTag>();
|
||||
}
|
||||
|
||||
public void UpdateFilter()
|
||||
{
|
||||
for (int i = 0; i < m_StackEnties.Count; i++)
|
||||
{
|
||||
CBlockTag tag = m_StackEnties[i];
|
||||
tag.UpdateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Draw(ref DataGridViewCellPaintingEventArgs e, Form1 form)
|
||||
{
|
||||
e.Handled = true;
|
||||
Graphics g = e.Graphics;
|
||||
|
||||
const int offset = 2;
|
||||
Rectangle cellBound = new Rectangle(e.CellBounds.X, e.CellBounds.Y + offset,
|
||||
e.CellBounds.Width, e.CellBounds.Height - offset - 1);
|
||||
|
||||
string threadText = "Thread: " + m_Times.name;
|
||||
g.FillRectangle(Brushes.DimGray, e.CellBounds);
|
||||
Font font = new Font(e.CellStyle.Font, FontStyle.Bold);
|
||||
|
||||
SizeF textSize = g.MeasureString(threadText, font);
|
||||
if (textSize.Width < cellBound.Width - 3)
|
||||
{
|
||||
StringFormat formatter = new StringFormat();
|
||||
formatter.LineAlignment = StringAlignment.Center;
|
||||
formatter.Alignment = StringAlignment.Center;
|
||||
float tx = cellBound.X + cellBound.Width / 2;
|
||||
float ty = cellBound.Y + cellBound.Height / 2;
|
||||
g.DrawString(threadText, font, Brushes.White, tx, ty, formatter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CTimeTag : IRowTag
|
||||
{
|
||||
private void DrawLinesBetween(ref DataGridViewCellPaintingEventArgs e, Pen pen, Font font, int pos1, int pos2, float pos2MS, float blockMS, int markerHeight)
|
||||
{
|
||||
int blockThreshold = 200; // in pixels
|
||||
//int blockDivCount = 5;
|
||||
|
||||
int diff = pos2 - pos1;
|
||||
|
||||
if (diff != 0 && diff > blockThreshold && markerHeight > 1)
|
||||
{
|
||||
|
||||
Graphics g = e.Graphics;
|
||||
|
||||
int X = e.CellBounds.X;
|
||||
int Y = e.CellBounds.Y + e.CellBounds.Height - 1;
|
||||
|
||||
int linePos = pos1 + (pos2 - pos1)/2;
|
||||
int DrawPos = X + linePos;
|
||||
|
||||
|
||||
float pos1MS = pos2MS - blockMS;
|
||||
float lineMS = pos1MS + (pos2MS - pos1MS) / 2.0f;
|
||||
|
||||
|
||||
//if (DrawPos > e.ClipBounds.X && DrawPos < e.ClipBounds.X + e.ClipBounds.Width) //artifacts ;(
|
||||
{
|
||||
g.DrawLine(pen, DrawPos, Y - markerHeight, DrawPos, Y);
|
||||
|
||||
|
||||
string s = string.Format("{0:##.##}", (lineMS));
|
||||
|
||||
StringFormat formatter = new StringFormat();
|
||||
formatter.LineAlignment = StringAlignment.Center;
|
||||
formatter.Alignment = StringAlignment.Center;
|
||||
g.DrawString(s, font, Brushes.Black, DrawPos, Y - markerHeight - 5, formatter);
|
||||
|
||||
blockMS = pos2MS - lineMS;
|
||||
DrawLinesBetween(ref e, pen, font, pos1, linePos, lineMS, blockMS, markerHeight - 2);
|
||||
DrawLinesBetween(ref e, pen, font, linePos, pos2, pos2MS, blockMS, markerHeight - 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Draw(ref DataGridViewCellPaintingEventArgs e, Form1 form)
|
||||
{
|
||||
e.Handled = true;
|
||||
Graphics g = e.Graphics;
|
||||
|
||||
g.FillRectangle(Brushes.White, e.CellBounds);
|
||||
|
||||
if (form.m_threadsData != null && form.m_threadsData.Count > 0)
|
||||
{
|
||||
Int32 cellWidth = e.CellBounds.Width;
|
||||
Int32 lineYpos = e.CellBounds.Y + e.CellBounds.Height - 1;
|
||||
|
||||
using (Pen btPen = new Pen(Color.Black, 2))
|
||||
{
|
||||
// bottom line
|
||||
g.DrawLine(btPen,
|
||||
e.CellBounds.X, lineYpos,
|
||||
e.CellBounds.X + cellWidth, lineYpos);
|
||||
|
||||
btPen.Dispose();
|
||||
}
|
||||
|
||||
|
||||
//form.dataGridView_Prof.PointToScreen()
|
||||
g.DrawString("Milliseconds", form.m_barFont, Brushes.Black, e.CellBounds.X + 1 , e.CellBounds.Y + 1);
|
||||
Pen pen = new Pen(Color.Black, 1);
|
||||
|
||||
CThreadTag threadData = form.m_threadsData[0];
|
||||
CBlockData threadTime = threadData.m_Times;
|
||||
float totalTimeMS = threadTime.timeMS;
|
||||
//long totalTime = threadTime.time;
|
||||
|
||||
int marketHeight = 16;
|
||||
|
||||
int[] blocks = {10000, 5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5, 2, 1};
|
||||
|
||||
float timeMSperBlock = 0.0f;
|
||||
int blockInx = 0;
|
||||
while (timeMSperBlock < 10.0f || blockInx > blocks.Count())
|
||||
{
|
||||
timeMSperBlock = totalTimeMS / blocks[blockInx++];
|
||||
}
|
||||
if (blockInx != 0 )
|
||||
--blockInx;
|
||||
|
||||
float blockMS = blocks[blockInx];
|
||||
|
||||
|
||||
|
||||
int linePx = 0;
|
||||
int linePrevPx = 0;
|
||||
int i = 0;
|
||||
while (linePx < cellWidth)
|
||||
{
|
||||
float lineMS = i *blockMS;
|
||||
++i;
|
||||
|
||||
linePrevPx = linePx;
|
||||
linePx = Convert.ToInt32(Math.Round((float)(lineMS * cellWidth) / totalTimeMS));
|
||||
|
||||
DrawLinesBetween(ref e, pen, form.m_barFont, linePrevPx, linePx, lineMS, blockMS, marketHeight);
|
||||
|
||||
int Xpos = e.CellBounds.X + linePx;
|
||||
g.DrawLine(pen, Xpos, lineYpos - marketHeight, Xpos, lineYpos);
|
||||
|
||||
string s = string.Format("{0}", Convert.ToInt32(lineMS));
|
||||
|
||||
StringFormat formatter = new StringFormat();
|
||||
formatter.LineAlignment = StringAlignment.Center;
|
||||
formatter.Alignment = StringAlignment.Center;
|
||||
float tx = Xpos;
|
||||
float ty = lineYpos - marketHeight - 5;
|
||||
|
||||
g.DrawString(s, form.m_barFont, Brushes.Black, tx, ty, formatter);
|
||||
}
|
||||
|
||||
pen.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
</root>
|
||||
@@ -1,129 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{3AF4C1C6-4DF2-4F3E-9997-B2844D461DF8}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ProfVis</RootNamespace>
|
||||
<AssemblyName>ProfVis</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.ReportViewer.Common, Version=11.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" />
|
||||
<Reference Include="Microsoft.ReportViewer.WinForms, Version=11.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Web.Services" />
|
||||
<Reference Include="System.Windows.Forms.DataVisualization" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Deployment" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Form1.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Form1.Designer.cs">
|
||||
<DependentUpon>Form1.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<EmbeddedResource Include="Form1.resx">
|
||||
<DependentUpon>Form1.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<None Include="Properties\DataSources\Form1.datasource" />
|
||||
<None Include="Properties\DataSources\ProfVis.Properties.Resources.datasource" />
|
||||
<None Include="Properties\DataSources\ProfVis.Properties.Settings.datasource" />
|
||||
<None Include="Properties\DataSources\Program.datasource" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.5">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Microsoft .NET Framework 4.5 %28x86 and x64%29</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ProfVis
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new Form1(args));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("ProfVis")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("ProfVis")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2012")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("c15ff8ac-4f15-4960-96a2-1cc11631169d")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is automatically generated by Visual Studio .Net. It is
|
||||
used to store generic object data source configuration information.
|
||||
Renaming the file extension or editing the content of this file may
|
||||
cause the file to be unrecognizable by the program.
|
||||
-->
|
||||
<GenericObjectDataSource DisplayName="Form1" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
|
||||
<TypeInfo>ProfVis.Form1, ProfVis, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
|
||||
</GenericObjectDataSource>
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is automatically generated by Visual Studio .Net. It is
|
||||
used to store generic object data source configuration information.
|
||||
Renaming the file extension or editing the content of this file may
|
||||
cause the file to be unrecognizable by the program.
|
||||
-->
|
||||
<GenericObjectDataSource DisplayName="Resources" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
|
||||
<TypeInfo>ProfVis.Properties.Resources, ProfVis, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
|
||||
</GenericObjectDataSource>
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is automatically generated by Visual Studio .Net. It is
|
||||
used to store generic object data source configuration information.
|
||||
Renaming the file extension or editing the content of this file may
|
||||
cause the file to be unrecognizable by the program.
|
||||
-->
|
||||
<GenericObjectDataSource DisplayName="Settings" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
|
||||
<TypeInfo>ProfVis.Properties.Settings, ProfVis, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
|
||||
</GenericObjectDataSource>
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is automatically generated by Visual Studio .Net. It is
|
||||
used to store generic object data source configuration information.
|
||||
Renaming the file extension or editing the content of this file may
|
||||
cause the file to be unrecognizable by the program.
|
||||
-->
|
||||
<GenericObjectDataSource DisplayName="Program" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
|
||||
<TypeInfo>ProfVis.Program, ProfVis, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
|
||||
</GenericObjectDataSource>
|
||||
@@ -1,72 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.17929
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
// Modifications copyright Amazon.com, Inc. or its affiliates
|
||||
|
||||
namespace ProfVis.Properties
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources
|
||||
{
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((resourceMan == null))
|
||||
{
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ProfVis.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return resourceCulture;
|
||||
}
|
||||
set
|
||||
{
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -1,31 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.17929
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
// Modifications copyright Amazon.com, Inc. or its affiliates
|
||||
|
||||
namespace ProfVis.Properties
|
||||
{
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||
{
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user