You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/AssetValidation/Code/Source/AssetSeedUtil.cpp

264 lines
12 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "AssetSeedUtil.h"
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
namespace AssetValidation::AssetSeed
{
GemInfo::GemInfo(AZStd::string name, AZStd::string relativeFilePath, AZStd::string absoluteFilePath, AZStd::string identifier, bool isGameGem, bool assetOnlyGem)
: m_gemName(AZStd::move(name))
, m_relativeFilePath(AZStd::move(relativeFilePath))
, m_absoluteFilePath(AZStd::move(absoluteFilePath))
, m_identifier(AZStd::move(identifier))
, m_isGameGem(isGameGem)
, m_assetOnly(assetOnlyGem)
{
}
void AddPlatformSeeds(const AZStd::string& rootFolder, AZStd::vector<AZStd::string>& defaultSeedLists, AzFramework::PlatformFlags platformFlags)
{
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
auto platforms = AzFramework::PlatformHelper::GetPlatformsInterpreted(platformFlags);
for (auto& platform : platforms)
{
AZStd::string platformDirectory;
AZ::StringFunc::Path::Join(rootFolder.c_str(), platform.data(), platformDirectory);
if (fileIO->Exists(platformDirectory.c_str()))
{
bool recurse = true;
AZ::Outcome<AZStd::list<AZStd::string>, AZStd::string> result = AzFramework::FileFunc::FindFileList(platformDirectory,
AZStd::string::format("*.%s", SeedFileExtension).c_str(), recurse);
if (result.IsSuccess())
{
AZStd::list<AZStd::string> seedFiles = result.TakeValue();
for (AZStd::string& seedFile : seedFiles)
{
AZStd::string normalizedFilePath = seedFile;
AZ::StringFunc::Path::Normalize(seedFile);
defaultSeedLists.emplace_back(seedFile);
}
}
}
}
}
void AddPlatformsDirectorySeeds(const AZStd::string& rootFolder, AZStd::vector<AZStd::string>& defaultSeedLists, AzFramework::PlatformFlags platformFlags)
{
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
AZ_Assert(fileIO, "AZ::IO::FileIOBase must be ready for use.\n");
// Check whether platforms directory exists inside the root, if yes than add
// * All seed files from the platforms directory
// * All platform specific seed files based on the platform flags specified.
AZStd::string platformsDirectory;
AZ::StringFunc::Path::Join(rootFolder.c_str(), PlatformsDirectoryName, platformsDirectory);
if (fileIO->Exists(platformsDirectory.c_str()))
{
fileIO->FindFiles(platformsDirectory.c_str(),
AZStd::string::format("*.%s", SeedFileExtension).c_str(),
[&](const char* fileName)
{
AZStd::string normalizedFilePath = fileName;
AZ::StringFunc::Path::Normalize(normalizedFilePath);
defaultSeedLists.emplace_back(normalizedFilePath);
return true;
});
}
AddPlatformSeeds(platformsDirectory, defaultSeedLists, platformFlags);
}
bool GetGemsInfo(const char* root, const char* assetRoot, const char* gameName, AZStd::vector<GemInfo>& gemInfoList)
{
Gems::IGemRegistry* registry = nullptr;
Gems::IProjectSettings* projectSettings = nullptr;
AZ::ModuleManagerRequests::LoadModuleOutcome result = AZ::Failure(AZStd::string("Failed to connect to ModuleManagerRequestBus.\n"));
AZ::ModuleManagerRequestBus::BroadcastResult(result, &AZ::ModuleManagerRequestBus::Events::LoadDynamicModule, "GemRegistry", AZ::ModuleInitializationSteps::Load, false);
if (!result.IsSuccess())
{
AZ_Error("AzToolsFramework::AssetUtils", false, "Could not load the GemRegistry module - %s.\n", result.GetError().c_str());
return false;
}
// Use shared_ptr aliasing ctor to use the refcount/deleter from the moduledata pointer, but we only need to store the dynamic module handle.
auto registryModule = AZStd::shared_ptr<AZ::DynamicModuleHandle>(result.GetValue(), result.GetValue()->GetDynamicModuleHandle());
auto CreateGemRegistry = registryModule->GetFunction<Gems::RegistryCreatorFunction>(GEMS_REGISTRY_CREATOR_FUNCTION_NAME);
Gems::RegistryDestroyerFunction registryDestroyerFunc = registryModule->GetFunction<Gems::RegistryDestroyerFunction>(GEMS_REGISTRY_DESTROYER_FUNCTION_NAME);
if (!CreateGemRegistry || !registryDestroyerFunc)
{
AZ_Error("AzToolsFramework::AssetUtils", false, "Failed to load GemRegistry functions.\n");
return false;
}
registry = CreateGemRegistry();
if (!registry)
{
AZ_Error("AzToolsFramework::AssetUtils", false, "Failed to create Gems::GemRegistry.\n");
return false;
}
registry->AddSearchPath({ root, GemsDirectoryName }, false);
registry->AddSearchPath({ assetRoot, GemsDirectoryName }, false);
const char* engineRootPath = nullptr;
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRootPath, &AzFramework::ApplicationRequests::GetEngineRoot);
if (engineRootPath && azstricmp(engineRootPath, root))
{
registry->AddSearchPath({ engineRootPath, GemsDirectoryName }, false);
}
projectSettings = registry->CreateProjectSettings();
if (!projectSettings)
{
registryDestroyerFunc(registry);
AZ_Error("AzToolsFramework::AssetUtils", false, "Failed to create Gems::ProjectSettings.\n");
return false;
}
if (!projectSettings->Initialize(assetRoot, gameName))
{
registry->DestroyProjectSettings(projectSettings);
registryDestroyerFunc(registry);
AZ_Error("AzToolsFramework::AssetUtils", false, "Failed to initialize Gems::ProjectSettings.\n");
return false;
}
auto loadProjectOutcome = registry->LoadProject(*projectSettings, true);
if (!loadProjectOutcome.IsSuccess())
{
registry->DestroyProjectSettings(projectSettings);
registryDestroyerFunc(registry);
AZ_Error("AzToolsFramework::AssetUtils", false, "Failed to load Gems project: %s.\n", loadProjectOutcome.GetError().c_str());
return false;
}
// Populating the gem info list
for (const auto& pair : projectSettings->GetGems())
{
Gems::IGemDescriptionConstPtr desc = registry->GetGemDescription(pair.second);
if (!desc)
{
Gems::ProjectGemSpecifier gemSpecifier = pair.second;
AZStd::string errorStr = AZStd::string::format("Failed to load Gem with ID %s and Version %s (from path %s).\n",
gemSpecifier.m_id.ToString<AZStd::string>().c_str(), gemSpecifier.m_version.ToString().c_str(), gemSpecifier.m_path.c_str());
if (Gems::IGemDescriptionConstPtr latestVersion = registry->GetLatestGem(pair.first))
{
errorStr += AZStd::string::format(" Found version %s, you may want to use that instead.\n", latestVersion->GetVersion().ToString().c_str());
}
AZ_Error("AzToolsFramework::AssetUtils", false, errorStr.c_str());
return false;
}
// Note: the two 'false' parameters in the ToString call below ToString(false, false)
// eliminates brackets and dashes in the formatting of the UUID.
// this keeps it compatible with legacy formatting which also omitted the curly braces and the dashes in the UUID.
AZStd::string gemId = desc->GetID().ToString<AZStd::string>(false, false).c_str();
AZStd::to_lower(gemId.begin(), gemId.end());
bool assetOnlyGem = true;
Gems::ModuleDefinitionVector moduleList = desc->GetModules();
for (Gems::ModuleDefinitionConstPtr moduleDef : moduleList)
{
if (moduleDef->m_linkType != Gems::LinkType::NoCode)
{
assetOnlyGem = false;
break;
}
}
gemInfoList.emplace_back(GemInfo(desc->GetName(), desc->GetPath(), desc->GetAbsolutePath(), gemId, desc->IsGameGem(), assetOnlyGem));
}
registry->DestroyProjectSettings(projectSettings);
registryDestroyerFunc(registry);
return true;
}
AZStd::vector<AZStd::string> GetGemSeedListFiles(const AZStd::vector<GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlags)
{
AZStd::vector<AZStd::string> gemSeedListFiles;
for (const GemInfo& gemInfo : gemInfoList)
{
AZStd::string absoluteGemSeedFilePath;
AZ::StringFunc::Path::ConstructFull(gemInfo.m_absoluteFilePath.c_str(), GemsSeedFileName, SeedFileExtension, absoluteGemSeedFilePath, true);
if (AZ::IO::FileIOBase::GetInstance()->Exists(absoluteGemSeedFilePath.c_str()))
{
gemSeedListFiles.emplace_back(absoluteGemSeedFilePath);
}
AddPlatformsDirectorySeeds(gemInfo.m_absoluteFilePath, gemSeedListFiles, platformFlags);
}
return gemSeedListFiles;
}
AZStd::vector<AZStd::string> GetDefaultSeedListFiles(const AZStd::vector<GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlag)
{
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
AZ_Assert(fileIO, "AZ::IO::FileIOBase must be ready for use.\n");
const char* root = nullptr;
AzFramework::ApplicationRequests::Bus::BroadcastResult(root, &AzFramework::ApplicationRequests::GetEngineRoot);
// Add all seed list files of enabled gems for the given project
AZStd::vector<AZStd::string> defaultSeedLists = GetGemSeedListFiles(gemInfoList, platformFlag);
// Add the engine seed list file
AZStd::string engineDirectory;
AZ::StringFunc::Path::Join(root, EngineDirectoryName, engineDirectory);
AZStd::string absoluteEngineSeedFilePath;
AZ::StringFunc::Path::ConstructFull(engineDirectory.c_str(), EngineSeedFileName, SeedFileExtension, absoluteEngineSeedFilePath, true);
if (fileIO->Exists(absoluteEngineSeedFilePath.c_str()))
{
defaultSeedLists.emplace_back(absoluteEngineSeedFilePath);
}
AddPlatformsDirectorySeeds(engineDirectory, defaultSeedLists, platformFlag);
// Add the current project default seed list file
AZStd::string projectName;
bool checkPlatform = false;
auto settingsRegistry = AZ::SettingsRegistry::Get();
if (settingsRegistry)
{
AZ::SettingsRegistryInterface::FixedValueString bootstrapProjectName;
auto projectKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/sys_game_folder", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey);
settingsRegistry->Get(bootstrapProjectName, projectKey);
if (!bootstrapProjectName.empty())
{
AZStd::string absoluteProjectDefaultSeedFilePath;
AZ::StringFunc::Path::ConstructFull(root, bootstrapProjectName.c_str(), EngineSeedFileName, SeedFileExtension, absoluteProjectDefaultSeedFilePath, true);
if (fileIO->Exists(absoluteProjectDefaultSeedFilePath.c_str()))
{
defaultSeedLists.emplace_back(move(absoluteProjectDefaultSeedFilePath));
}
}
}
return defaultSeedLists;
}
}