Integrating latest from github/staging

Integrating up through commit 5e1bdae
This commit is contained in:
alexpete
2021-03-26 14:31:50 -07:00
parent 9c54341af8
commit 36c4e827bd
764 changed files with 11453 additions and 20251 deletions
@@ -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:
+65
View File
@@ -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([&registry](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)
{
@@ -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.
@@ -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
@@ -2,7 +2,7 @@
; without a scan folder, it is an invalid file.
[ScanFolder Game]
watch=@ROOT@/@GAMENAME@
watch=@PROJECTROOT@
recursive=1
order=0
@@ -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
@@ -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
@@ -42,7 +42,7 @@ cbc=abc
fbx.assetinfo=fbx
[ScanFolder Game]
watch=@ROOT@/@GAMENAME@
watch=@PROJECTROOT@
display=gameoutput
recursive=1
order=0
-12
View File
@@ -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>