Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
+68
View File
@@ -0,0 +1,68 @@
#
# 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(NOT PAL_TRAIT_BUILD_HOST_TOOLS)
return()
endif()
ly_add_target(
NAME AssetBundler.Static STATIC
NAMESPACE AZ
FILES_CMAKE
assetbundlerbatch_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
COMPILE_DEFINITIONS
PRIVATE
METRICS_ENABLED
BUILD_DEPENDENCIES
PUBLIC
AZ::AzToolsFramework
${additional_dependencies}
)
ly_add_target(
NAME AssetBundlerBatch EXECUTABLE
NAMESPACE AZ
FILES_CMAKE
assetbundlerbatch_exe_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
BUILD_DEPENDENCIES
PRIVATE
AZ::AssetBundler.Static
)
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_target(
NAME AssetBundler.Tests EXECUTABLE
NAMESPACE AZ
FILES_CMAKE
assetbundlerbatch_test_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
AZ::AssetBundler.Static
AZ::AzFrameworkTestShared
)
ly_add_googletest(
NAME AZ::AssetBundler.Tests
TEST_COMMAND $<TARGET_FILE:AZ::AssetBundler.Tests> --unittest
)
endif()
@@ -0,0 +1,14 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
source/main.cpp
)
@@ -0,0 +1,17 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
source/utils/utils.h
source/utils/utils.cpp
source/utils/applicationManager.h
source/utils/applicationManager.cpp
)
@@ -0,0 +1,17 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
tests/applicationManagerTests.cpp
tests/tests_main.cpp
tests/main.h
tests/UtilsTests.cpp
)
+26
View File
@@ -0,0 +1,26 @@
/*
* 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 <source/utils/applicationManager.h>
int main(int argc, char* argv[])
{
AZ::AllocatorInstance<AZ::SystemAllocator>::Create();
int runSuccess = 0;
{
AssetBundler::ApplicationManager applicationManger(&argc, &argv);
applicationManger.Init();
runSuccess = applicationManger.Run() ? 0 : 1;
}
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
return runSuccess;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,305 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AzToolsFramework/Asset/AssetSeedManager.h>
#include <AzToolsFramework/Asset/AssetBundler.h>
#include <AzToolsFramework/Asset/AssetUtils.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <source/utils/utils.h>
#include <AzToolsFramework/AssetCatalog/PlatformAddressedAssetCatalogManager.h>
namespace AssetBundler
{
struct SeedsParams
{
AZ_CLASS_ALLOCATOR(SeedsParams, AZ::SystemAllocator, 0);
FilePath m_seedListFile;
AZStd::vector<AZStd::string> m_addSeedList;
AZStd::vector<AZStd::string> m_removeSeedList;
bool m_addPlatformToAllSeeds = false;
bool m_removePlatformFromAllSeeds = false;
bool m_updateSeedPathHint = false;
bool m_removeSeedPathHint = false;
bool m_ignoreFileCase = false;
bool m_save = false;
bool m_print = false;
AzFramework::PlatformFlags m_platformFlags = AzFramework::PlatformFlags::Platform_NONE;
FilePath m_assetCatalogFile;
};
struct AssetListsParams
{
AZ_CLASS_ALLOCATOR(AssetListsParams, AZ::SystemAllocator, 0);
FilePath m_assetListFile;
AZStd::vector<FilePath> m_seedListFiles;
AZStd::vector<AZStd::string> m_addSeedList;
AZStd::vector<AZStd::string> m_skipList;
bool m_addDefaultSeedListFiles = false;
bool m_print = false;
bool m_dryRun = false;
bool m_generateDebugFile = false;
bool m_allowOverwrites = false;
AzFramework::PlatformFlags m_platformFlags = AzFramework::PlatformFlags::Platform_NONE;
FilePath m_assetCatalogFile;
};
enum ComparisonRulesStepAction
{
Add,
AddToEnd,
Remove,
Move,
Edit,
Default,
};
struct ComparisonRulesParams
{
AZ_CLASS_ALLOCATOR(ComparisonRulesParams, AZ::SystemAllocator, 0);
AZStd::vector<AzToolsFramework::AssetFileInfoListComparison::ComparisonType> m_comparisonTypeList;
AZStd::vector<AZStd::string> m_filePatternList;
AZStd::vector<AzToolsFramework::AssetFileInfoListComparison::FilePatternType> m_filePatternTypeList;
AZStd::vector<AZStd::string> m_tokenNamesList;
AZStd::vector<AZStd::string> m_firstInputList;
AZStd::vector<AZStd::string> m_secondInputList;
FilePath m_comparisonRulesFile;
ComparisonRulesStepAction m_comparisonRulesStepAction = ComparisonRulesStepAction::Default;
size_t m_initialLine = 0;
size_t m_destinationLine = 0;
unsigned int m_intersectionCount = 0;
bool m_print = false;
};
struct ComparisonParams
{
AZ_CLASS_ALLOCATOR(ComparisonParams, AZ::SystemAllocator, 0);
// Comparison input/output
AZStd::vector<AZStd::string> m_firstCompareFile;
AZStd::vector<AZStd::string> m_secondCompareFile;
AZStd::vector<AZStd::string> m_outputs;
AZStd::vector<AZStd::string> m_printComparisons;
bool m_printLast = false;
bool m_allowOverwrites = false;
AzFramework::PlatformFlags m_platformFlags = AzFramework::PlatformFlags::Platform_NONE;
// Comparison definitions
FilePath m_comparisonRulesFile;
ComparisonRulesParams m_comparisonRulesParams;
};
struct BundleSettingsParams
{
AZ_CLASS_ALLOCATOR(BundleSettingsParams, AZ::SystemAllocator, 0);
FilePath m_bundleSettingsFile;
FilePath m_assetListFile;
FilePath m_outputBundlePath;
int m_bundleVersion = -1;
int m_maxBundleSizeInMB = -1;
bool m_print = false;
AzFramework::PlatformFlags m_platformFlags = AzFramework::PlatformFlags::Platform_NONE;
};
struct BundlesParams
{
AZ_CLASS_ALLOCATOR(BundlesParams, AZ::SystemAllocator, 0);
FilePath m_bundleSettingsFile;
FilePath m_assetListFile;
FilePath m_outputBundlePath;
int m_bundleVersion = -1;
int m_maxBundleSizeInMB = -1;
AzFramework::PlatformFlags m_platformFlags = AzFramework::PlatformFlags::Platform_NONE;
bool m_allowOverwrites = false;
};
typedef AZStd::vector<BundlesParams> BundlesParamsList;
struct BundleSeedParams
{
AZ_CLASS_ALLOCATOR(BundleSeedParams, AZ::SystemAllocator, 0);
AZStd::vector<AZStd::string> m_addSeedList;
BundlesParams m_bundleParams;
};
class ApplicationManager
: public AZ::Debug::TraceMessageBus::Handler
, public AzToolsFramework::ToolsApplication
{
public:
explicit ApplicationManager(int* argc, char*** argv);
~ApplicationManager();
void Init();
void DestroyApplication();
bool Run();
////////////////////////////////////////////////////////////////////////////////////////////
// AzFramework::Application overrides
AZ::ComponentTypeList GetRequiredSystemComponents() const override;
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
// TraceMessageBus Interface
bool OnPreError(const char* window, const char* fileName, int line, const char* func, const char* message) override;
bool OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message) override;
bool OnPrintf(const char* window, const char* message) override;
////////////////////////////////////////////////////////////////////////////////////////////
AZStd::string GetCurrentProjectName() { return m_currentProjectName; }
AZStd::vector<AzToolsFramework::AssetUtils::GemInfo> GetGemInfoList() { return m_gemInfoList; }
protected:
////////////////////////////////////////////////////////////////////////////////////////////
// AzFramework::Application overrides
void SetSettingsRegistrySpecializations(AZ::SettingsRegistryInterface::Specializations& specializations) override;
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
// Get Generic Command Info
CommandType GetCommandType(const AzFramework::CommandLine* parser, bool suppressErrors);
bool ShouldPrintHelp(const AzFramework::CommandLine* parser);
bool ShouldPrintVerbose(const AzFramework::CommandLine* parser);
void InitArgValidationLists();
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
// Store Detailed Command Info and Validate parser input (command correctness)
AZ::Outcome<SeedsParams, AZStd::string> ParseSeedsCommandData(const AzFramework::CommandLine* parser);
AZ::Outcome<AssetListsParams, AZStd::string> ParseAssetListsCommandData(const AzFramework::CommandLine* parser);
AZ::Outcome<ComparisonRulesParams, AZStd::string> ParseComparisonRulesCommandData(const AzFramework::CommandLine* parser);
AZ::Outcome<ComparisonParams, AZStd::string> ParseCompareCommandData(const AzFramework::CommandLine* parser);
AZ::Outcome<BundleSettingsParams, AZStd::string> ParseBundleSettingsCommandData(const AzFramework::CommandLine* parser);
AZ::Outcome<BundlesParamsList, AZStd::string> ParseBundlesCommandData(const AzFramework::CommandLine* parser);
AZ::Outcome<BundleSeedParams, AZStd::string> ParseBundleSeedCommandData(const AzFramework::CommandLine* parser);
AZ::Outcome<void, AZStd::string> ValidateInputArgs(const AzFramework::CommandLine* parser, const AZStd::vector<const char*>& validArgList);
AZ::Outcome<AZStd::string, AZStd::string> GetFilePathArg(const AzFramework::CommandLine* parser, const char* argName, const char* subCommandName, bool isRequired = false);
template <typename T>
AZ::Outcome<AZStd::vector<T>, AZStd::string> GetArgsList(const AzFramework::CommandLine* parser, const char* argName, const char* subCommandName, bool isRequired = false);
AZ::Outcome<AzFramework::PlatformFlags, AZStd::string> GetPlatformArg(const AzFramework::CommandLine* parser);
AzFramework::PlatformFlags GetInputPlatformFlagsOrEnabledPlatformFlags(AzFramework::PlatformFlags inputPlatformFlags);
AZStd::vector<AZStd::string> GetAddSeedArgList(const AzFramework::CommandLine* parser);
AZStd::vector<AZStd::string> GetSkipArgList(const AzFramework::CommandLine* parser);
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
// Run Commands and Validate param data (value correctness)
bool RunSeedsCommands(const AZ::Outcome<SeedsParams, AZStd::string>& paramsOutcome);
bool RunAssetListsCommands(const AZ::Outcome<AssetListsParams, AZStd::string>& paramsOutcome);
bool RunComparisonRulesCommands(const AZ::Outcome<ComparisonRulesParams, AZStd::string>& paramsOutcome);
bool RunCompareCommand(const AZ::Outcome<ComparisonParams, AZStd::string>& paramsOutcome);
bool RunBundleSettingsCommands(const AZ::Outcome<BundleSettingsParams, AZStd::string>& paramsOutcome);
bool RunBundlesCommands(const AZ::Outcome<BundlesParamsList, AZStd::string>& paramsOutcome);
bool RunBundleSeedCommands(const AZ::Outcome<BundleSeedParams, AZStd::string>& paramsOutcome);
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
// Helpers
AZ::Outcome<void, AZStd::string> InitAssetCatalog(AzFramework::PlatformFlags platforms, const AZStd::string& assetCatalogFile = AZStd::string());
//! Given a gem seed file, validates whether the seed file is valid for the current project
//! and platform flags specified before loading the file from disk.
//! Does not do any validation on non gem seed files.
AZ::Outcome<void, AZStd::string> LoadSeedListFile(const AZStd::string& seedListFileAbsolutePath, AzFramework::PlatformFlags platformFlags);
AZ::Outcome<void, AZStd::string> LoadProjectDependenciesFile(AzFramework::PlatformFlags platformFlags);
void PrintSeedList(const AZStd::string& seedListFileAbsolutePath);
bool RunPlatformSpecificAssetListCommands(const AssetListsParams& params, AzFramework::PlatformFlags platformFlags);
void PrintAssetLists(const AssetListsParams& params,
const AZStd::fixed_vector<AzFramework::PlatformId, AzFramework::PlatformId::NumPlatformIds>& platformIds,
bool printExistingFiles,
const AZStd::unordered_set<AZ::Data::AssetId>& exclusionList,
const AZStd::vector<AZStd::string>& wildcardPatternExclusionList);
AZStd::vector<FilePath> GetAllPlatformSpecificFilesOnDisk(const FilePath& platformIndependentFilePath, AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_NONE);
AZ::Outcome<void, AZStd::string> ApplyBundleSettingsOverrides(
AzToolsFramework::AssetBundleSettings& bundleSettings,
const AZStd::string& assetListFilePath,
const AZStd::string& outputBundleFilePath,
int bundleVersion,
int maxBundleSize);
AZ::Outcome<void, AZStd::string> ParseComparisonTypesAndPatterns(const AzFramework::CommandLine* parser, ComparisonRulesParams& params);
AZ::Outcome<void, AZStd::string> ParseComparisonTypesAndPatternsForEditCommand(const AzFramework::CommandLine* parser, ComparisonRulesParams& params);
AZ::Outcome<void, AZStd::string> ParseComparisonRulesFirstAndSecondInputArgs(const AzFramework::CommandLine* parser, ComparisonRulesParams& params);
AZ::Outcome<BundlesParamsList, AZStd::string> ParseBundleSettingsAndOverrides(const AzFramework::CommandLine* parser, const char* commandName);
bool ConvertRulesParamsToComparisonData(const ComparisonRulesParams& params, AzToolsFramework::AssetFileInfoListComparison& assetListComparison, size_t startingIndex);
bool EditComparisonData(const ComparisonRulesParams& params, AzToolsFramework::AssetFileInfoListComparison& assetListComparison, size_t index);
void PrintComparisonRules(const AzToolsFramework::AssetFileInfoListComparison& assetListComparison, const AZStd::string& comparisonRulesAbsoluteFilePath);
bool IsDefaultToken(const AZStd::string& pathOrToken);
void PrintComparisonAssetList(const AzToolsFramework::AssetFileInfoList& infoList, const AZStd::string& resultName);
void AddPlatformToAllComparisonParams(ComparisonParams& params, const AZStd::string& platformName);
void AddPlatformToComparisonParam(AZStd::string& inOut, const AZStd::string& platformName);
//! Error message to display when neither of two optional arguments was found
static AZStd::string GetBinaryArgOptionFailure(const char* arg1, const char* arg2);
bool SeedsOperationRequiresCatalog(const SeedsParams& params);
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
// Output Help Text
void OutputHelp(CommandType commandType);
void OutputHelpSeeds();
void OutputHelpAssetLists();
void OutputHelpComparisonRules();
void OutputHelpCompare();
void OutputHelpBundleSettings();
void OutputHelpBundles();
void OutputHelpBundleSeed();
////////////////////////////////////////////////////////////////////////////////////////////
AZStd::unique_ptr<AzToolsFramework::AssetSeedManager> m_assetSeedManager;
AZStd::unique_ptr<AzToolsFramework::PlatformAddressedAssetCatalogManager> m_platformCatalogManager;
AZStd::vector<AzToolsFramework::AssetUtils::GemInfo> m_gemInfoList;
bool m_showVerboseOutput = false;
AZStd::string m_currentProjectName;
CommandType m_commandType = CommandType::Invalid;
AZStd::vector<const char*> m_allSeedsArgs;
AZStd::vector<const char*> m_allAssetListsArgs;
AZStd::vector<const char*> m_allComparisonRulesArgs;
AZStd::vector<const char*> m_allCompareArgs;
AZStd::vector<const char*> m_allBundleSettingsArgs;
AZStd::vector<const char*> m_allBundlesArgs;
AZStd::vector<const char*> m_allBundleSeedArgs;
};
}
@@ -0,0 +1,977 @@
/*
* 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 <AzFramework/Asset/AssetSystemComponent.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/FileFunc/FileFunc.h>
#include <AzFramework/Platform/PlatformDefaults.h>
#include <AzToolsFramework/Asset/AssetSeedManager.h>
#include <AzToolsFramework/Asset/AssetBundler.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <source/utils/utils.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/string/regex.h>
#include <cctype>
AZ_PUSH_DISABLE_WARNING(4244 4251, "-Wunknown-warning-option")
#include <QDir>
#include <QString>
#include <QStringList>
AZ_POP_DISABLE_WARNING
namespace AssetBundler
{
// General
const char* AppWindowName = "AssetBundler";
const char* AppWindowNameVerbose = "AssetBundlerVerbose";
const char* HelpFlag = "help";
const char* HelpFlagAlias = "h";
const char* VerboseFlag = "verbose";
const char* SaveFlag = "save";
const char* PlatformArg = "platform";
const char* PrintFlag = "print";
const char* AssetCatalogFileArg = "overrideAssetCatalogFile";
const char* AllowOverwritesFlag = "allowOverwrites";
const char* IgnoreFileCaseFlag = "ignoreFileCase";
const char* ProjectArg = "project";
// Seeds
const char* SeedsCommand = "seeds";
const char* SeedListFileArg = "seedListFile";
const char* AddSeedArg = "addSeed";
const char* RemoveSeedArg = "removeSeed";
const char* AddPlatformToAllSeedsFlag = "addPlatformToSeeds";
const char* RemovePlatformFromAllSeedsFlag = "removePlatformFromSeeds";
const char* UpdateSeedPathArg = "updateSeedPath";
const char* RemoveSeedPathArg = "removeSeedPath";
const char* DefaultProjectTemplatePath = "ProjectTemplates/DefaultTemplate/${ProjectName}";
const char* ProjectName = "${ProjectName}";
const char* DependenciesFileSuffix = "_Dependencies";
const char* DependenciesFileExtension = "xml";
// Asset Lists
const char* AssetListsCommand = "assetLists";
const char* AssetListFileArg = "assetListFile";
const char* AddDefaultSeedListFilesFlag = "addDefaultSeedListFiles";
const char* DryRunFlag = "dryRun";
const char* GenerateDebugFileFlag = "generateDebugFile";
const char* SkipArg = "skip";
// Comparison Rules
const char* ComparisonRulesCommand = "comparisonRules";
const char* ComparisonRulesFileArg = "comparisonRulesFile";
const char* ComparisonTypeArg = "comparisonType";
const char* ComparisonFilePatternArg = "filePattern";
const char* ComparisonFilePatternTypeArg = "filePatternType";
const char* ComparisonTokenNameArg = "tokenName";
const char* ComparisonFirstInputArg = "firstInput";
const char* ComparisonSecondInputArg = "secondInput";
const char* AddComparisonStepArg = "addComparison";
const char* RemoveComparisonStepArg = "removeComparison";
const char* MoveComparisonStepArg = "moveComparison";
const char* EditComparisonStepArg = "editComparison";
// Compare
const char* CompareCommand = "compare";
const char* CompareFirstFileArg = "firstAssetFile";
const char* CompareSecondFileArg = "secondAssetFile";
const char* CompareOutputFileArg = "output";
const char* ComparePrintArg = "print";
const char* IntersectionCountArg = "intersectionCount";
// Bundle Settings
const char* BundleSettingsCommand = "bundleSettings";
const char* BundleSettingsFileArg = "bundleSettingsFile";
const char* OutputBundlePathArg = "outputBundlePath";
const char* BundleVersionArg = "bundleVersion";
const char* MaxBundleSizeArg = "maxSize";
// Bundles
const char* BundlesCommand = "bundles";
// Bundle Seed
const char* BundleSeedCommand = "bundleSeed";
const char* AssetCatalogFilename = "assetcatalog.xml";
char g_cachedEngineRoot[AZ_MAX_PATH_LEN];
const char EngineDirectoryName[] = "Engine";
const char RestrictedDirectoryName[] = "restricted";
const char PlatformsDirectoryName[] = "Platforms";
const char GemsDirectoryName[] = "Gems";
const char GemsAssetsDirectoryName[] = "Assets";
const char GemsSeedFileName[] = "seedList";
const char EngineSeedFileName[] = "SeedAssetList";
namespace Internal
{
const AZ::u32 PlatformFlags_RESTRICTED = aznumeric_cast<AZ::u32>(AzFramework::PlatformFlags::Platform_JASPER | AzFramework::PlatformFlags::Platform_PROVO | AzFramework::PlatformFlags::Platform_SALEM | AzFramework::PlatformFlags::Platform_XENIA);
void AddPlatformSeeds(
AZStd::string rootFolder,
const AZStd::string& rootFolderDisplayName,
AZStd::unordered_map<AZStd::string, AZStd::string>& defaultSeedLists,
AzFramework::PlatformFlags platformFlags)
{
AZ::IO::FixedMaxPath engineRoot(GetEngineRoot());
AZ::IO::FixedMaxPath engineRestrcitedRoot = engineRoot / RestrictedDirectoryName;
AZ::IO::FixedMaxPath inputPath = AZ::IO::FixedMaxPath(rootFolder);
AZ::IO::FixedMaxPath engineLocalPath = inputPath.LexicallyRelative(engineRoot);
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
auto platformsIdxList = AzFramework::PlatformHelper::GetPlatformIndicesInterpreted(platformFlags);
for (const AzFramework::PlatformId& platformId : platformsIdxList)
{
const AzFramework::PlatformFlags platformFlag = AzFramework::PlatformHelper::GetPlatformFlagFromPlatformIndex(platformId);
const char* platformDirName = AzFramework::PlatformHelper::GetPlatformName(platformId);
AZ::IO::FixedMaxPath platformDirectory;
if (aznumeric_cast<AZ::u32>(platformFlag) & PlatformFlags_RESTRICTED)
{
platformDirectory = engineRestrcitedRoot / platformDirName / engineLocalPath;
}
else
{
platformDirectory = inputPath / PlatformsDirectoryName / platformDirName;
}
if (fileIO->Exists(platformDirectory.c_str()))
{
bool recurse = true;
AZ::Outcome<AZStd::list<AZStd::string>, AZStd::string> result = AzFramework::FileFunc::FindFileList(platformDirectory.String(),
AZStd::string::format("*.%s", AzToolsFramework::AssetSeedManager::GetSeedFileExtension()).c_str(), recurse);
if (result.IsSuccess())
{
AZStd::list<AZStd::string> seedFiles = result.TakeValue();
for (AZStd::string& seedFile : seedFiles)
{
AZStd::string normalizedFilePath = seedFile;
AzFramework::StringFunc::Path::Normalize(seedFile);
defaultSeedLists[seedFile] = AZStd::string::format("%s (%s)", rootFolderDisplayName.c_str(), platformDirName);
}
}
}
}
}
void AddPlatformsDirectorySeeds(
const AZStd::string& rootFolder,
const AZStd::string& rootFolderDisplayName,
AZStd::unordered_map<AZStd::string, 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;
AzFramework::StringFunc::Path::Join(rootFolder.c_str(), PlatformsDirectoryName, platformsDirectory);
if (fileIO->Exists(platformsDirectory.c_str()))
{
fileIO->FindFiles(platformsDirectory.c_str(),
AZStd::string::format("*.%s", AzToolsFramework::AssetSeedManager::GetSeedFileExtension()).c_str(),
[&](const char* fileName)
{
AZStd::string normalizedFilePath = fileName;
AzFramework::StringFunc::Path::Normalize(normalizedFilePath);
defaultSeedLists[normalizedFilePath] = rootFolderDisplayName;
return true;
});
}
AddPlatformSeeds(rootFolder, rootFolderDisplayName, defaultSeedLists, platformFlags);
}
}
bool ComputeEngineRoot()
{
if (g_cachedEngineRoot[0])
{
return true;
}
const char* engineRoot = nullptr;
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot);
if (!engineRoot)
{
AZ_Error(AssetBundler::AppWindowName, false, "Unable to locate engine root.\n");
return false;
}
azstrcpy(g_cachedEngineRoot, AZ_MAX_PATH_LEN, engineRoot);
return true;
}
const char* GetEngineRoot()
{
if (!g_cachedEngineRoot[0])
{
ComputeEngineRoot();
}
return g_cachedEngineRoot;
}
AZ::Outcome<void, AZStd::string> ComputeAssetAliasAndGameName(const AZStd::string& platformIdentifier, const AZStd::string& assetCatalogFile, AZStd::string& assetAlias, AZStd::string& gameName)
{
AZStd::string assetPath;
AZStd::string gameFolder;
if (!ComputeEngineRoot())
{
return AZ::Failure(AZStd::string("Unable to compute engine root.\n"));
}
if (assetCatalogFile.empty())
{
if (gameName.empty())
{
bool checkPlatform = false;
bool result{};
auto gameFolderKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/%s",
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, AzFramework::AssetSystem::ProjectName);
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
result = settingsRegistry->Get(gameFolder, gameFolderKey);
}
if (!result)
{
return AZ::Failure(AZStd::string("Unable to locate game name in bootstrap.\n"));
}
gameName = gameFolder;
}
else
{
gameFolder = gameName;
}
// Appending Cache/%gamename%/%platform%/%gameName% to the engine root
bool success = AzFramework::StringFunc::Path::ConstructFull(g_cachedEngineRoot, "Cache", assetPath) &&
AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), gameFolder.c_str(), assetPath) &&
AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), platformIdentifier.c_str(), assetPath);
if (success)
{
AZStd::to_lower(gameFolder.begin(), gameFolder.end());
success = AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), gameFolder.c_str(), assetPath); // game name is lowercase
}
if (success)
{
assetAlias = assetPath;
}
}
else if (AzFramework::StringFunc::Path::GetFullPath(assetCatalogFile.c_str(), assetPath))
{
AzFramework::StringFunc::Strip(assetPath, AZ_CORRECT_FILESYSTEM_SEPARATOR, false, false, true);
assetAlias = assetPath;
// 3rd component from reverse should give us the correct case game name because the assetalias directory
// looks like ./Cache/%GameName%/%platform%/%gameName%/assetcatalog.xml
// GetComponent util method returns the component with the separator appended at the end
// therefore we need to strip the separator to get the game name string
gameFolder = AZ::IO::PathView(assetPath).ParentPath().ParentPath().Filename().Native();
if (gameFolder.empty())
{
return AZ::Failure(AZStd::string::format("Unable to retrieve game name from assetCatalog file (%s).\n", assetCatalogFile.c_str()));
}
if (!AzFramework::StringFunc::Strip(gameFolder, AZ_CORRECT_FILESYSTEM_SEPARATOR, false, false, true))
{
return AZ::Failure(AZStd::string::format("Unable to strip separator from game name (%s).\n", gameFolder.c_str()));
}
if (!gameName.empty() && !AzFramework::StringFunc::Equal(gameFolder.c_str(), gameName.c_str()))
{
return AZ::Failure(AZStd::string::format("Game name retrieved from the assetCatalog file (%s) does not match the inputted game name (%s).\n", gameFolder.c_str(), gameName.c_str()));
}
else
{
gameName = gameFolder;
}
}
return AZ::Success();
}
void AddPlatformIdentifier(AZStd::string& filePath, const AZStd::string& platformIdentifier)
{
AZStd::string fileName;
AzFramework::StringFunc::Path::GetFileName(filePath.c_str(), fileName);
AZStd::string extension;
AzFramework::StringFunc::Path::GetExtension(filePath.c_str(), extension);
AZStd::string platformSuffix = AZStd::string::format("_%s", platformIdentifier.c_str());
fileName = AZStd::string::format("%s%s", fileName.c_str(), platformSuffix.c_str());
AzFramework::StringFunc::Path::ReplaceFullName(filePath, fileName.c_str(), extension.c_str());
}
AzFramework::PlatformFlags GetPlatformsOnDiskForPlatformSpecificFile(const AZStd::string& platformIndependentAbsolutePath)
{
AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_NONE;
auto allPlatformNames = AzFramework::PlatformHelper::GetPlatforms(AzFramework::PlatformFlags::AllNamedPlatforms);
for (const auto& platformName : allPlatformNames)
{
AZStd::string filePath = platformIndependentAbsolutePath;
AddPlatformIdentifier(filePath, platformName);
if (AZ::IO::FileIOBase::GetInstance()->Exists(filePath.c_str()))
{
platformFlags = platformFlags | AzFramework::PlatformHelper::GetPlatformFlag(platformName);
}
}
return platformFlags;
}
AZStd::unordered_map<AZStd::string, AZStd::string> GetDefaultSeedListFiles(const char* root, const char* projectName, const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlag)
{
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
AZ_Assert(fileIO, "AZ::IO::FileIOBase must be ready for use.\n");
// Add all seed list files of enabled gems for the given project
AZStd::unordered_map<AZStd::string, AZStd::string> defaultSeedLists = GetGemSeedListFilePathToGemNameMap(gemInfoList, platformFlag);
// Add the engine seed list file
AZStd::string engineDirectory;
AzFramework::StringFunc::Path::Join(root, EngineDirectoryName, engineDirectory);
AZStd::string absoluteEngineSeedFilePath;
AzFramework::StringFunc::Path::ConstructFull(engineDirectory.c_str(), EngineSeedFileName, AzToolsFramework::AssetSeedManager::GetSeedFileExtension(), absoluteEngineSeedFilePath, true);
if (fileIO->Exists(absoluteEngineSeedFilePath.c_str()))
{
defaultSeedLists[absoluteEngineSeedFilePath] = EngineDirectoryName;
}
// Add Seed Lists from the Platforms directory
Internal::AddPlatformsDirectorySeeds(engineDirectory, EngineDirectoryName, defaultSeedLists, platformFlag);
AZStd::string absoluteProjectDefaultSeedFilePath;
AzFramework::StringFunc::Path::ConstructFull(root, projectName, EngineSeedFileName, AzToolsFramework::AssetSeedManager::GetSeedFileExtension(), absoluteProjectDefaultSeedFilePath, true);
if (fileIO->Exists(absoluteProjectDefaultSeedFilePath.c_str()))
{
defaultSeedLists[absoluteProjectDefaultSeedFilePath] = projectName;
}
return defaultSeedLists;
}
AZStd::vector<AZStd::string> GetDefaultSeeds(const char* root, const char* projectName)
{
AZStd::vector<AZStd::string> defaultSeeds;
defaultSeeds.emplace_back(GetProjectDependenciesAssetPath(root, projectName));
return defaultSeeds;
}
AZStd::string GetProjectDependenciesFile(const char* root, const char* projectName)
{
AZStd::string projectDependenciesFilePath = AZStd::string::format("%s%s", projectName, DependenciesFileSuffix);
AzFramework::StringFunc::Path::ConstructFull(root, projectName, projectDependenciesFilePath.c_str(), DependenciesFileExtension, projectDependenciesFilePath, true);
return projectDependenciesFilePath;
}
AZStd::string GetProjectDependenciesFileTemplate(const char* root)
{
AZStd::string projectDependenciesFileTemplate = ProjectName;
projectDependenciesFileTemplate += DependenciesFileSuffix;
AzFramework::StringFunc::Path::ConstructFull(root, DefaultProjectTemplatePath, projectDependenciesFileTemplate.c_str(), DependenciesFileExtension, projectDependenciesFileTemplate, true);
return projectDependenciesFileTemplate;
}
AZStd::string GetProjectDependenciesAssetPath(const char* root, const char* projectName)
{
AZStd::string projectDependenciesFile = AZStd::move(GetProjectDependenciesFile(root, projectName));
if (!AZ::IO::FileIOBase::GetInstance()->Exists(projectDependenciesFile.c_str()))
{
AZ_TracePrintf(AssetBundler::AppWindowName, "Project dependencies file %s doesn't exist.\n", projectDependenciesFile.c_str());
AZStd::string projectDependenciesFileTemplate = AZStd::move(GetProjectDependenciesFileTemplate(root));
if (AZ::IO::FileIOBase::GetInstance()->Copy(projectDependenciesFileTemplate.c_str(), projectDependenciesFile.c_str()))
{
AZ_TracePrintf(AssetBundler::AppWindowName, "Copied project dependencies file template %s to the current project.\n",
projectDependenciesFile.c_str());
}
else
{
AZ_Error(AppWindowName, false, "Failed to copy project dependencies file template %s from default project"
" template to the current project.\n", projectDependenciesFileTemplate.c_str());
return {};
}
}
// Turn the absolute path into a cache-relative path
AZStd::string relativeProductPath;
AzFramework::StringFunc::Path::GetFullFileName(projectDependenciesFile.c_str(), relativeProductPath);
AZStd::to_lower(relativeProductPath.begin(), relativeProductPath.end());
return relativeProductPath;
}
AZStd::unordered_map<AZStd::string, AZStd::string> GetGemSeedListFilePathToGemNameMap(const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlags)
{
AZStd::unordered_map<AZStd::string, AZStd::string> filePathToGemNameMap;
for (const AzToolsFramework::AssetUtils::GemInfo& gemInfo : gemInfoList)
{
AZ::IO::Path gemInfoAssetFilePath = gemInfo.m_absoluteFilePath;
gemInfoAssetFilePath /= gemInfo.GetGemAssetFolder();
AZ::IO::Path absoluteGemSeedFilePath = gemInfoAssetFilePath / GemsSeedFileName;
absoluteGemSeedFilePath.ReplaceExtension(AZ::IO::PathView{ AzToolsFramework::AssetSeedManager::GetSeedFileExtension() });
absoluteGemSeedFilePath = absoluteGemSeedFilePath.LexicallyNormal();
AZStd::string gemName = gemInfo.m_gemName + " Gem";
if (AZ::IO::FileIOBase::GetInstance()->Exists(absoluteGemSeedFilePath.c_str()))
{
filePathToGemNameMap[absoluteGemSeedFilePath.Native()] = gemName;
}
Internal::AddPlatformsDirectorySeeds(gemInfoAssetFilePath.Native(), gemName, filePathToGemNameMap, platformFlags);
}
return filePathToGemNameMap;
}
bool IsGemSeedFilePathValid(const char* root, AZStd::string seedAbsoluteFilePath, const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlags)
{
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
AZ_Assert(fileIO, "AZ::IO::FileIOBase must be ready for use.\n");
if (!fileIO->Exists(seedAbsoluteFilePath.c_str()))
{
return false;
}
AZ::IO::Path gemsFolder{ root };
gemsFolder /= GemsDirectoryName;
gemsFolder /= GemsAssetsDirectoryName;
gemsFolder = gemsFolder.LexicallyNormal();
if (!AzFramework::StringFunc::StartsWith(seedAbsoluteFilePath, gemsFolder.Native()))
{
// if we are here it implies that this seed file does not live under the gems directory and
// therefore we do not have to validate it
return true;
}
for (const AzToolsFramework::AssetUtils::GemInfo& gemInfo : gemInfoList)
{
// We want to check the path before going through the effort of creating the default Seed List file map
if (!AzFramework::StringFunc::StartsWith(seedAbsoluteFilePath, gemInfo.m_absoluteFilePath))
{
continue;
}
AZStd::unordered_map<AZStd::string, AZStd::string> seeds = GetGemSeedListFilePathToGemNameMap({gemInfo}, platformFlags);
if (seeds.find(seedAbsoluteFilePath) != seeds.end())
{
return true;
}
// If we have not validated the input path yet, we need to keep looking, or we will return false negatives
// for Gems that have the same prefix in their name
}
return false;
}
AzFramework::PlatformFlags GetEnabledPlatformFlags(const char* root, const char* assetRoot, const char* gameName)
{
QStringList configFiles = AzToolsFramework::AssetUtils::GetConfigFiles(root, assetRoot, gameName, true, true);
QStringList enabaledPlatformList = AzToolsFramework::AssetUtils::GetEnabledPlatforms(configFiles);
AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_NONE;
for (const QString& enabledPlatform : enabaledPlatformList)
{
AzFramework::PlatformFlags platformFlag = AzFramework::PlatformHelper::GetPlatformFlag(enabledPlatform.toUtf8().data());
if (platformFlag != AzFramework::PlatformFlags::Platform_NONE)
{
platformFlags = platformFlags | platformFlag;
}
else
{
AZ_Warning(AssetBundler::AppWindowName, false, "Platform Helper is not aware of the platform (%s).\n ", enabledPlatform.toUtf8().data());
}
}
return platformFlags;
}
void ValidateOutputFilePath(FilePath filePath, const char* format, ...)
{
if (!filePath.IsValid())
{
char message[MaxErrorMessageLength] = {};
va_list args;
va_start(args, format);
azvsnprintf(message, MaxErrorMessageLength, format, args);
va_end(args);
AZ_Error(AssetBundler::AppWindowName, false, message);
}
}
AZ::Outcome<AZStd::string, AZStd::string> GetCurrentProjectName()
{
AZStd::string gameName;
bool result{ false };
auto gameFolderKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/%s",
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, AzFramework::AssetSystem::ProjectName);
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
result = settingsRegistry->Get(gameName, gameFolderKey);
}
if (result)
{
return AZ::Success(gameName);
}
else
{
return AZ::Failure(AZStd::string("Unable to locate current project name in bootstrap.cfg"));
}
}
AZ::Outcome<AZStd::string, AZStd::string> GetProjectFolderPath(const AZStd::string& engineRoot, const AZStd::string& projectName)
{
AZStd::string projectFolderPath;
bool success = AzFramework::StringFunc::Path::ConstructFull(engineRoot.c_str(), projectName.c_str(), projectFolderPath);
if (success && AZ::IO::FileIOBase::GetInstance()->Exists(projectFolderPath.c_str()))
{
return AZ::Success(projectFolderPath);
}
else
{
return AZ::Failure(AZStd::string::format( "Unable to locate the current Project folder: %s", projectName.c_str()));
}
}
AZ::Outcome<AZStd::string, AZStd::string> GetProjectCacheFolderPath(const AZStd::string& engineRoot, const AZStd::string& projectName)
{
AZStd::string projectCacheFolderPath;
bool success = AzFramework::StringFunc::Path::ConstructFull(engineRoot.c_str(), "Cache", projectCacheFolderPath);
if (!success || !AZ::IO::FileIOBase::GetInstance()->Exists(projectCacheFolderPath.c_str()))
{
return AZ::Failure(AZStd::string::format(
"Unable to locate the Cache in the engine directory: %s. Please run the Lumberyard Asset Processor to generate a Cache and build assets.",
engineRoot.c_str()));
}
success = AzFramework::StringFunc::Path::ConstructFull(projectCacheFolderPath.c_str(), projectName.c_str(), projectCacheFolderPath);
if (success && AZ::IO::FileIOBase::GetInstance()->Exists(projectCacheFolderPath.c_str()))
{
return AZ::Success(projectCacheFolderPath);
}
else
{
return AZ::Failure(AZStd::string::format(
"Unable to locate the current Project in the Cache folder: %s. Please run the Lumberyard Asset Processor to generate a Cache and build assets.",
projectName.c_str()));
}
}
AZ::Outcome<void, AZStd::string> GetPlatformNamesFromCacheFolder(const AZStd::string& projectCacheFolder, AZStd::vector<AZStd::string>& platformNames)
{
QDir projectCacheDir(QString(projectCacheFolder.c_str()));
auto tempPlatformList = projectCacheDir.entryList(QDir::Filter::Dirs | QDir::Filter::NoDotAndDotDot);
if (tempPlatformList.empty())
{
return AZ::Failure(AZStd::string("Cache is empty. Please run the Lumberyard Asset Processor to generate a Cache and build assets."));
}
for (const QString& platform : tempPlatformList)
{
platformNames.push_back(AZStd::string(platform.toUtf8().data()));
}
return AZ::Success();
}
AZ::Outcome<AZStd::string, AZStd::string> GetAssetCatalogFilePath(const char* pathToCacheFolder, const char* platformIdentifier, const char* projectName)
{
AZStd::string assetCatalogFilePath;
bool success = AzFramework::StringFunc::Path::ConstructFull(pathToCacheFolder, platformIdentifier, assetCatalogFilePath, true);
if (!success)
{
return AZ::Failure(AZStd::string::format(
"Unable to find platform folder %s in cache found at: %s. Please run the Lumberyard Asset Processor to generate platform-specific cache folders and build assets.",
platformIdentifier,
pathToCacheFolder));
}
// Project name is lower case in the platform-specific cache folder
AZStd::string lowerCaseProjectName = AZStd::string(projectName);
AZStd::to_lower(lowerCaseProjectName.begin(), lowerCaseProjectName.end());
success = AzFramework::StringFunc::Path::ConstructFull(assetCatalogFilePath.c_str(), lowerCaseProjectName.c_str(), assetCatalogFilePath)
&& AzFramework::StringFunc::Path::ConstructFull(assetCatalogFilePath.c_str(), AssetCatalogFilename, assetCatalogFilePath);
if (!success)
{
return AZ::Failure(AZStd::string("Unable to find the asset catalog. Please run the Lumberyard Asset Processor to generate a Cache and build assets."));
}
return AZ::Success(assetCatalogFilePath);
}
AZStd::string GetPlatformSpecificCacheFolderPath(const AZStd::string& projectSpecificCacheFolderAbsolutePath, const AZStd::string& platform, const AZStd::string& projectName)
{
// C:/dev/Cache/ProjectName -> C:/dev/Cache/ProjectName/platform
AZStd::string platformSpecificCacheFolderPath;
AzFramework::StringFunc::Path::ConstructFull(projectSpecificCacheFolderAbsolutePath.c_str(), platform.c_str(), platformSpecificCacheFolderPath, true);
// C:/dev/Cache/ProjectName/platform -> C:/dev/Cache/ProjectName/platform/projectname
AZStd::string lowerCaseProjectName = AZStd::string(projectName);
AZStd::to_lower(lowerCaseProjectName.begin(), lowerCaseProjectName.end());
AzFramework::StringFunc::Path::ConstructFull(platformSpecificCacheFolderPath.c_str(), lowerCaseProjectName.c_str(), platformSpecificCacheFolderPath, true);
return platformSpecificCacheFolderPath;
}
AZStd::string GenerateKeyFromAbsolutePath(const AZStd::string& absoluteFilePath)
{
AZStd::string key(absoluteFilePath);
AzFramework::StringFunc::Path::Normalize(key);
AzFramework::StringFunc::Path::StripDrive(key);
return key;
}
void ConvertToRelativePath(const AZStd::string& parentFolderPath, AZStd::string& absoluteFilePath)
{
// Qt and AZ return different Drive Letter formats, so strip them away before doing a comparison
AZStd::string parentFolderPathWithoutDrive(parentFolderPath);
AzFramework::StringFunc::Path::StripDrive(parentFolderPathWithoutDrive);
AzFramework::StringFunc::Path::Normalize(parentFolderPathWithoutDrive);
AzFramework::StringFunc::Path::StripDrive(absoluteFilePath);
AzFramework::StringFunc::Path::Normalize(absoluteFilePath);
AzFramework::StringFunc::Replace(absoluteFilePath, parentFolderPathWithoutDrive.c_str(), "");
}
AZ::Outcome<void, AZStd::string> MakePath(const AZStd::string& path)
{
// Create the folder if it does not already exist
if (!AZ::IO::FileIOBase::GetInstance()->Exists(path.c_str()))
{
auto result = AZ::IO::FileIOBase::GetInstance()->CreatePath(path.c_str());
if (!result)
{
return AZ::Failure(AZStd::string::format("Path creation failed. Input path: %s \n", path.c_str()));
}
}
return AZ::Success();
}
WarningAbsorber::WarningAbsorber()
{
AZ::Debug::TraceMessageBus::Handler::BusConnect();
}
WarningAbsorber::~WarningAbsorber()
{
AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
}
bool WarningAbsorber::OnWarning(const char* window, const char* message)
{
AZ_UNUSED(window);
AZ_UNUSED(message);
return true; // do not forward
}
bool WarningAbsorber::OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message)
{
AZ_UNUSED(window);
AZ_UNUSED(fileName);
AZ_UNUSED(line);
AZ_UNUSED(func);
AZ_UNUSED(message);
return true; // do not forward
}
FilePath::FilePath(const AZStd::string& filePath, AZStd::string platformIdentifier, bool checkFileCase, bool ignoreFileCase)
{
AZStd::string platform = platformIdentifier;
if (!platform.empty())
{
AZStd::string filePlatform = AzToolsFramework::GetPlatformIdentifier(filePath);
if (!filePlatform.empty())
{
// input file path already has a platform, no need to append a platform id
platform = AZStd::string();
if (!AzFramework::StringFunc::Equal(filePlatform.c_str(), platformIdentifier.c_str(), true))
{
// Platform identifier does not match the current platform
return;
}
}
}
if (!filePath.empty())
{
m_validPath = true;
m_originalPath = m_absolutePath = filePath;
AzFramework::StringFunc::Path::Normalize(m_originalPath);
ComputeAbsolutePath(m_absolutePath, platform, checkFileCase, ignoreFileCase);
}
}
FilePath::FilePath(const AZStd::string& filePath, bool checkFileCase, bool ignoreFileCase)
:FilePath(filePath, AZStd::string(), checkFileCase, ignoreFileCase)
{
}
const AZStd::string& FilePath::AbsolutePath() const
{
return m_absolutePath;
}
const AZStd::string& FilePath::OriginalPath() const
{
return m_originalPath;
}
bool FilePath::IsValid() const
{
return m_validPath;
}
AZStd::string FilePath::ErrorString() const
{
return m_errorString;
}
void FilePath::ComputeAbsolutePath(AZStd::string& filePath, const AZStd::string& platformIdentifier, bool checkFileCase, bool ignoreFileCase)
{
if (AzToolsFramework::AssetFileInfoListComparison::IsTokenFile(filePath))
{
return;
}
if (!platformIdentifier.empty())
{
AssetBundler::AddPlatformIdentifier(filePath, platformIdentifier);
}
const char* appRoot = nullptr;
AzFramework::ApplicationRequests::Bus::BroadcastResult(appRoot, &AzFramework::ApplicationRequests::GetAppRoot);
#if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
AZStd::string driveString;
AzFramework::StringFunc::Path::GetDrive(appRoot, driveString);
if (AzFramework::StringFunc::FirstCharacter(filePath.c_str()) == AZ_CORRECT_FILESYSTEM_SEPARATOR)
{
AzFramework::StringFunc::Path::ConstructFull(driveString.c_str(), filePath.c_str(), filePath, true);
}
#endif
if (!AzFramework::StringFunc::Path::IsRelative(filePath.c_str()))
{
// it is already an absolute path
AzFramework::StringFunc::Path::Normalize(filePath);
}
else
{
AzFramework::StringFunc::Path::ConstructFull(appRoot, m_absolutePath.c_str(), m_absolutePath, true);
}
if (checkFileCase)
{
QDir rootDir(appRoot);
QString relFilePath = rootDir.relativeFilePath(m_absolutePath.c_str());
if (AzToolsFramework::AssetUtils::UpdateFilePathToCorrectCase(QString(appRoot), relFilePath))
{
if (ignoreFileCase)
{
AzFramework::StringFunc::Path::ConstructFull(appRoot, relFilePath.toUtf8().data(), m_absolutePath, true);
}
else
{
AZStd::string absfilePath(rootDir.filePath(relFilePath).toUtf8().data());
AzFramework::StringFunc::Path::Normalize(absfilePath);
if (!AZ::StringFunc::Equal(absfilePath.c_str(), m_absolutePath.c_str(), true))
{
m_errorString = AZStd::string::format("File case mismatch, file ( %s ) does not exist on disk, did you mean file ( %s ). \
Please run the command again with the correct file path or use ( --%s ) arg if you want to allow case insensitive file match.\n",
m_absolutePath.c_str(), rootDir.filePath(relFilePath.toUtf8().data()).toUtf8().data(), IgnoreFileCaseFlag);
m_validPath = false;
}
}
}
}
}
ScopedTraceHandler::ScopedTraceHandler()
{
BusConnect();
}
ScopedTraceHandler::~ScopedTraceHandler()
{
BusDisconnect();
}
bool ScopedTraceHandler::OnError(const char* window, const char* message)
{
AZ_UNUSED(window);
if (m_reportingError)
{
// if we are reporting error than we dont want to store errors again.
return false;
}
m_errors.emplace_back(message);
return true;
}
int ScopedTraceHandler::GetErrorCount() const
{
return static_cast<int>(m_errors.size());
}
void ScopedTraceHandler::ReportErrors()
{
m_reportingError = true;
for (const AZStd::string& error : m_errors)
{
AZ_Error(AssetBundler::AppWindowName, false, error.c_str());
}
ClearErrors();
m_reportingError = false;
}
void ScopedTraceHandler::ClearErrors()
{
m_errors.clear();
m_errors.swap(AZStd::vector<AZStd::string>());
}
AZ::Outcome<AzToolsFramework::AssetFileInfoListComparison::ComparisonType, AZStd::string> ParseComparisonType(const AZStd::string& comparisonType)
{
using namespace AzToolsFramework;
const size_t numTypes = AZ_ARRAY_SIZE(AssetFileInfoListComparison::ComparisonTypeNames);
int comparisonTypeIndex = 0;
if (AzFramework::StringFunc::LooksLikeInt(comparisonType.c_str(), &comparisonTypeIndex))
{
// User passed in a number
if (comparisonTypeIndex < numTypes)
{
return AZ::Success(static_cast<AssetFileInfoListComparison::ComparisonType>(comparisonTypeIndex));
}
}
else
{
// User passed in the name of a ComparisonType
for (size_t i = 0; i < numTypes; ++i)
{
if (AzFramework::StringFunc::Equal(comparisonType.c_str(), AssetFileInfoListComparison::ComparisonTypeNames[i]))
{
return AZ::Success(static_cast<AssetFileInfoListComparison::ComparisonType>(i));
}
}
}
// Failure case
AZStd::string failureMessage = AZStd::string::format("Invalid Comparison Type ( %s ). Valid types are: ", comparisonType.c_str());
for (size_t i = 0; i < numTypes - 1; ++i)
{
failureMessage.append(AZStd::string::format("%s, ", AssetFileInfoListComparison::ComparisonTypeNames[i]));
}
failureMessage.append(AZStd::string::format("and %s.", AssetFileInfoListComparison::ComparisonTypeNames[numTypes - 1]));
return AZ::Failure(failureMessage);
}
AZ::Outcome<AzToolsFramework::AssetFileInfoListComparison::FilePatternType, AZStd::string> ParseFilePatternType(const AZStd::string& filePatternType)
{
using namespace AzToolsFramework;
const size_t numTypes = AZ_ARRAY_SIZE(AssetFileInfoListComparison::FilePatternTypeNames);
int filePatternTypeIndex = 0;
if (AzFramework::StringFunc::LooksLikeInt(filePatternType.c_str(), &filePatternTypeIndex))
{
// User passed in a number
if (filePatternTypeIndex < numTypes)
{
return AZ::Success(static_cast<AssetFileInfoListComparison::FilePatternType>(filePatternTypeIndex));
}
}
else
{
// User passed in the name of a FilePatternType
for (size_t i = 0; i < numTypes; ++i)
{
if (AzFramework::StringFunc::Equal(filePatternType.c_str(), AssetFileInfoListComparison::FilePatternTypeNames[i]))
{
return AZ::Success(static_cast<AssetFileInfoListComparison::FilePatternType>(i));
}
}
}
// Failure case
AZStd::string failureMessage = AZStd::string::format("Invalid File Pattern Type ( %s ). Valid types are: ", filePatternType.c_str());
for (size_t i = 0; i < numTypes - 1; ++i)
{
failureMessage.append(AZStd::string::format("%s, ", AssetFileInfoListComparison::FilePatternTypeNames[i]));
}
failureMessage.append(AZStd::string::format("and %s.", AssetFileInfoListComparison::FilePatternTypeNames[numTypes - 1]));
return AZ::Failure(failureMessage);
}
bool LooksLikePath(const AZStd::string& inputString)
{
for (auto thisChar : inputString)
{
if (thisChar == '.' || thisChar == AZ_CORRECT_FILESYSTEM_SEPARATOR || thisChar == AZ_WRONG_FILESYSTEM_SEPARATOR)
{
return true;
}
}
return false;
}
bool LooksLikeWildcardPattern(const AZStd::string& inputPattern)
{
for (auto thisChar : inputPattern)
{
if (thisChar == '*' || thisChar == '?')
{
return true;
}
}
return false;
}
}
@@ -0,0 +1,309 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/std/string/string.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/IO/SystemFile.h> //AZ_MAX_PATH_LEN
#include <AzCore/Outcome/Outcome.h>
#include <AzFramework/Platform/PlatformDefaults.h>
#include <AzToolsFramework/Asset/AssetBundler.h>
#include <AzToolsFramework/Asset/AssetUtils.h>
namespace AssetBundler
{
enum CommandType
{
Invalid,
Seeds,
AssetLists,
ComparisonRules,
Compare,
BundleSettings,
Bundles,
BundleSeed
};
////////////////////////////////////////////////////////////////////////////////////////////
// General
extern const char* AppWindowName;
extern const char* AppWindowNameVerbose;
extern const char* HelpFlag;
extern const char* HelpFlagAlias;
extern const char* VerboseFlag;
extern const char* SaveFlag;
extern const char* PlatformArg;
extern const char* PrintFlag;
extern const char* AssetCatalogFileArg;
extern const char* AllowOverwritesFlag;
extern const char* IgnoreFileCaseFlag;
extern const char* ProjectArg;
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
// Seeds
extern const char* SeedsCommand;
extern const char* SeedListFileArg;
extern const char* AddSeedArg;
extern const char* RemoveSeedArg;
extern const char* AddPlatformToAllSeedsFlag;
extern const char* RemovePlatformFromAllSeedsFlag;
extern const char* UpdateSeedPathArg;
extern const char* RemoveSeedPathArg;
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
// Asset Lists
extern const char* AssetListsCommand;
extern const char* AssetListFileArg;
extern const char* AddDefaultSeedListFilesFlag;
extern const char* DryRunFlag;
extern const char* GenerateDebugFileFlag;
extern const char* SkipArg;
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
// Comparison Rules
extern const char* ComparisonRulesCommand;
extern const char* ComparisonRulesFileArg;
extern const char* ComparisonTypeArg;
extern const char* ComparisonFilePatternArg;
extern const char* ComparisonFilePatternTypeArg;
extern const char* ComparisonTokenNameArg;
extern const char* ComparisonFirstInputArg;
extern const char* ComparisonSecondInputArg;
extern const char* AddComparisonStepArg;
extern const char* RemoveComparisonStepArg;
extern const char* MoveComparisonStepArg;
extern const char* EditComparisonStepArg;
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
// Compare
extern const char* CompareCommand;
extern const char* CompareFirstFileArg;
extern const char* CompareSecondFileArg;
extern const char* CompareOutputFileArg;
extern const char* ComparePrintArg;
extern const char* IntersectionCountArg;
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
// Bundle Settings
extern const char* BundleSettingsCommand;
extern const char* BundleSettingsFileArg;
extern const char* OutputBundlePathArg;
extern const char* BundleVersionArg;
extern const char* MaxBundleSizeArg;
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
// Bundles
extern const char* BundlesCommand;
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
// Bundle Seed
extern const char* BundleSeedCommand;
////////////////////////////////////////////////////////////////////////////////////////////
extern const char* AssetCatalogFilename;
extern char g_cachedEngineRoot[AZ_MAX_PATH_LEN];
static const size_t MaxErrorMessageLength = 4096;
//! This struct stores gem related information
struct GemInfo
{
AZ_CLASS_ALLOCATOR(GemInfo, AZ::SystemAllocator, 0);
GemInfo(AZStd::string name, AZStd::string relativeFilePath, AZStd::string absoluteFilePath);
GemInfo() = default;
AZStd::string m_gemName;
AZStd::string m_relativeFilePath;
AZStd::string m_absoluteFilePath;
};
// The Warning Absorber is used to absorb warnings
// One case that this is being used is during loading of the asset catalog.
// During loading the asset catalog tries to communicate to the AP which is not required for this application.
class WarningAbsorber
: public AZ::Debug::TraceMessageBus::Handler
{
public:
WarningAbsorber();
~WarningAbsorber();
bool OnWarning(const char* window, const char* message) override;
bool OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message) override;
};
// computes the asset alias and game name either through the asset catalog file provided by the user or using the platform and game folder
AZ::Outcome<void, AZStd::string> ComputeAssetAliasAndGameName(const AZStd::string& platformIdentifier, const AZStd::string& assetCatalogFile, AZStd::string& assetAlias, AZStd::string& gameName);
// Computes the engine root and cache it locally
bool ComputeEngineRoot();
// Retrurns the engine root
const char* GetEngineRoot();
/**
* Determines the name of the currently enabled game project
* @return Current Project name on success, error message on failure
*/
AZ::Outcome<AZStd::string, AZStd::string> GetCurrentProjectName();
/**
* Constructs an absolute path to the project folder found at: dev/ProjectName
* @param engineRoot The absolute path of the dev/ folder
* @param projectName A project present in the dev/ folder
* @return Absolute path of the Project Folder on success, error message on failure
*/
AZ::Outcome<AZStd::string, AZStd::string> GetProjectFolderPath(const AZStd::string& engineRoot, const AZStd::string& projectName);
/**
* Constructs an absolute path to the project-specific cache folder found at: dev/Cache/ProjectName
* @param engineRoot The absolute path of the dev/ folder
* @param projectName A project present in the dev/ folder
* @return Absolute path of the project-specific cache folder on success, error message on failure
*/
AZ::Outcome<AZStd::string, AZStd::string> GetProjectCacheFolderPath(const AZStd::string& engineRoot, const AZStd::string& projectName);
/**
* Calculates the list of enabled platforms for the input project by reading the folder names inside the project-specific cache folder.
* If the Asset Processor has not been run yet, or has not been run since the enabled platform list inside AssetProcessorPlatformConfig.ini
* was changed, the output of this function will be incorrect.
*
* @param projectCacheFolder The directory of a project-specific cache folder: dev/Cache/ProjectName
* @param platformNames [out] The list of platforms enabled in the project
* @return void on success, error message on failure
*/
AZ::Outcome<void, AZStd::string> GetPlatformNamesFromCacheFolder(const AZStd::string& projectCacheFolder, AZStd::vector<AZStd::string>& platformNames);
/**
* Computes the absolute path to the Asset Catalog file for a specified project and platform.
* With platform set as "pc" and project as "ProjectName", the path will resemble: C:/dev/Cache/ProjectName/pc/projectname/assetcatalog.xml
*
* @param pathToCacheFolder The absolute path to the Cache folder. ex: C:/dev/Cache
* @param platformIdentifier The platform identifier of the desired Asset Catalog. Valid inputs can be found by reading the folder names
* found inside dev/Cache/ProjectName
* @param projectName The name of the project you want to search
* @return Absolute Path to the Asset Catalog file on success, error message on failure
*/
AZ::Outcome<AZStd::string, AZStd::string> GetAssetCatalogFilePath(const char* pathToCacheFolder, const char* platformIdentifier, const char* projectName);
/**
* Computes the absolute path to the platform-specific Cache folder where product assets are stored.
* With platform set as "pc" and project as "ProjectName", the path will resemble: C:/dev/Cache/ProjectName/pc/projectname/
*
* @param projectSpecificCacheFolderAbsolutePath The absolute path to the Cache folder. Example: C:/dev/Cache/ProjectName
* @param platform the platform of the desired cache location
* @param projectName The name of the current project
* @return Absolute path to the platform-specific Cache folder where product assets are stored
*/
AZStd::string GetPlatformSpecificCacheFolderPath(const AZStd::string& projectSpecificCacheFolderAbsolutePath, const AZStd::string& platform, const AZStd::string& projectName);
AZStd::string GenerateKeyFromAbsolutePath(const AZStd::string& absoluteFilePath);
void ConvertToRelativePath(const AZStd::string& parentFolderPath, AZStd::string& absoluteFilePath);
AZ::Outcome<void, AZStd::string> MakePath(const AZStd::string& path);
//! Add the specified platform identifier to the filename
void AddPlatformIdentifier(AZStd::string& filePath, const AZStd::string& platformIdentifier);
//! Returns the list of platforms that exist on-disk for the input file path.
AzFramework::PlatformFlags GetPlatformsOnDiskForPlatformSpecificFile(const AZStd::string& platformIndependentAbsolutePath);
//! Returns a map of <absolute file path, source folder display name> of all default Seed List files for the current game project.
AZStd::unordered_map<AZStd::string, AZStd::string> GetDefaultSeedListFiles(const char* root, const char* projectName, const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlags);
//! Returns a vector of relative paths to Assets that should be included as default Seeds, but are not already in a Seed List file.
AZStd::vector<AZStd::string> GetDefaultSeeds(const char* root, const char* projectName);
//! Returns the absolute path of {ProjectName}_Dependencies.xml
AZStd::string GetProjectDependenciesFile(const char* root, const char* projectName);
//! Returns the absolute path of the project dependencies file in the default project template
AZStd::string GetProjectDependenciesFileTemplate(const char* root);
//! Creates the ProjectName_Dependencies.xml file if it does not exist, and adds returns the relative path to the asset in the Cache.
AZStd::string GetProjectDependenciesAssetPath(const char* root, const char* projectName);
//! Returns the map from gem seed list file path to gem name
AZStd::unordered_map<AZStd::string, AZStd::string> GetGemSeedListFilePathToGemNameMap(const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlags);
//! Given an absolute gem seed file path determines whether the file is valid for the current game project.
//! This method is for validating gem seed list files only.
bool IsGemSeedFilePathValid(const char* root, AZStd::string seedAbsoluteFilePath, const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlags);
//! Returns platformFlags of all enabled platforms by parsing all the asset processor config files.
//! Please note that the game project could be in a different location to the engine therefore we need the assetRoot param.
AzFramework::PlatformFlags GetEnabledPlatformFlags(const char* root, const char* assetRoot, const char* gameName);
//! Filepath is a helper class that is used to find the absolute path of a file
//! if the inputted file path is an absolute path than it does nothing
//! if the inputted file path is a relative path than based on whether the user
//! also inputted a root directory it computes the absolute path,
//! if root directory is provided it uses that otherwise it uses the engine root as the default root folder.
class FilePath
{
public:
AZ_CLASS_ALLOCATOR(FilePath, AZ::SystemAllocator, 0);
explicit FilePath(const AZStd::string& filePath, AZStd::string platformIdentifier = AZStd::string(), bool checkFileCase = false, bool ignoreFileCase = false);
explicit FilePath(const AZStd::string& filePath, bool checkFileCase, bool ignoreFileCase);
FilePath() = default;
const AZStd::string& AbsolutePath() const;
const AZStd::string& OriginalPath() const;
AZStd::string ErrorString() const;
bool IsValid() const;
private:
void ComputeAbsolutePath(AZStd::string& filePath, const AZStd::string& platformIdentifier, bool checkFileCase, bool ignoreFileCase);
AZStd::string m_absolutePath;
AZStd::string m_originalPath;
AZStd::string m_errorString;
bool m_validPath = false;
};
void ValidateOutputFilePath(FilePath filePath, const char* format, ...);
//! ScopedTraceHandler can be used to handle and report errors
class ScopedTraceHandler : public AZ::Debug::TraceMessageBus::Handler
{
public:
ScopedTraceHandler();
~ScopedTraceHandler();
//! TraceMessageBus Interface
bool OnError(const char* /*window*/, const char* /*message*/) override;
//////////////////////////////////////////////////////////
//! Returns the error count
int GetErrorCount() const;
//! Report all the errors
void ReportErrors();
//! Clear all the errors
void ClearErrors();
private:
AZStd::vector<AZStd::string> m_errors;
bool m_reportingError = false;
};
AZ::Outcome<AzToolsFramework::AssetFileInfoListComparison::ComparisonType, AZStd::string> ParseComparisonType(const AZStd::string& comparisonType);
AZ::Outcome<AzToolsFramework::AssetFileInfoListComparison::FilePatternType, AZStd::string> ParseFilePatternType(const AZStd::string& filePatternType);
bool LooksLikePath(const AZStd::string& inputString);
bool LooksLikeWildcardPattern(const AZStd::string& inputPattern);
}
@@ -0,0 +1,10 @@
[Platforms]
;pc=enabled
es3=enabled
;ios=enabled
;osx_gl=enabled
;xenia=enabled
;provo=enabled
;server=enabled
@@ -0,0 +1,8 @@
[Platforms]
;pc=enabled
;es3=enabled
ios=enabled
;osx_gl=enabled
;xenia=enabled
;provo=enabled
;server=enabled
@@ -0,0 +1,4 @@
<ObjectStream version="3">
<Class name="AZStd::vector" type="{82FC5264-88D0-57CD-9307-FC52E4DAD550}"/>
</ObjectStream>
@@ -0,0 +1,25 @@
{
"GemListFormatVersion": 2,
"Gems": [
{
"Path": "Gems/GemA",
"Uuid": "044a63ea67d04479aa5daf62ded9d9cb",
"Version": "0.1.0",
"_comment": "GemA"
},
{
"Path": "Gems/GemB",
"Uuid": "07375b61b1a2424bb03088bbdf28b2c9",
"Version": "0.1.0",
"_comment": "GemB"
},
{
"Path": "Gems/GemC",
"Uuid": "0945e21b7ae848ac80b4ec1f34c459cd",
"Version": "0.1.0",
"_comment": "GemC"
}
]
}
@@ -0,0 +1,14 @@
{
"project_name": "DummyProject",
"product_name": "DummyProject",
"executable_name": "DummyProjectLauncher",
"modules" : [],
"project_id": "{91FB81A1-072C-4A80-8FCC-7E2C4C767B4D}",
"android_settings" : {
"package_name" : "com.lumberyard.yourgame",
"version_number" : 1,
"version_name" : "1.0.0.0",
"orientation" : "landscape"
}
}
@@ -0,0 +1,5 @@
<ObjectStream version="3">
<Class name="AZStd::vector" type="{82FC5264-88D0-57CD-9307-FC52E4DAD550}">
</Class>
</ObjectStream>
@@ -0,0 +1,10 @@
[Platforms]
;pc=enabled
;es3=enabled
;ios=enabled
;osx_gl=enabled
;xenia=enabled
provo=enabled
;server=enabled
@@ -0,0 +1,5 @@
<ObjectStream version="3">
<Class name="AZStd::vector" type="{82FC5264-88D0-57CD-9307-FC52E4DAD550}">
</Class>
</ObjectStream>
@@ -0,0 +1,11 @@
{
"GemFormatVersion": 4,
"Uuid": "044A63EA67D04479AA5DAF62DED9D9CB",
"Name": "GemA",
"DisplayName": "GemA",
"Version": "0.1.0",
"Summary": "Only for unit test purposes.",
"Tags": ["Foo"],
"IconPath": "preview.png",
"EditorModule": true
}
@@ -0,0 +1,5 @@
<ObjectStream version="3">
<Class name="AZStd::vector" type="{82FC5264-88D0-57CD-9307-FC52E4DAD550}">
</Class>
</ObjectStream>
@@ -0,0 +1,5 @@
<ObjectStream version="3">
<Class name="AZStd::vector" type="{82FC5264-88D0-57CD-9307-FC52E4DAD550}">
</Class>
</ObjectStream>
@@ -0,0 +1,5 @@
<ObjectStream version="3">
<Class name="AZStd::vector" type="{82FC5264-88D0-57CD-9307-FC52E4DAD550}">
</Class>
</ObjectStream>
@@ -0,0 +1,11 @@
{
"GemFormatVersion": 4,
"Uuid": "07375B61B1A2424BB03088BBDF28B2C9",
"Name": "GemB",
"DisplayName": "GemB",
"Version": "0.1.0",
"Summary": "Only for unit test purposes.",
"Tags": ["Foo"],
"IconPath": "preview.png",
"EditorModule": true
}
@@ -0,0 +1,11 @@
{
"GemFormatVersion": 4,
"Uuid": "0945E21B7AE848AC80B4EC1F34C459CD",
"Name": "GemC",
"DisplayName": "GemC",
"Version": "0.1.0",
"Summary": "Only for unit test purposes.",
"Tags": ["Foo"],
"IconPath": "preview.png",
"EditorModule": true
}
@@ -0,0 +1,147 @@
/*
* 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 <AzFramework/API/ApplicationAPI.h>
#include <source/utils/utils.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <Utils/Utils.h>
#include <AzFramework/IO/LocalFileIO.h>
namespace AssetBundler
{
class MockUtilsTest
: public UnitTest::ScopedAllocatorSetupFixture
, public AzFramework::ApplicationRequests::Bus::Handler
{
public:
void SetUp() override
{
ScopedAllocatorSetupFixture::SetUp();
AzFramework::ApplicationRequests::Bus::Handler::BusConnect();
m_localFileIO = aznew AZ::IO::LocalFileIO();
m_priorFileIO = AZ::IO::FileIOBase::GetInstance();
// we need to set it to nullptr first because otherwise the
// underneath code assumes that we might be leaking the previous instance
AZ::IO::FileIOBase::SetInstance(nullptr);
AZ::IO::FileIOBase::SetInstance(m_localFileIO);
m_tempDir = new UnitTest::ScopedTemporaryDirectory();
}
void TearDown() override
{
delete m_tempDir;
AZ::IO::FileIOBase::SetInstance(nullptr);
delete m_localFileIO;
AZ::IO::FileIOBase::SetInstance(m_priorFileIO);
AzFramework::ApplicationRequests::Bus::Handler::BusDisconnect();
ScopedAllocatorSetupFixture::TearDown();
}
// AzFramework::ApplicationRequests::Bus::Handler interface
void NormalizePath(AZStd::string& /*path*/) override {}
void NormalizePathKeepCase(AZStd::string& /*path*/) override {}
void CalculateBranchTokenForAppRoot(AZStd::string& /*token*/) const override {}
const char* GetAppRoot() const override
{
return m_tempDir->GetDirectory();
}
AZ::IO::FileIOBase* m_priorFileIO = nullptr;
AZ::IO::FileIOBase* m_localFileIO = nullptr;
UnitTest::ScopedTemporaryDirectory* m_tempDir = nullptr;
};
TEST_F(MockUtilsTest, DISABLED_TestFilePath_StartsWithAFileSeparator_Valid)
{
AZStd::string relFilePath = "Foo/foo.xml";
AzFramework::StringFunc::Prepend(relFilePath, AZ_CORRECT_FILESYSTEM_SEPARATOR);
AZStd::string absoluteFilePath;
#if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
AZStd::string driveString;
AzFramework::StringFunc::Path::GetDrive(GetAppRoot(), driveString);
AzFramework::StringFunc::Path::ConstructFull(driveString.c_str(), relFilePath.c_str(), absoluteFilePath, true);
#else
absoluteFilePath = relFilePath;
#endif
FilePath filePath(relFilePath);
EXPECT_STREQ(filePath.AbsolutePath().c_str(), absoluteFilePath.c_str());
}
TEST_F(MockUtilsTest, TestFilePath_RelativePath_Valid)
{
AZStd::string relFilePath = "Foo\\foo.xml";
AZStd::string absoluteFilePath;
AzFramework::StringFunc::Path::ConstructFull(GetAppRoot(), relFilePath.c_str(), absoluteFilePath, true);
FilePath filePath(relFilePath);
EXPECT_EQ(filePath.AbsolutePath(), absoluteFilePath);
}
TEST_F(MockUtilsTest, TestFilePath_CasingMismatch_Error_valid)
{
AZStd::string relFilePath = "Foo\\Foo.xml";
AZStd::string wrongCaseRelFilePath = "Foo\\foo.xml";
AZStd::string correctAbsoluteFilePath;
AZStd::string wrongCaseAbsoluteFilePath;
AzFramework::StringFunc::Path::ConstructFull(GetAppRoot(), relFilePath.c_str(), correctAbsoluteFilePath, true);
AzFramework::StringFunc::Path::ConstructFull(GetAppRoot(), wrongCaseRelFilePath.c_str(), wrongCaseAbsoluteFilePath, true);
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
AZ::IO::FileIOBase::GetInstance()->Open(correctAbsoluteFilePath.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath, fileHandle);
FilePath filePath(wrongCaseAbsoluteFilePath, true, false);
EXPECT_FALSE(filePath.IsValid());
EXPECT_TRUE(filePath.ErrorString().find("File case mismatch") != AZStd::string::npos);
}
TEST_F(MockUtilsTest, TestFilePath_NoFileExists_NoError_valid)
{
AZStd::string relFilePath = "Foo\\Foo.xml";
AZStd::string absoluteFilePath;
AzFramework::StringFunc::Path::ConstructFull(GetAppRoot(), relFilePath.c_str(), absoluteFilePath, true);
FilePath filePath(absoluteFilePath, true, false);
EXPECT_TRUE(filePath.IsValid());
EXPECT_TRUE(filePath.ErrorString().empty());
}
TEST_F(MockUtilsTest, TestFilePath_CasingMismatch_Ignore_Filecase_valid)
{
AZStd::string relFilePath = "Foo\\Foo.xml";
AZStd::string wrongCaseRelFilePath = "Foo\\foo.xml";
AZStd::string correctAbsoluteFilePath;
AZStd::string wrongCaseAbsoluteFilePath;
AzFramework::StringFunc::Path::ConstructFull(GetAppRoot(), relFilePath.c_str(), correctAbsoluteFilePath, true);
AzFramework::StringFunc::Path::ConstructFull(GetAppRoot(), wrongCaseRelFilePath.c_str(), wrongCaseAbsoluteFilePath, true);
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
AZ::IO::FileIOBase::GetInstance()->Open(correctAbsoluteFilePath.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath, fileHandle);
FilePath filePath(wrongCaseAbsoluteFilePath, true, true);
EXPECT_TRUE(filePath.IsValid());
EXPECT_STREQ(filePath.AbsolutePath().c_str(), correctAbsoluteFilePath.c_str());
}
TEST_F(MockUtilsTest, LooksLikeWildcardPattern_IsWildcardPattern_ExpectTrue)
{
EXPECT_TRUE(LooksLikeWildcardPattern("*"));
EXPECT_TRUE(LooksLikeWildcardPattern("?"));
EXPECT_TRUE(LooksLikeWildcardPattern("*/*"));
EXPECT_TRUE(LooksLikeWildcardPattern("*/test?/*.xml"));
}
TEST_F(MockUtilsTest, LooksLikeWildcardPattern_IsNotWildcardPattern_ExpectFalse)
{
EXPECT_FALSE(LooksLikeWildcardPattern(""));
EXPECT_FALSE(LooksLikeWildcardPattern("test"));
EXPECT_FALSE(LooksLikeWildcardPattern("test/path.xml"));
}
}
@@ -0,0 +1,240 @@
/*
* 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 <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AzToolsFramework/Asset/AssetBundler.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <source/utils/utils.h>
#include <source/utils/applicationManager.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <tests/main.h>
namespace AssetBundler
{
const char DummyProjectName[] = "DummyProject";
class MockApplicationManagerTest
: public AssetBundler::ApplicationManager
{
public:
friend class GTEST_TEST_CLASS_NAME_(ApplicationManagerTest, ValidatePlatformFlags_ReadConfigFiles_OK);
explicit MockApplicationManagerTest(int* argc, char*** argv)
: ApplicationManager(argc, argv)
{
}
};
class BasicApplicationManagerTest
: public UnitTest::ScopedAllocatorSetupFixture
{
};
class ApplicationManagerTest
: public UnitTest::ScopedAllocatorSetupFixture
{
public:
void SetUp() override
{
UnitTest::ScopedAllocatorSetupFixture::SetUp();
m_data = AZStd::make_unique<StaticData>();
m_data->m_applicationManager.reset(aznew MockApplicationManagerTest(0, 0));
m_data->m_applicationManager->Start(AzFramework::Application::Descriptor());
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
const char* engineRoot = nullptr;
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot);
ASSERT_TRUE(engineRoot) << "Unable to locate engine root.\n";
AzFramework::StringFunc::Path::Join(engineRoot, RelativeTestFolder, m_data->m_testEngineRoot);
m_data->m_localFileIO = aznew AZ::IO::LocalFileIO();
m_data->m_priorFileIO = AZ::IO::FileIOBase::GetInstance();
// we need to set it to nullptr first because otherwise the
// underneath code assumes that we might be leaking the previous instance
AZ::IO::FileIOBase::SetInstance(nullptr);
AZ::IO::FileIOBase::SetInstance(m_data->m_localFileIO);
}
void TearDown() override
{
AZ::IO::FileIOBase::SetInstance(nullptr);
delete m_data->m_localFileIO;
AZ::IO::FileIOBase::SetInstance(m_data->m_priorFileIO);
m_data->m_applicationManager->Stop();
m_data->m_applicationManager.reset();
m_data.reset();
UnitTest::ScopedAllocatorSetupFixture::TearDown();
}
struct StaticData
{
AZStd::unique_ptr<MockApplicationManagerTest> m_applicationManager = {};
AZ::IO::FileIOBase* m_priorFileIO = nullptr;
AZ::IO::FileIOBase* m_localFileIO = nullptr;
AZStd::string m_testEngineRoot;
};
AZStd::unique_ptr<StaticData> m_data;
};
TEST_F(ApplicationManagerTest, ValidatePlatformFlags_ReadConfigFiles_OK)
{
AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
ASSERT_NE(nullptr, settingsRegistry);
AZStd::unordered_set<AZStd::string> gemsNameMap{ "GemA", "GemB", "GemC" };
for (AZStd::string& gemName : gemsNameMap)
{
auto gemSourcePathKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/Gems/%s/SourcePaths/0",
AZ::SettingsRegistryMergeUtils::OrganizationRootKey, gemName.c_str());
auto gemSourcePath = AZ::IO::Path(m_data->m_testEngineRoot) / "Gems" / gemName;
settingsRegistry->Set(gemSourcePathKey, gemSourcePath.Native());
}
AzToolsFramework::AssetUtils::GetGemsInfo(m_data->m_testEngineRoot.c_str(), m_data->m_testEngineRoot.c_str(), DummyProjectName, m_data->m_applicationManager->m_gemInfoList);
EXPECT_GE(m_data->m_applicationManager->m_gemInfoList.size(), 3);
for (const AzToolsFramework::AssetUtils::GemInfo& gemInfo : m_data->m_applicationManager->m_gemInfoList)
{
gemsNameMap.erase(gemInfo.m_gemName);
}
EXPECT_EQ(0, gemsNameMap.size());
AzFramework::PlatformFlags platformFlags = GetEnabledPlatformFlags(m_data->m_testEngineRoot.c_str(), m_data->m_testEngineRoot.c_str(), DummyProjectName);
AzFramework::PlatformFlags hostPlatformFlag = AzFramework::PlatformHelper::GetPlatformFlag(AzToolsFramework::AssetSystem::GetHostAssetPlatform());
AzFramework::PlatformFlags expectedFlags = AzFramework::PlatformFlags::Platform_ES3 | AzFramework::PlatformFlags::Platform_IOS | AzFramework::PlatformFlags::Platform_PROVO | hostPlatformFlag;
ASSERT_EQ(platformFlags, expectedFlags);
}
TEST_F(BasicApplicationManagerTest, ComputeComparisonTypeFromString_InvalidString_Fails)
{
auto invalidResult = AssetBundler::ParseComparisonType("notacomparisontype");
EXPECT_EQ(invalidResult.IsSuccess(), false);
}
TEST_F(BasicApplicationManagerTest, ComputeComparisonTypeFromString_ValidString_Success)
{
using namespace AzToolsFramework;
auto deltaResult = AssetBundler::ParseComparisonType(AssetFileInfoListComparison::ComparisonTypeNames[aznumeric_cast<AZ::u8>(AssetFileInfoListComparison::ComparisonType::Delta)]);
EXPECT_EQ(deltaResult.IsSuccess(), true);
EXPECT_EQ(deltaResult.GetValue(), AssetFileInfoListComparison::ComparisonType::Delta);
auto unionResult = AssetBundler::ParseComparisonType(AssetFileInfoListComparison::ComparisonTypeNames[aznumeric_cast<AZ::u8>(AssetFileInfoListComparison::ComparisonType::Union)]);
EXPECT_EQ(unionResult.IsSuccess(), true);
EXPECT_EQ(unionResult.GetValue(), AssetFileInfoListComparison::ComparisonType::Union);
auto intersectionResult = AssetBundler::ParseComparisonType(AssetFileInfoListComparison::ComparisonTypeNames[aznumeric_cast<AZ::u8>(AssetFileInfoListComparison::ComparisonType::Intersection)]);
EXPECT_EQ(intersectionResult.IsSuccess(), true);
EXPECT_EQ(intersectionResult.GetValue(), AssetFileInfoListComparison::ComparisonType::Intersection);
auto complementResult = AssetBundler::ParseComparisonType(AssetFileInfoListComparison::ComparisonTypeNames[aznumeric_cast<AZ::u8>(AssetFileInfoListComparison::ComparisonType::Complement)]);
EXPECT_EQ(complementResult.IsSuccess(), true);
EXPECT_EQ(complementResult.GetValue(), AssetFileInfoListComparison::ComparisonType::Complement);
auto filePatternResult = AssetBundler::ParseComparisonType(AssetFileInfoListComparison::ComparisonTypeNames[aznumeric_cast<AZ::u8>(AssetFileInfoListComparison::ComparisonType::FilePattern)]);
EXPECT_EQ(filePatternResult.IsSuccess(), true);
EXPECT_EQ(filePatternResult.GetValue(), AssetFileInfoListComparison::ComparisonType::FilePattern);
}
TEST_F(BasicApplicationManagerTest, ComputeComparisonTypeFromInt_InvalidInt_Fails)
{
auto invalidResult = AssetBundler::ParseComparisonType("999");
EXPECT_EQ(invalidResult.IsSuccess(), false);
}
TEST_F(BasicApplicationManagerTest, ComputeComparisonTypeFromInt_ValidInt_Success)
{
int unionIndex(aznumeric_cast<int>(AzToolsFramework::AssetFileInfoListComparison::ComparisonType::Union));
auto unionResult = AssetBundler::ParseComparisonType(AZStd::string::format("%i", unionIndex));
EXPECT_TRUE(unionResult.IsSuccess());
EXPECT_EQ(unionResult.GetValue(), AzToolsFramework::AssetFileInfoListComparison::ComparisonType::Union);
}
TEST_F(BasicApplicationManagerTest, ComputeFilePatternTypeFromString_InvalidString_Fails)
{
auto invalidResult = AssetBundler::ParseFilePatternType("notafilepatterntype");
EXPECT_EQ(invalidResult.IsSuccess(), false);
}
TEST_F(BasicApplicationManagerTest, ComputeFilePatternTypeFromString_ValidString_Success)
{
using namespace AzToolsFramework;
auto wildcardResult = AssetBundler::ParseFilePatternType(AssetFileInfoListComparison::FilePatternTypeNames[aznumeric_cast<AZ::u8>(AssetFileInfoListComparison::FilePatternType::Wildcard)]);
EXPECT_TRUE(wildcardResult.IsSuccess());
EXPECT_EQ(wildcardResult.GetValue(), AssetFileInfoListComparison::FilePatternType::Wildcard);
auto regexResult = AssetBundler::ParseFilePatternType(AssetFileInfoListComparison::FilePatternTypeNames[aznumeric_cast<AZ::u8>(AssetFileInfoListComparison::FilePatternType::Regex)]);
EXPECT_TRUE(regexResult.IsSuccess());
EXPECT_EQ(regexResult.GetValue(), AssetFileInfoListComparison::FilePatternType::Regex);
}
TEST_F(BasicApplicationManagerTest, ComputeFilePatternTypeFromInt_InvalidInt_Fails)
{
auto invalidResult = AssetBundler::ParseFilePatternType("555");
EXPECT_EQ(invalidResult.IsSuccess(), false);
}
TEST_F(BasicApplicationManagerTest, IsTokenFile_Empty_ReturnsFalse)
{
EXPECT_FALSE(AzToolsFramework::AssetFileInfoListComparison::IsTokenFile(""));
}
TEST_F(BasicApplicationManagerTest, IsTokenFile_NonToken_ReturnsFalse)
{
EXPECT_FALSE(AzToolsFramework::AssetFileInfoListComparison::IsTokenFile("Somefile"));
}
TEST_F(BasicApplicationManagerTest, IsTokenFile_Token_ReturnsTrue)
{
EXPECT_TRUE(AzToolsFramework::AssetFileInfoListComparison::IsTokenFile("$SomeToken"));
}
TEST_F(BasicApplicationManagerTest, IsOutputPath_Empty_ReturnsFalse)
{
EXPECT_FALSE(AzToolsFramework::AssetFileInfoListComparison::IsOutputPath(""));
}
TEST_F(BasicApplicationManagerTest, IsOutputPath_NonToken_ReturnsTrue)
{
EXPECT_TRUE(AzToolsFramework::AssetFileInfoListComparison::IsOutputPath("Somefile"));
}
TEST_F(BasicApplicationManagerTest, IsOutputPath_Token_ReturnsFalse)
{
EXPECT_FALSE(AzToolsFramework::AssetFileInfoListComparison::IsOutputPath("$SomeToken"));
}
TEST_F(BasicApplicationManagerTest, ComputeFilePatternTypeFromInt_ValidInt_Success)
{
int regexIndex(aznumeric_cast<int>(AzToolsFramework::AssetFileInfoListComparison::FilePatternType::Regex));
auto regexResult = AssetBundler::ParseFilePatternType(AZStd::string::format("%i", regexIndex));
EXPECT_TRUE(regexResult.IsSuccess());
EXPECT_EQ(regexResult.GetValue(), AzToolsFramework::AssetFileInfoListComparison::FilePatternType::Regex);
}
}
+16
View File
@@ -0,0 +1,16 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
namespace AssetBundler
{
extern const char RelativeTestFolder[];
}
@@ -0,0 +1,412 @@
/*
* 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 <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/FileFunc/FileFunc.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AzToolsFramework/Asset/AssetBundler.h>
#include <AzFramework/Platform/PlatformDefaults.h>
#include <source/utils/utils.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/Settings/SettingsRegistryImpl.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <tests/main.h>
#include <source/utils/applicationManager.h>
extern char g_cachedEngineRoot[AZ_MAX_PATH_LEN];
namespace AssetBundler
{
class AssetBundlerBatchUtilsTest
: public UnitTest::ScopedAllocatorSetupFixture
{
};
TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_MacFile_OutputBaseNameAndPlatform)
{
AZStd::string filePath = "assetInfoFile_osx_gl.xml";
AZStd::string baseFilename;
AZStd::string platformIdentifier;
AzToolsFramework::SplitFilename(filePath, baseFilename, platformIdentifier);
ASSERT_EQ(baseFilename, "assetInfoFile");
ASSERT_EQ(platformIdentifier, "osx_gl");
}
TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_PcFile_OutputBaseNameAndPlatform)
{
AZStd::string filePath = "assetInfoFile_pc.xml";
AZStd::string baseFilename;
AZStd::string platformIdentifier;
AzToolsFramework::SplitFilename(filePath, baseFilename, platformIdentifier);
ASSERT_EQ(baseFilename, "assetInfoFile");
ASSERT_EQ(platformIdentifier, "pc");
}
TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_MacFileWithUnderScoreInFileName_OutputBaseNameAndPlatform)
{
AZStd::string filePath = "assetInfoFile_test_osx_gl.xml";
AZStd::string baseFilename;
AZStd::string platformIdentifier;
AzToolsFramework::SplitFilename(filePath, baseFilename, platformIdentifier);
ASSERT_EQ(baseFilename, "assetInfoFile_test");
ASSERT_EQ(platformIdentifier, "osx_gl");
}
TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_PcFileWithUnderScoreInFileName_OutputBaseNameAndPlatform)
{
AZStd::string filePath = "assetInfoFile_test_pc.xml";
AZStd::string baseFilename;
AZStd::string platformIdentifier;
AzToolsFramework::SplitFilename(filePath, baseFilename, platformIdentifier);
ASSERT_EQ(baseFilename, "assetInfoFile_test");
ASSERT_EQ(platformIdentifier, "pc");
}
const char RelativeTestFolder[] = "Code/Tools/AssetBundler/tests";
const char GemsFolder[] = "Gems";
const char EngineFolder[] = "Engine";
const char PlatformsFolder[] = "Platforms";
const char DummyProjectFolder[] = "DummyProject";
class AssetBundlerGemsUtilTest
: public UnitTest::ScopedAllocatorSetupFixture
{
public:
void SetUp() override
{
m_data = AZStd::make_unique<StaticData>();
m_data->m_application.reset(aznew AzToolsFramework::ToolsApplication());
m_data->m_application.get()->Start(AzFramework::Application::Descriptor());
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
if (!AZ::SettingsRegistry::Get())
{
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(m_registry);
AZ::SettingsRegistry::Register(&m_registry);
}
const char* engineRoot = nullptr;
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot);
if (!engineRoot)
{
GTEST_FATAL_FAILURE_(AZStd::string::format("Unable to locate engine root.\n").c_str());
}
AzFramework::StringFunc::Path::Join(engineRoot, RelativeTestFolder, m_data->m_testEngineRoot);
m_data->m_localFileIO = aznew AZ::IO::LocalFileIO();
m_data->m_priorFileIO = AZ::IO::FileIOBase::GetInstance();
// we need to set it to nullptr first because otherwise the
// underneath code assumes that we might be leaking the previous instance
AZ::IO::FileIOBase::SetInstance(nullptr);
AZ::IO::FileIOBase::SetInstance(m_data->m_localFileIO);
AddGemData(m_data->m_testEngineRoot.c_str(), "GemA");
AddGemData(m_data->m_testEngineRoot.c_str(), "GemB");
AZStd::string absoluteEngineSeedFilePath;
AzFramework::StringFunc::Path::ConstructFull(m_data->m_testEngineRoot.c_str(), EngineFolder, "SeedAssetList", AzToolsFramework::AssetSeedManager::GetSeedFileExtension(), absoluteEngineSeedFilePath, true);
m_data->m_gemSeedFilePairList.emplace_back(AZStd::make_pair(absoluteEngineSeedFilePath, true));
AddGemData(m_data->m_testEngineRoot.c_str(), "GemC", false);
AZStd::string absoluteProjectSeedFilePath;
AzFramework::StringFunc::Path::ConstructFull(m_data->m_testEngineRoot.c_str(), DummyProjectFolder, "SeedAssetList", AzToolsFramework::AssetSeedManager::GetSeedFileExtension(), absoluteProjectSeedFilePath, true);
m_data->m_gemSeedFilePairList.emplace_back(AZStd::make_pair(absoluteProjectSeedFilePath, true));
}
void TearDown() override
{
AZ::IO::FileIOBase::SetInstance(nullptr);
delete m_data->m_localFileIO;
AZ::IO::FileIOBase::SetInstance(m_data->m_priorFileIO);
m_data->m_gemInfoList.set_capacity(0);
m_data->m_gemSeedFilePairList.set_capacity(0);
m_data->m_application.get()->Stop();
m_data->m_application.reset();
}
void AddGemData(const char* engineRoot, const char* gemName, bool seedFileExists = true)
{
AZ::IO::Path relativeGemPath{ GemsFolder };
relativeGemPath /= gemName;
AZ::IO::Path absoluteGemPath{ engineRoot };
absoluteGemPath /= relativeGemPath;
AZ::IO::Path absoluteGemSeedFilePath = absoluteGemPath;
absoluteGemSeedFilePath /= "Assets/seedList";
absoluteGemSeedFilePath.ReplaceExtension(AZ::IO::PathView{ AzToolsFramework::AssetSeedManager::GetSeedFileExtension() });
absoluteGemSeedFilePath = absoluteGemSeedFilePath.LexicallyNormal();
m_data->m_gemSeedFilePairList.emplace_back(absoluteGemSeedFilePath, seedFileExists);
m_data->m_gemInfoList.emplace_back(AzToolsFramework::AssetUtils::GemInfo(gemName, relativeGemPath.Native(), absoluteGemPath.Native(), AZ::Uuid::CreateRandom().ToString<AZStd::string>().c_str(), false, false));
AZ::IO::Path platformsDirectory = absoluteGemPath / "Assets" / PlatformsFolder;
if (m_data->m_localFileIO->Exists(platformsDirectory.c_str()))
{
m_data->m_localFileIO->FindFiles(platformsDirectory.c_str(),
AZStd::string::format("*.%s", AzToolsFramework::AssetSeedManager::GetSeedFileExtension()).c_str(),
[&](const char* fileName)
{
AZStd::string normalizedFilePath = fileName;
AzFramework::StringFunc::Path::Normalize(normalizedFilePath);
m_data->m_gemSeedFilePairList.emplace_back(AZStd::make_pair(normalizedFilePath, seedFileExists));
return true;
});
}
AZ::IO::Path iosDirectory = platformsDirectory / AzFramework::PlatformIOS;
if (m_data->m_localFileIO->Exists(iosDirectory.c_str()))
{
bool recurse = true;
AZ::Outcome<AZStd::list<AZStd::string>, AZStd::string> result = AzFramework::FileFunc::FindFileList(iosDirectory.Native(),
AZStd::string::format("*.%s", AzToolsFramework::AssetSeedManager::GetSeedFileExtension()).c_str(), recurse);
if (result.IsSuccess())
{
AZStd::list<AZStd::string> seedFiles = result.TakeValue();
for(AZStd::string& seedFile : seedFiles)
{
AZStd::string normalizedFilePath = seedFile;
AzFramework::StringFunc::Path::Normalize(normalizedFilePath);
m_data->m_gemSeedFilePairList.emplace_back(AZStd::make_pair(normalizedFilePath, seedFileExists));
}
}
}
}
struct StaticData
{
AZStd::vector<AzToolsFramework::AssetUtils::GemInfo> m_gemInfoList;
AZStd::vector<AZStd::pair<AZStd::string, bool>> m_gemSeedFilePairList;
AZStd::unique_ptr<AzToolsFramework::ToolsApplication> m_application = {};
AZ::IO::FileIOBase* m_priorFileIO = nullptr;
AZ::IO::FileIOBase* m_localFileIO = nullptr;
AZStd::string m_testEngineRoot;
};
const int GemAIndex = 0;
const int GemBIndex = 1;
const int GemBSharedFileIndex = 2;
const int GemBIosFileIndex = 3;
const int EngineIndex = 4;
const int GemCIndex = 5;
const int ProjectIndex = 6;
AZStd::unique_ptr<StaticData> m_data;
AZ::SettingsRegistryImpl m_registry;
};
TEST_F(AssetBundlerGemsUtilTest, GetDefaultSeedFiles_AllSeedFiles_Found)
{
// DummyProject and fake Engine/Gem structure lives at dev/Code/Tools/AssetBundler/tests/
auto defaultSeedList = AssetBundler::GetDefaultSeedListFiles(m_data->m_testEngineRoot.c_str(), DummyProjectFolder, m_data->m_gemInfoList, AzFramework::PlatformFlags::Platform_PC);
ASSERT_EQ(defaultSeedList.size(), 5); //adding one for the engine seed file and one for the project file
// Validate whether both GemA and GemB seed file are present
EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[GemAIndex].first), defaultSeedList.end());
EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[GemBIndex].first), defaultSeedList.end());
EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[GemBSharedFileIndex].first), defaultSeedList.end());
// Validate that the engine seed file is present
EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[EngineIndex].first), defaultSeedList.end());
EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[ProjectIndex].first), defaultSeedList.end());
}
TEST_F(AssetBundlerGemsUtilTest, GetDefaultSeedFilesForMultiplePlatforms_AllSeedFiles_Found)
{
// DummyProject and fake Engine/Gem structure lives at dev/Code/Tools/AssetBundler/tests/
auto defaultSeedList = AssetBundler::GetDefaultSeedListFiles(m_data->m_testEngineRoot.c_str(), DummyProjectFolder, m_data->m_gemInfoList, AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_IOS);
ASSERT_EQ(defaultSeedList.size(), 6); //adding one for the engine seed file and one for the project file
// Validate whether both GemA and GemB seed file are present
EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[GemAIndex].first), defaultSeedList.end());
EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[GemBIndex].first), defaultSeedList.end());
EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[GemBSharedFileIndex].first), defaultSeedList.end());
EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[GemBIosFileIndex].first), defaultSeedList.end());
// Validate that the engine seed file is present
EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[EngineIndex].first), defaultSeedList.end());
EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[ProjectIndex].first), defaultSeedList.end());
}
TEST_F(AssetBundlerGemsUtilTest, IsSeedFileValid_Ok)
{
for (const auto& pair : m_data->m_gemSeedFilePairList)
{
bool result = IsGemSeedFilePathValid(m_data->m_testEngineRoot.c_str(), pair.first, m_data->m_gemInfoList, AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_IOS);
EXPECT_EQ(result,pair.second);
}
}
const char TestProject[] = "TestProject";
const char TestProjectLowerCase[] = "testproject";
#if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
const char TestRoot[] = "D:\\Dummy\\Test\\dev\\";
#else
const char TestRoot[] = "/Dummy/Test/dev/";
#endif
class MockApplication
: public AzFramework::ApplicationRequests::Bus::Handler
{
public:
MockApplication()
{
// ensure the cached engine root from previous tests is cleared
// so the mock application behaves properly
g_cachedEngineRoot[0] = 0;
if (AZ::SettingsRegistry::Get() == nullptr)
{
m_settingsRegistry = AZStd::make_unique<AZ::SettingsRegistryImpl>();
AZ::SettingsRegistry::Register(m_settingsRegistry.get());
auto gameProjectKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/%s",
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, "sys_game_folder");
m_settingsRegistry->Set(gameProjectKey, TestProject);
}
AzFramework::ApplicationRequests::Bus::Handler::BusConnect();
}
~MockApplication()
{
AzFramework::ApplicationRequests::Bus::Handler::BusDisconnect();
if (m_settingsRegistry.get() == AZ::SettingsRegistry::Get())
{
AZ::SettingsRegistry::Unregister(m_settingsRegistry.get());
}
}
// AzFramework::ApplicationRequests::Bus::Handler interface
void NormalizePath(AZStd::string& /*path*/) override {};
void NormalizePathKeepCase(AZStd::string& /*path*/) override {};
void CalculateBranchTokenForAppRoot(AZStd::string& /*token*/) const override {};
const char* GetEngineRoot() const { return TestRoot; }
private:
AZStd::unique_ptr<AZ::SettingsRegistryInterface> m_settingsRegistry;
};
class AssetBundlerPathUtilTest
: public UnitTest::ScopedAllocatorSetupFixture
{
public:
void SetUp() override
{
UnitTest::ScopedAllocatorSetupFixture::SetUp();
m_data = AZStd::make_unique<StaticData>();
}
void TearDown() override
{
m_data.reset();
UnitTest::ScopedAllocatorSetupFixture::TearDown();
}
struct StaticData
{
MockApplication m_mockApplication;
};
AZStd::unique_ptr<StaticData> m_data;
};
TEST_F(AssetBundlerPathUtilTest, ComputeAssetAliasAndGameName_AssetCatalogPathNotProvided_Valid)
{
AZStd::string platformIdentifier = "pc";
AZStd::string assetCatalogFile;
AZStd::string assetAlias;
AZStd::string gameName;
EXPECT_TRUE(ComputeAssetAliasAndGameName(platformIdentifier, assetCatalogFile, assetAlias, gameName).IsSuccess());
EXPECT_TRUE(gameName == TestProject);
AZStd::string assetPath;
bool success = AzFramework::StringFunc::Path::ConstructFull(TestRoot, "Cache", assetPath) &&
AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), TestProject, assetPath) &&
AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), platformIdentifier.c_str(), assetPath) &&
AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), TestProjectLowerCase, assetPath);
EXPECT_EQ(assetAlias, assetPath);
}
TEST_F(AssetBundlerPathUtilTest, ComputeAssetAliasAndGameName_AssetCatalogPathProvided_Valid)
{
AZStd::string platformIdentifier = "pc";
#if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
AZStd::string assetCatalogFile = "D:\\Dummy\\Test\\dev\\Cache\\TestProject1\\pc\\testproject1\\assetcatalog.xml";
#else
AZStd::string assetCatalogFile = "/Dummy/Test/dev/Cache/TestProject1/pc/testproject1/assetcatalog.xml";
#endif
AZStd::string assetAlias;
AZStd::string gameName;
EXPECT_TRUE(ComputeAssetAliasAndGameName(platformIdentifier, assetCatalogFile, assetAlias, gameName).IsSuccess());
EXPECT_EQ(gameName, "TestProject1");
AZStd::string assetPath;
bool success = AzFramework::StringFunc::Path::ConstructFull(TestRoot, "Cache", assetPath) &&
AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), "TestProject1", assetPath) &&
AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), platformIdentifier.c_str(), assetPath) &&
AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), "testproject1", assetPath);
EXPECT_EQ(assetAlias, assetPath);
}
TEST_F(AssetBundlerPathUtilTest, ComputeAssetAliasAndGameName_GameNameMismatch_Failure)
{
AZStd::string platformIdentifier = "pc";
#if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
AZStd::string assetCatalogFile = "D:\\Dummy\\Cache\\TestProject1\\pc\\testproject1\\assetcatalog.xml";
#else
AZStd::string assetCatalogFile = "/Dummy/Cache/TestProject1/pc/testproject1/assetcatalog.xml";
#endif
AZStd::string assetAlias;
AZStd::string gameName="SomeOtherGamename";
EXPECT_FALSE(ComputeAssetAliasAndGameName(platformIdentifier, assetCatalogFile, assetAlias, gameName).IsSuccess());
}
}
int main(int argc, char* argv[])
{
INVOKE_AZ_UNIT_TEST_MAIN();
AZ::AllocatorInstance<AZ::SystemAllocator>::Create();
int runSuccess = 0;
{
AssetBundler::ApplicationManager applicationManger(&argc, &argv);
applicationManger.Init();
runSuccess = applicationManger.Run() ? 0 : 1;
}
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
return runSuccess;
}