Update the FileIO Aliases (#4186)

* Update the FileIOAlias naming to make the cache, project root and engine
root paths more clear

The alias of `@root@`, `@assets@`, and `@projectplatformcache@` has been
collapsed to `@projectproductassets@`

The alias of `@devroot@` and `@engroot@` has been collapsed to
`@engroot@`

The alias of `@devassets@` and `@projectroot@` has been collapsed to
`@projectroot@`

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updated use of devassets and devroot properties in python

Those properties now use projectroot and engroot

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updating the alias @engroot@ alias path comment in each platform specific LocalFileIO_*.cpp file

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Removed hardcoded size of 9 for the product asset alias.

The ResolvePath function now just appends the @projectproductassets@
alias with the input path

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Remove duplicate @projectproductassets@ check in ProcessFileTreeRequest

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Fix for typos in Hydra python test

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updated LocalFileIO::Copy call on Windows to use the Unicode aware CopyFileW API

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updated the AWSMetreicsGemAllocatorFixture to properly suppress asset
cache write errors for Test file creation.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Removed unneeded call to set the @projectproductasstes@ alias at the bottom of the AssetSeedManagerTest SetUp

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Added a deprecated alias map to the FileIO System

When a deprecated alias is accessed, the FileIO System logs an AZ_Error and indicates the alias that should be used

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updated python test scripts to use the projectroot binding

Retrieving the AutomatedTesting project path based on "<devroot>/AutomatedTesting" has been removed.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updated references to devroot and devgame within the codebase

The GetAbsoluteDevGameFolderPath functions has been replaced with direct call to AZ::Utils::GetProjectPath
The GetAbsoluteDevRootFolderPath functions has been replaced with direct calls to AZ::Utils::GetEnginePath

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updated <engroot>/AutomatedTesting references to projectroot


Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Replaced references that assumes the project path is <engroot>/AutomatedTesting with <projectroot> in the AutomatedTesting python test

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Correct casing in emfxworkspace file


Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Removed newly added AppendPathParts function
Removed the Path constructors which accepts a PathIterable instance

The PathIterable isn't safe to return to a user of the Path class as it might be referencing temporary paths supplied via PathView arguments

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Fixed unused parameter warning

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Undid change to the LexicallyProximate function to set the path result to the base path.

It needs to return the *this path if the pathResult is empty

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Moved the LocalFileIO ConvertToAbsolutePath implementations to AZ::Utils

Fixed the ConvertToAbsolutePath implementation for Unix platforms to use a buffer that is size PATH_MAX(4096 on all our supported Unix platforms).
Because the buffer before was AZ::IO::MaxPathLength which as a size of 1024, this was resulting in the Unix `realpath` function smashing the stack when it converts a path to an absolute path that is longer than 1024 characters

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updated the EditorCore.Tests to attach the AZ Environment to the EditorCore shared library that is statically loaded on launch.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Fixed for DeprecatedAlaisesKeyVisitor Visit function causing the non string_view overloads being hidden causing a hidden base class function warning

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Changed the AWSMetricsGemMock to use a temporary for writing test assets

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updated the LocalFileIO::ResolvePath function to use HasRootPath to determine if a path starts with a root path such as "D:", "D:/" or "/"

IsAbsolute was not the corect check as the path of "D:" is a relative
path.
To be absolute according to the Windows the path must have a root
directory. i.e "D:/" or "D:\"

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Removed absolute path comment from LocalFile_UnixLike.cpp and LocalFile_Android.cpp FindFiles implementations
Updated the ConvertToAlias to supply an AZ::IO::FixedMaxPath

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Replaced usage of the @projectproductassets@ alias with @engroot@ when referring to the LmbrCentral source folder in the CopyDependencyBuilderTest and the SeedBuilderTests

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updated the ScriptCanvas Upgrade Tool to output backed up files to the
Project User Path instead of the engine root

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Fixed whitespacing issues in Application.cpp

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Remove unnecessary creation of a FixedMaxPath in the UpgradeTool.cpp

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Modified testSeedFolder variable in the SeedBuilderTests to use the
@engroot@ alias instead of @projectproductassets@/.. alias when
referring to the LmbrCentral Gem source path

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updated references to the Project Asset Cache in the PythonTests.

Those tests no longer use the logic `azlmbr.paths.projectroot / "Cache" / "pc"` to retrieve a path to the cache root but instead the `azlmbr.paths.projectproductassets` constant

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Fixed the FileIO Deprecated Alias test on Windows

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Removing @projectsourceassets@ alias, as it is only used once.

Updated the PhysX EditorSystemComponent.cpp to query the ProjectPath
form the SettingsRegistry.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Replaced @projectproductassets@ alias with @products@

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Rollback changes to the PhysX EditorSystemComponent.cpp

The changes to use the ProjectPath from the SettingsRegistry has been implemented in PR #4497

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
This commit is contained in:
lumberyard-employee-dm
2021-10-05 15:12:53 -05:00
committed by GitHub
parent 643bd84739
commit f648cb1fd8
209 changed files with 1365 additions and 1678 deletions
+10 -5
View File
@@ -148,7 +148,7 @@ namespace AZ
virtual AZ::u64 ModificationTime(HandleType fileHandle) = 0;
virtual AZ::u64 ModificationTime(const char* filePath) = 0;
/// Get the size of the file. Returns Success if we report size.
/// Get the size of the file. Returns Success if we report size.
virtual Result Size(const char* filePath, AZ::u64& size) = 0;
virtual Result Size(HandleType fileHandle, AZ::u64& size) = 0;
@@ -198,7 +198,7 @@ namespace AZ
/// note: the callback will contain the full concatenated path (filePath + slash + fileName)
/// not just the individual file name found.
/// note: if the file path of the found file corresponds to a registered ALIAS, the longest matching alias will be returned
/// so expect return values like @assets@/textures/mytexture.dds instead of a full path. This is so that fileIO works over remote connections.
/// so expect return values like @products@/textures/mytexture.dds instead of a full path. This is so that fileIO works over remote connections.
/// note: if rootPath is specified the implementation has the option of substituting it for the current directory
/// as would be the case on a file server.
typedef AZStd::function<bool(const char*)> FindFilesCallbackType;
@@ -206,13 +206,18 @@ namespace AZ
// Alias system
/// SetAlias - Adds an alias to the path resolution system, e.g. @user@, @root@, etc.
/// SetAlias - Adds an alias to the path resolution system, e.g. @user@, @products@, etc.
virtual void SetAlias(const char* alias, const char* path) = 0;
/// ClearAlias - Removes an alias from the path resolution system
virtual void ClearAlias(const char* alias) = 0;
/// GetAlias - Returns the destination path for a given alias, or nullptr if the alias does not exist
virtual const char* GetAlias(const char* alias) const = 0;
/// SetDeprecateAlias - Adds a deprecated alias with path resolution which points to a new alias
/// When the DeprecatedAlias is used an Error is logged and the alias is resolved to the path
/// specified by the new alais
virtual void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) = 0;
/// Shorten the given path if it contains an alias. it will always pick the longest alias match.
/// note that it re-uses the buffer, since the data can only get smaller and we don't want to internally allocate memory if we
/// can avoid it.
@@ -230,8 +235,8 @@ namespace AZ
//! ResolvePath - Replaces any aliases in path with their values and stores the result in resolvedPath,
//! also ensures that the path is absolute
//! NOTE: If the path does not start with an alias then the resolved value of the @assets@ is used
//! which has the effect of making the path relative to the @assets@/ folder
//! NOTE: If the path does not start with an alias then the resolved value of the @products@ is used
//! which has the effect of making the path relative to the @products@/ folder
//! returns true if path was resolved, false otherwise
//! note that all of the above file-finding and opening functions automatically resolve the path before operating
//! so you should not need to call this except in very exceptional circumstances where you absolutely need to
+16 -16
View File
@@ -42,8 +42,8 @@ namespace AZ::IO
// These functions can't be called after a request has been queued.
//
//! Creates a request to read a file.
//! @param relativePath Relative path to the file to load. This can include aliases such as @assets@.
//! Creates a request to read a file.
//! @param relativePath Relative path to the file to load. This can include aliases such as @products@.
//! @param outputBuffer The buffer that will hold the loaded data. This must be able to at least hold "size" number of bytes.
//! @param outputBufferSize The size of the buffer that will hold the loaded data. This must be equal or larger than "size" number of bytes.
//! @param readSize The number of bytes to read from the file at the relative path.
@@ -62,9 +62,9 @@ namespace AZ::IO
IStreamerTypes::Priority priority = IStreamerTypes::s_priorityMedium,
size_t offset = 0) = 0;
//! Sets a request to the read command.
//! Sets a request to the read command.
//! @param request The request that will store the read command.
//! @param relativePath Relative path to the file to load. This can include aliases such as @assets@.
//! @param relativePath Relative path to the file to load. This can include aliases such as @products@.
//! @param outputBuffer The buffer that will hold the loaded data. This must be able to at least hold "size" number of bytes.
//! @param outputBufferSize The size of the buffer that will hold the loaded data. This must be equal or larger than "size" number of bytes.
//! @param readSize The number of bytes to read from the file at the relative path.
@@ -84,8 +84,8 @@ namespace AZ::IO
IStreamerTypes::Priority priority = IStreamerTypes::s_priorityMedium,
size_t offset = 0) = 0;
//! Creates a request to the read command.
//! @param relativePath Relative path to the file to load. This can include aliases such as @assets@.
//! Creates a request to the read command.
//! @param relativePath Relative path to the file to load. This can include aliases such as @products@.
//! @param allocator The allocator used to reserve and release memory for the read request. Memory allocated this way will
//! be automatically freed when there are no more references to the FileRequestPtr. To avoid this, use GetReadRequestResult
//! to claim the pointer and use the provided allocator to release the memory at a later point.
@@ -106,9 +106,9 @@ namespace AZ::IO
IStreamerTypes::Priority priority = IStreamerTypes::s_priorityMedium,
size_t offset = 0) = 0;
//! Sets a request to the read command.
//! Sets a request to the read command.
//! @param request The request that will store the read command.
//! @param relativePath Relative path to the file to load. This can include aliases such as @assets@.
//! @param relativePath Relative path to the file to load. This can include aliases such as @products@.
//! @param allocator The allocator used to reserve and release memory for the read request. Memory allocated this way will
//! be automatically freed when there are no more references to the FileRequestPtr. To avoid this, use GetReadRequestResult
//! to claim the pointer and use the provided allocator to release the memory at a later point.
@@ -138,7 +138,7 @@ namespace AZ::IO
//! @result A smart pointer to the newly created request with the cancel command.
virtual FileRequestPtr Cancel(FileRequestPtr target) = 0;
//! Sets a request to the cancel command.
//! Sets a request to the cancel command.
//! When this request completes it's not guaranteed to have canceled the target request. Not all requests can be canceled and requests
//! that already processing may complete. It's recommended to let the target request handle the completion of the request as normal
//! and handle cancellation by checking the status on the target request is set to IStreamerTypes::RequestStatus::Canceled.
@@ -177,7 +177,7 @@ namespace AZ::IO
//! DestroyDedicatedCache is called. Typical use of a dedicated cache is for files that have their own compression
//! and are periodically visited to read a section, e.g. streaming video play or streaming audio banks. This
//! request will fail if there are no nodes in Streamer's stack that deal with dedicated caches.
//! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @assets@.
//! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @products@.
//! @return A smart pointer to the newly created request with the command to create a dedicated cache.
virtual FileRequestPtr CreateDedicatedCache(AZStd::string_view relativePath) = 0;
@@ -186,25 +186,25 @@ namespace AZ::IO
//! and are periodically visited to read a section, e.g. streaming video play or streaming audio banks. This
//! request will fail if there are no nodes in Streamer's stack that deal with dedicated caches.
//! @param request The request that will store the command to create a dedicated cache.
//! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @assets@.
//! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @products@.
//! @return A reference to the provided request.
virtual FileRequestPtr& CreateDedicatedCache(FileRequestPtr& request, AZStd::string_view relativePath) = 0;
//! Destroy a dedicated cache created by CreateDedicatedCache. See CreateDedicatedCache for more details.
//! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @assets@.
//! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @products@.
//! @return A smart pointer to the newly created request with the command to destroy a dedicated cache.
virtual FileRequestPtr DestroyDedicatedCache(AZStd::string_view relativePath) = 0;
//! Destroy a dedicated cache created by CreateDedicatedCache. See CreateDedicatedCache for more details.
//! @param request The request that will store the command to destroy a dedicated cache.
//! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @assets@.
//! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @products@.
//! @return A reference to the provided request.
virtual FileRequestPtr& DestroyDedicatedCache(FileRequestPtr& request, AZStd::string_view relativePath) = 0;
//! Clears a file from all caches in use by Streamer.
//! Flushing the cache will cause the streaming stack to pause processing until it's idle before issuing the flush and resuming
//! processing. This can result in a noticeable interruption.
//! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @assets@.
//! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @products@.
//! @return A smart pointer to the newly created request with the command to flush a file from all caches.
virtual FileRequestPtr FlushCache(AZStd::string_view relativePath) = 0;
@@ -212,7 +212,7 @@ namespace AZ::IO
//! Flushing the cache will cause the streaming stack to pause processing until it's idle before issuing the flush and resuming
//! processing. This can result in a noticeable interruption.
//! @param request The request that will store the command to flush a file from all caches.
//! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @assets@.
//! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @products@.
//! @return A reference to the provided request.
virtual FileRequestPtr& FlushCache(FileRequestPtr& request, AZStd::string_view relativePath) = 0;
@@ -334,7 +334,7 @@ namespace AZ::IO
//
//! Collect statistics from all the components that make up Streamer.
//! This is thread safe in the sense that it won't crash.
//! This is thread safe in the sense that it won't crash.
//! Data is collected lockless from involved threads and might be slightly
//! out of date in some cases.
//! @param statistics The container where statistics will be added to.
+32 -22
View File
@@ -98,6 +98,11 @@ namespace AZ::IO
//! made from the internal string
constexpr AZStd::fixed_string<MaxPathLength> FixedMaxPathString() const noexcept;
// as_posix
//! Replicates the behavior of the Python pathlib as_posix method
//! by replacing the Windows Path Separator with the Posix Path Seperator
constexpr AZStd::fixed_string<MaxPathLength> FixedMaxPathStringAsPosix() const noexcept;
// decomposition
//! Given a windows path of "C:\O3DE\foo\bar\name.txt" and a posix path of
//! "/O3DE/foo/bar/name.txt"
@@ -178,7 +183,7 @@ namespace AZ::IO
//! Normalizes a path in a purely lexical manner.
//! # Path separators are converted to their preferred path separator
//! # Path parts of "." are collapsed to nothing empty
//! # Paths parts of ".." are removed if there is a preceding directory
//! # Paths parts of ".." are removed if there is a preceding directory
//! The preceding directory is also removed
//! # Runs of Two or more path separators are collapsed into one path separator
//! unless the path begins with two path separators
@@ -238,7 +243,7 @@ namespace AZ::IO
// iterators
//! Returns an iterator to the beginning of the path that can be used to traverse the path
//! according to the following
//! according to the following
//! 1. Root name - (0 or 1)
//! 2. Root directory - (0 or 1)
//! 3. Filename - (0 or more)
@@ -253,24 +258,23 @@ namespace AZ::IO
template <typename StringType>
friend class BasicPath;
friend struct AZStd::hash<PathView>;
template <typename PathResultType>
static constexpr void MakeRelativeTo(PathResultType& pathResult, const AZ::IO::PathView& path, const AZ::IO::PathView& base);
struct PathIterable;
static constexpr void MakeRelativeTo(PathIterable& pathResult, const AZ::IO::PathView& path, const AZ::IO::PathView& base) noexcept;
//! Returns a structure that provides a view of the path parts which can be used for iteration
//! Only the path parts that correspond to creating an normalized path is returned
//! This function is useful for returning a "view" into a normalized path without the need
//! to allocate memory for the heap
static constexpr PathIterable GetNormalPathParts(const AZ::IO::PathView& path) noexcept;
// joins the input path to the Path Iterable structure using similiar logic to Path::Append
// If the input path is absolute it will replace the current PathIterable otherwise
// the input path will be appended to the Path Iterable structure
// For example a PathIterable with parts = ['C:', '/', 'foo']
// If the path input = 'bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar']
// If the path input = 'C:/bar', then the new PathIterable parts = [C:', '/', 'bar']
// If the path input = 'C:bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar' ]
// If the path input = 'D:bar', then the new PathIterable parts = [D:, 'bar' ]
//! joins the input path to the Path Iterable structure using similiar logic to Path::Append
//! If the input path is absolute it will replace the current PathIterable otherwise
//! the input path will be appended to the Path Iterable structure
//! For example a PathIterable with parts = ['C:', '/', 'foo']
//! If the path input = 'bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar']
//! If the path input = 'C:/bar', then the new PathIterable parts = [C:', '/', 'bar']
//! If the path input = 'C:bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar' ]
//! If the path input = 'D:bar', then the new PathIterable parts = [D:, 'bar' ]
static constexpr void AppendNormalPathParts(PathIterable& pathIterableResult, const AZ::IO::PathView& path) noexcept;
constexpr int ComparePathView(const PathView& other) const;
@@ -325,32 +329,32 @@ namespace AZ::IO
constexpr BasicPath(BasicPath&& other) = default;
// Conversion constructor for other types of BasicPath instantiations
constexpr BasicPath(const PathView& other);
constexpr BasicPath(const PathView& other) noexcept;
// String constructors
//! Constructs a Path by copying the pathString to its internal string
//! The preferred separator is to the OS default path separator
constexpr BasicPath(const string_type& pathString) noexcept;
//! Constructs a Path by copying the pathString to its internal string
//! The preferred separator it set to the parameter
//! The preferred separator is set to the parameter
constexpr BasicPath(const string_type& pathString, const char preferredSeparator) noexcept;
//! Constructs a Path by moving the pathString to its internal string
//! The preferred separator is to the OS default path separator
constexpr BasicPath(string_type&& pathString) noexcept;
//! Constructs a Path by copying the pathString to its internal string
//! The preferred separator it set to the parameter
//! The preferred separator is set to the parameter
constexpr BasicPath(string_type&& pathString, const char preferredSeparator) noexcept;
//! Constructs a Path by constructing it's internal out of a string_view
//! The preferred separator is to the OS default path separator
constexpr BasicPath(AZStd::string_view src) noexcept;
//! Constructs a Path by constructing it's internal out of a string_view
//! The preferred separators it set to the parameter
//! The preferred separator is set to the parameter
constexpr BasicPath(AZStd::string_view src, const char preferredSeparator) noexcept;
//! Constructs a Path by constructing it's internal out of a value_type*
//! The preferred separator is to the OS default path separator
constexpr BasicPath(const value_type* pathString) noexcept;
//! Constructs a Path by constructing it's internal out of a value_type*
//! The preferred separator it set to the parameter
//! The preferred separator is set to the parameter
constexpr BasicPath(const value_type* pathString, const char preferredSeparator) noexcept;
//! Constructs a empty Path with the preferred separator set to the parameter
explicit constexpr BasicPath(const char preferredSeparator) noexcept;
@@ -371,7 +375,7 @@ namespace AZ::IO
constexpr BasicPath& operator=(BasicPath&& other) = default;
// conversion assignment operator
constexpr BasicPath& operator=(const PathView& pathView);
constexpr BasicPath& operator=(const PathView& pathView) noexcept;
constexpr BasicPath& operator=(const string_type& str) noexcept;
constexpr BasicPath& operator=(string_type&& str) noexcept;
constexpr BasicPath& operator=(AZStd::string_view str) noexcept;
@@ -477,6 +481,12 @@ namespace AZ::IO
//! made from the internal string
constexpr AZStd::fixed_string<MaxPathLength> FixedMaxPathString() const;
// as_posix
//! Replicates the behavior of the Python pathlib as_posix method
//! by replacing the Windows Path Separator with the Posix Path Seperator
AZStd::string StringAsPosix() const;
constexpr AZStd::fixed_string<MaxPathLength> FixedMaxPathStringAsPosix() const noexcept;
// compare
//! Performs a compare of each of the path parts for equivalence
//! Each part of the path is compare using string comparison
@@ -574,7 +584,7 @@ namespace AZ::IO
//! Normalizes a path in a purely lexical manner.
//! # Path separators are converted to their preferred path separator
//! # Path parts of "." are collapsed to nothing empty
//! # Paths parts of ".." are removed if there is a preceding directory
//! # Paths parts of ".." are removed if there is a preceding directory
//! The preceding directory is also removed
//! # Runs of Two or more path separators are collapsed into one path separator
//! unless the path begins with two path separators
@@ -616,7 +626,7 @@ namespace AZ::IO
// iterators
//! Returns an iterator to the beginning of the path that can be used to traverse the path
//! according to the following
//! according to the following
//! 1. Root name - (0 or 1)
//! 2. Root directory - (0 or 1)
//! 3. Filename - (0 or more)
+59 -24
View File
@@ -240,6 +240,14 @@ namespace AZ::IO
return AZStd::fixed_string<MaxPathLength>(m_path.begin(), m_path.end());
}
// as_posix
constexpr AZStd::fixed_string<MaxPathLength> PathView::FixedMaxPathStringAsPosix() const noexcept
{
AZStd::fixed_string<MaxPathLength> resultPath(m_path.begin(), m_path.end());
AZStd::replace(resultPath.begin(), resultPath.end(), AZ::IO::WindowsPathSeparator, AZ::IO::PosixPathSeparator);
return resultPath;
}
// decomposition
constexpr auto PathView::RootName() const -> PathView
{
@@ -473,8 +481,7 @@ namespace AZ::IO
return lhs.Compare(rhs) >= 0;
}
template <typename PathResultType>
constexpr void PathView::MakeRelativeTo(PathResultType& pathResult, const AZ::IO::PathView& path, const AZ::IO::PathView& base)
constexpr void PathView::MakeRelativeTo(PathIterable& pathIterable, const AZ::IO::PathView& path, const AZ::IO::PathView& base) noexcept
{
const bool exactCaseCompare = path.m_preferred_separator == PosixPathSeparator
|| base.m_preferred_separator == PosixPathSeparator;
@@ -492,13 +499,11 @@ namespace AZ::IO
if (int res = Internal::ComparePathSegment(*pathParser, *pathParserBase, exactCaseCompare);
res != 0)
{
pathResult.m_path = AZStd::string_view{};
return;
}
}
else if (CheckIterMismatchAtBase())
{
pathResult.m_path = AZStd::string_view{};
return;
}
@@ -512,7 +517,6 @@ namespace AZ::IO
}
if (CheckIterMismatchAtBase())
{
pathResult.m_path = AZStd::string_view{};
return;
}
}
@@ -530,7 +534,7 @@ namespace AZ::IO
// If there is no mismatch, return ".".
if (!pathParser && !pathParserBase)
{
pathResult.m_path = AZStd::string_view{ "." };
pathIterable.emplace_back(".", parser::PathPartKind::PK_Dot);
return;
}
@@ -539,27 +543,25 @@ namespace AZ::IO
int elemCount = parser::DetermineLexicalElementCount(pathParserBase);
if (elemCount < 0)
{
pathResult.m_path = AZStd::string_view{};
return;
}
// if elemCount == 0 and (pathParser == end() || pathParser->empty()), returns path("."); otherwise
if (elemCount == 0 && (pathParser.AtEnd() || *pathParser == ""))
{
pathResult.m_path = AZStd::string_view{ "." };
pathIterable.emplace_back(".", parser::PathPartKind::PK_Dot);
return;
}
// return a path constructed with 'n' dot-dot elements, followed by the
// elements of '*this' after the mismatch.
pathResult = PathResultType(path.m_preferred_separator);
while (elemCount--)
{
pathResult /= "..";
pathIterable.emplace_back("..", parser::PathPartKind::PK_DotDot);
}
for (; pathParser; ++pathParser)
{
pathResult /= *pathParser;
pathIterable.emplace_back(*pathParser, parser::ClassifyPathPart(pathParser));
}
}
@@ -673,7 +675,7 @@ namespace AZ::IO
// Basic Path implementation
template <typename StringType>
constexpr BasicPath<StringType>::BasicPath(const PathView& other)
constexpr BasicPath<StringType>::BasicPath(const PathView& other) noexcept
: m_path(other.m_path)
, m_preferred_separator(other.m_preferred_separator) {}
@@ -726,6 +728,7 @@ namespace AZ::IO
: m_path(first, last)
, m_preferred_separator(preferredSeparator) {}
template <typename StringType>
constexpr BasicPath<StringType>::operator PathView() const noexcept
{
@@ -733,7 +736,7 @@ namespace AZ::IO
}
template <typename StringType>
constexpr auto BasicPath<StringType>::operator=(const PathView& other) -> BasicPath&
constexpr auto BasicPath<StringType>::operator=(const PathView& other) noexcept -> BasicPath&
{
m_path = other.m_path;
m_preferred_separator = other.m_preferred_separator;
@@ -974,13 +977,13 @@ namespace AZ::IO
template <typename StringType>
constexpr auto BasicPath<StringType>::MakePreferred() -> BasicPath&
{
if (m_preferred_separator != '/')
if (m_preferred_separator != PosixPathSeparator)
{
AZStd::replace(m_path.begin(), m_path.end(), '/', m_preferred_separator);
AZStd::replace(m_path.begin(), m_path.end(), PosixPathSeparator, m_preferred_separator);
}
else
{
AZStd::replace(m_path.begin(), m_path.end(), '\\', m_preferred_separator);
AZStd::replace(m_path.begin(), m_path.end(), WindowsPathSeparator, m_preferred_separator);
}
return *this;
}
@@ -1033,6 +1036,24 @@ namespace AZ::IO
return AZStd::fixed_string<MaxPathLength>(m_path.begin(), m_path.end());
}
// as_posix
// Returns a copy of the path with the path separators converted to PosixPathSeparator
template <typename StringType>
AZStd::string BasicPath<StringType>::StringAsPosix() const
{
AZStd::string resultPath(m_path.begin(), m_path.end());
AZStd::replace(resultPath.begin(), resultPath.end(), WindowsPathSeparator, PosixPathSeparator);
return resultPath;
}
template <typename StringType>
constexpr AZStd::fixed_string<MaxPathLength> BasicPath<StringType>::FixedMaxPathStringAsPosix() const noexcept
{
AZStd::fixed_string<MaxPathLength> resultPath(m_path.begin(), m_path.end());
AZStd::replace(resultPath.begin(), resultPath.end(), WindowsPathSeparator, PosixPathSeparator);
return resultPath;
}
template <typename StringType>
constexpr void BasicPath<StringType>::swap(BasicPath& rhs) noexcept
{
@@ -1234,6 +1255,7 @@ namespace AZ::IO
{
pathResult /= pathPartView;
}
return pathResult;
}
@@ -1241,7 +1263,13 @@ namespace AZ::IO
constexpr auto BasicPath<StringType>::LexicallyRelative(const PathView& base) const -> BasicPath
{
BasicPath pathResult(m_preferred_separator);
static_cast<PathView>(*this).MakeRelativeTo(pathResult, *this, base);
PathView::PathIterable pathIterable;
PathView::MakeRelativeTo(pathIterable, *this, base);
for ([[maybe_unused]] auto [pathPartView, pathPartKind] : pathIterable)
{
pathResult /= pathPartView;
}
return pathResult;
}
@@ -1355,7 +1383,7 @@ namespace AZ::IO
return !basePathParts.empty() || !thisPathParts.IsAbsolute();
}
constexpr FixedMaxPath PathView::LexicallyNormal() const
constexpr auto PathView::LexicallyNormal() const -> FixedMaxPath
{
FixedMaxPath pathResult(m_preferred_separator);
PathIterable pathIterable = GetNormalPathParts(*this);
@@ -1367,21 +1395,28 @@ namespace AZ::IO
return pathResult;
}
constexpr FixedMaxPath PathView::LexicallyRelative(const PathView& base) const
constexpr auto PathView::LexicallyRelative(const PathView& base) const -> FixedMaxPath
{
FixedMaxPath pathResult(m_preferred_separator);
MakeRelativeTo(pathResult, *this, base);
PathIterable pathIterable;
MakeRelativeTo(pathIterable, *this, base);
for ([[maybe_unused]] auto [pathPartView, pathPartKind] : pathIterable)
{
pathResult /= pathPartView;
}
return pathResult;
}
constexpr FixedMaxPath PathView::LexicallyProximate(const PathView& base) const
constexpr auto PathView::LexicallyProximate(const PathView& base) const -> FixedMaxPath
{
FixedMaxPath result = LexicallyRelative(base);
if (result.empty())
FixedMaxPath pathResult = LexicallyRelative(base);
if (pathResult.empty())
{
return FixedMaxPath(*this);
}
return result;
return pathResult;
}
}
@@ -49,8 +49,8 @@ namespace AZ::IO
constexpr void clear() noexcept;
friend constexpr auto PathView::GetNormalPathParts(const AZ::IO::PathView&) noexcept -> PathIterable;
friend constexpr auto PathView::AppendNormalPathParts(PathIterable& pathIterable, const AZ::IO::PathView&) noexcept -> void;
friend constexpr auto PathView::MakeRelativeTo(PathIterable& pathIterable, const AZ::IO::PathView&, const AZ::IO::PathView&) noexcept -> void;
PartKindArray m_parts{};
size_t m_size{};
};
@@ -546,7 +546,7 @@ namespace AZ::SettingsRegistryMergeUtils
AZ::IO::FixedMaxPath path = AZ::Utils::GetExecutableDirectory();
registry.Set(FilePathKey_BinaryFolder, path.LexicallyNormal().Native());
// Engine root folder - corresponds to the @engroot@ and @devroot@ aliases
// Engine root folder - corresponds to the @engroot@ and @engroot@ aliases
AZ::IO::FixedMaxPath engineRoot = FindEngineRoot(registry);
registry.Set(FilePathKey_EngineRootFolder, engineRoot.LexicallyNormal().Native());
@@ -570,7 +570,7 @@ namespace AZ::SettingsRegistryMergeUtils
assetPlatform = AZ::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME);
}
// Project path - corresponds to the @devassets@ alias
// Project path - corresponds to the @projectroot@ alias
// NOTE: Here we append to engineRoot, but if projectPathValue is absolute then engineRoot is discarded.
path = engineRoot / projectPathValue;
@@ -662,7 +662,7 @@ namespace AZ::SettingsRegistryMergeUtils
}
else
{
// Cache: root - same as the @root@ alias, this is the starting path for cache files.
// Cache: root - same as the @products@ alias, this is the starting path for cache files.
path = normalizedProjectPath / "Cache";
registry.Set(FilePathKey_CacheProjectRootFolder, path.LexicallyNormal().Native());
path /= assetPlatform;
@@ -52,6 +52,7 @@ namespace AZ
MOCK_METHOD2(SetAlias, void(const char* alias, const char* path));
MOCK_METHOD1(ClearAlias, void(const char* alias));
MOCK_CONST_METHOD1(GetAlias, const char*(const char* alias));
MOCK_METHOD2(SetDeprecatedAlias, void(AZStd::string_view, AZStd::string_view));
MOCK_CONST_METHOD2(ConvertToAlias, AZStd::optional<AZ::u64>(char* inOutBuffer, AZ::u64 bufferLength));
MOCK_CONST_METHOD2(ConvertToAlias, bool(AZ::IO::FixedMaxPath& aliasPath, const AZ::IO::PathView& path));
MOCK_CONST_METHOD3(ResolvePath, bool(const char* path, char* resolvedPath, AZ::u64 resolvedPathSize));
@@ -51,6 +51,20 @@ namespace AZ::Utils
return executableDirectory;
}
AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path)
{
AZ::IO::FixedMaxPathString absolutePath;
AZ::IO::FixedMaxPathString srcPath{ path };
if (ConvertToAbsolutePath(srcPath.c_str(), absolutePath.data(), absolutePath.capacity()))
{
// Fix the size value of the fixed string by calculating the c-string length using char traits
absolutePath.resize_no_construct(AZStd::char_traits<char>::length(absolutePath.data()));
return srcPath;
}
return AZStd::nullopt;
}
AZ::IO::FixedMaxPathString GetEngineManifestPath()
{
AZ::IO::FixedMaxPath o3deManifestPath = GetO3deManifestDirectory();
@@ -104,6 +104,7 @@ namespace AZ
// Attempts the supplied path to an absolute path.
//! Returns nullopt if path cannot be converted to an absolute path
AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path);
bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 absolutePathMaxSize);
//! Save a string to a file. Otherwise returns a failure with error message.
AZ::Outcome<void, AZStd::string> WriteFile(AZStd::string_view content, AZStd::string_view filePath);
@@ -60,23 +60,34 @@ namespace AZ
return writeStorage ? AZStd::make_optional<AZ::IO::FixedMaxPathString>(writeStorage) : AZStd::nullopt;
}
AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path)
bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength)
{
AZ::IO::FixedMaxPathString absolutePath;
AZ::IO::FixedMaxPathString srcPath{ path };
if (AZ::Android::Utils::IsApkPath(srcPath.c_str()))
if (AZ::Android::Utils::IsApkPath(path))
{
return srcPath;
azstrcpy(absolutePath, maxLength, path);
return true;
}
if(char* result = realpath(srcPath.c_str(), absolutePath.data()); result)
#ifdef PATH_MAX
static constexpr size_t UnixMaxPathLength = PATH_MAX;
#else
// Fallback to 4096 if the PATH_MAX macro isn't defined on the Unix System
static constexpr size_t UnixMaxPathLength = 4096;
#endif
if (!AZ::IO::PathView(path).IsAbsolute())
{
// Fix the size value of the fixed string by calculating the c-string length using char traits
absolutePath.resize_no_construct(AZStd::char_traits<char>::length(absolutePath.data()));
return absolutePath;
// note that realpath fails if the path does not exist and actually changes the return value
// to be the actual place that FAILED, which we don't want.
// if we fail, we'd prefer to fall through and at least use the original path.
char absolutePathBuffer[UnixMaxPathLength];
if (const char* result = realpath(path, absolutePathBuffer); result != nullptr)
{
azstrcpy(absolutePath, maxLength, absolutePathBuffer);
return true;
}
}
return AZStd::nullopt;
azstrcpy(absolutePath, maxLength, path);
return AZ::IO::PathView(absolutePath).IsAbsolute();
}
}
}
@@ -47,23 +47,32 @@ namespace AZ
AZ::IO::FixedMaxPath path{pass->pw_dir};
return path.Native();
}
return {};
}
AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path)
bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength)
{
AZ::IO::FixedMaxPathString absolutePath;
AZ::IO::FixedMaxPathString srcPath{ path };
if (char* result = realpath(srcPath.c_str(), absolutePath.data()); result)
#ifdef PATH_MAX
static constexpr size_t UnixMaxPathLength = PATH_MAX;
#else
// Fallback to 4096 if the PATH_MAX macro isn't defined on the Unix System
static constexpr size_t UnixMaxPathLength = 4096;
#endif
if (!AZ::IO::PathView(path).IsAbsolute())
{
// Fix the size value of the fixed string by calculating the c-string length using char traits
absolutePath.resize_no_construct(AZStd::char_traits<char>::length(absolutePath.data()));
return absolutePath;
// note that realpath fails if the path does not exist and actually changes the return value
// to be the actual place that FAILED, which we don't want.
// if we fail, we'd prefer to fall through and at least use the original path.
char absolutePathBuffer[UnixMaxPathLength];
if (const char* result = realpath(path, absolutePathBuffer); result != nullptr)
{
azstrcpy(absolutePath, maxLength, absolutePathBuffer);
return true;
}
}
return AZStd::nullopt;
azstrcpy(absolutePath, maxLength, path);
return AZ::IO::PathView(absolutePath).IsAbsolute();
}
} // namespace Utils
} // namespace AZ
@@ -67,19 +67,12 @@ namespace AZ
return AZStd::nullopt;
}
AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path)
bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength)
{
AZ::IO::FixedMaxPathString absolutePath;
AZ::IO::FixedMaxPathString srcPath{ path };
char* result = _fullpath(absolutePath.data(), srcPath.c_str(), absolutePath.capacity());
// Force update of the fixed_string size() value
absolutePath.resize_no_construct(AZStd::char_traits<char>::length(absolutePath.data()));
if (result)
{
return absolutePath;
}
return AZStd::nullopt;
char* result = _fullpath(absolutePath, path, maxLength);
return result != nullptr;
}
}
}
@@ -426,6 +426,10 @@ public:
return nullptr;
}
void SetDeprecatedAlias(AZStd::string_view, AZStd::string_view) override
{
}
void ClearAlias(const char* ) override { }
AZStd::optional<AZ::u64> ConvertToAlias(char* inOutBuffer, AZ::u64) const override
@@ -698,7 +698,7 @@ AZ_POP_DISABLE_WARNING
using PathViewLexicallyProximateFixture = PathLexicallyFixture<PathViewLexicallyProximateParams>;
TEST_P(PathViewLexicallyProximateFixture, LexicallyProximate_ReturnsRelativePathIfNotEmptyOrTestPathIfNot)
TEST_P(PathViewLexicallyProximateFixture, LexicallyProximate_ReturnsRelativePathIfNotEmptyOrTestPath)
{
const auto& testParams = GetParam();
AZ::IO::PathView testPath(testParams.m_testPathString, testParams.m_preferredSeparator);
@@ -77,11 +77,15 @@
namespace AzFramework
{
namespace ApplicationInternal
{
static constexpr const char s_prefabSystemKey[] = "/Amazon/Preferences/EnablePrefabSystem";
static constexpr const char s_prefabWipSystemKey[] = "/Amazon/Preferences/EnablePrefabSystemWipFeatures";
static constexpr const char s_legacySlicesAssertKey[] = "/Amazon/Preferences/ShouldAssertForLegacySlicesUsage";
static constexpr const char* DeprecatedFileIOAliasesRoot = "/O3DE/AzCore/FileIO/DeprecatedAliases";
static constexpr const char* DeprecatedFileIOAliasesOldAliasKey = "OldAlias";
static constexpr const char* DeprecatedFileIOAliasesNewAliasKey = "NewAlias";
}
Application::Application()
@@ -563,6 +567,68 @@ namespace AzFramework
}
}
struct DeprecatedAliasesKeyVisitor
: AZ::SettingsRegistryInterface::Visitor
{
using VisitResponse = AZ::SettingsRegistryInterface::VisitResponse;
using VisitAction = AZ::SettingsRegistryInterface::VisitAction;
using Type = AZ::SettingsRegistryInterface::Type;
using AZ::SettingsRegistryInterface::Visitor::Visit;
VisitResponse Traverse(AZStd::string_view path, AZStd::string_view,
VisitAction action, Type type) override
{
if (action == AZ::SettingsRegistryInterface::VisitAction::Begin)
{
if (type == AZ::SettingsRegistryInterface::Type::Array)
{
m_parentArrayPath = path;
}
// Strip off last path segment from json path and check if is a child element of the array
if (AZ::StringFunc::TokenizeLast(path, '/');
m_parentArrayPath == path)
{
m_aliases.emplace_back();
}
}
else if (action == AZ::SettingsRegistryInterface::VisitAction::End)
{
if (type == AZ::SettingsRegistryInterface::Type::Array)
{
m_parentArrayPath = AZStd::string{};
}
}
return AZ::SettingsRegistryInterface::VisitResponse::Continue;
}
void Visit(AZStd::string_view, AZStd::string_view valueName, Type, AZStd::string_view value) override
{
if (!m_aliases.empty())
{
if (valueName == ApplicationInternal::DeprecatedFileIOAliasesOldAliasKey)
{
m_aliases.back().m_oldAlias = value;
}
else if (valueName == ApplicationInternal::DeprecatedFileIOAliasesNewAliasKey)
{
m_aliases.back().m_newAlias = value;
}
}
}
struct AliasPair
{
AZStd::string m_oldAlias;
AZStd::string m_newAlias;
};
AZStd::vector<AliasPair> m_aliases;
private:
AZStd::string m_parentArrayPath;
};
static void CreateUserCache(const AZ::IO::FixedMaxPath& cacheUserPath, AZ::IO::FileIOBase& fileIoBase)
{
@@ -610,9 +676,8 @@ namespace AzFramework
void Application::SetFileIOAliases()
{
if (m_archiveFileIO)
if (auto fileIoBase = m_archiveFileIO.get(); fileIoBase)
{
auto fileIoBase = m_archiveFileIO.get();
// Set up the default file aliases based on the settings registry
fileIoBase->SetAlias("@engroot@", GetEngineRoot());
fileIoBase->SetAlias("@projectroot@", GetEngineRoot());
@@ -620,29 +685,20 @@ namespace AzFramework
{
AZ::IO::FixedMaxPath pathAliases;
if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder))
{
fileIoBase->SetAlias("@projectcache@", pathAliases.c_str());
}
pathAliases.clear();
if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder))
{
fileIoBase->SetAlias("@assets@", pathAliases.c_str());
fileIoBase->SetAlias("@projectplatformcache@", pathAliases.c_str());
fileIoBase->SetAlias("@root@", pathAliases.c_str()); // Deprecated Use @projectplatformcache@
fileIoBase->SetAlias("@products@", pathAliases.c_str());
}
pathAliases.clear();
if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
{
fileIoBase->SetAlias("@engroot@", pathAliases.c_str());
fileIoBase->SetAlias("@devroot@", pathAliases.c_str()); // Deprecated - Use @engroot@
}
pathAliases.clear();
if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath))
{
fileIoBase->SetAlias("@devassets@", pathAliases.c_str()); // Deprecated - Use @projectsourceassets@
fileIoBase->SetAlias("@projectroot@", pathAliases.c_str());
fileIoBase->SetAlias("@projectsourceassets@", (pathAliases / "Assets").c_str());
}
}
@@ -663,6 +719,15 @@ namespace AzFramework
}
fileIoBase->SetAlias("@log@", projectLogPath.c_str());
fileIoBase->CreatePath(projectLogPath.c_str());
DeprecatedAliasesKeyVisitor visitor;
if (m_settingsRegistry->Visit(visitor, ApplicationInternal::DeprecatedFileIOAliasesRoot))
{
for (const auto& [oldAlias, newAlias] : visitor.m_aliases)
{
fileIoBase->SetDeprecatedAlias(oldAlias, newAlias);
}
}
}
}
@@ -1121,7 +1121,7 @@ namespace AZ::IO
if (AZ::IO::FixedMaxPath pathBindRoot; !AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pathBindRoot, szBindRoot))
{
AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pathBindRoot, "@assets@");
AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pathBindRoot, "@products@");
desc.m_pathBindRoot = pathBindRoot.LexicallyNormal().String();
}
else
@@ -1807,9 +1807,9 @@ namespace AZ::IO
if (m_eRecordFileOpenList != IArchive::RFOM_Disabled)
{
// we only want to record ASSET access
// assets are identified as files that are relative to the resolved @assets@ alias path
// assets are identified as files that are relative to the resolved @products@ alias path
auto fileIoBase = AZ::IO::FileIOBase::GetInstance();
const char* aliasValue = fileIoBase->GetAlias("@assets@");
const char* aliasValue = fileIoBase->GetAlias("@products@");
if (AZ::IO::FixedMaxPath resolvedFilePath;
fileIoBase->ResolvePath(resolvedFilePath, szFilename)
@@ -546,6 +546,16 @@ namespace AZ::IO
realUnderlyingFileIO->GetAlias(alias);
}
void ArchiveFileIO::SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias)
{
FileIOBase* realUnderlyingFileIO = FileIOBase::GetDirectInstance();
if (!realUnderlyingFileIO)
{
return;
}
realUnderlyingFileIO->SetDeprecatedAlias(oldAlias, newAlias);
}
AZStd::optional<AZ::u64> ArchiveFileIO::ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const
{
if ((!inOutBuffer) || (bufferLength == 0))
@@ -63,6 +63,7 @@ namespace AZ::IO
IO::Result FindFiles(const char* filePath, const char* filter, FindFilesCallbackType callback) override;
void SetAlias(const char* alias, const char* path) override;
void ClearAlias(const char* alias) override;
void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override;
AZStd::optional<AZ::u64> ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override;
bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override;
using FileIOBase::ConvertToAlias;
@@ -186,8 +186,8 @@ namespace AZ::IO
{
// filter out the stuff which does not match.
// the problem here is that szDir might be something like "@assets@/levels/*"
// but our archive might be mounted at the root, or at some other folder at like "@assets@" or "@assets@/levels/mylevel"
// the problem here is that szDir might be something like "@products@/levels/*"
// but our archive might be mounted at the root, or at some other folder at like "@products@" or "@products@/levels/mylevel"
// so there's really no way to filter out opening the pack and looking at the files inside.
// however, the bind root is not part of the inner zip entry name either
// and the ZipDir::FindFile actually expects just the chopped off piece.
@@ -202,22 +202,22 @@ namespace AZ::IO
// Example:
// "@assets@\\levels\\*" <--- szDir
// "@assets@\\" <--- mount point
// "@products@\\levels\\*" <--- szDir
// "@products@\\" <--- mount point
// ~~~~~~~~~~~ Common part
// "levels\\*" <---- remainder that is not in common
// "" <--- mount point remainder. In this case, we should scan the contents of the pak for the remainder
// Example:
// "@assets@\\levels\\*" <--- szDir
// "@assets@\\levels\\mylevel\\" <--- mount point (its level.pak)
// "@products@\\levels\\*" <--- szDir
// "@products@\\levels\\mylevel\\" <--- mount point (its level.pak)
// ~~~~~~~~~~~~~~~~~~ common part
// "*" <---- remainder that is not in common
// "mylevel\\" <--- mount point remainder.
// example:
// "@assets@\\levels\\otherlevel\\*" <--- szDir
// "@assets@\\levels\\mylevel\\" <--- mount point (its level.pak)
// "@products@\\levels\\otherlevel\\*" <--- szDir
// "@products@\\levels\\mylevel\\" <--- mount point (its level.pak)
// "otherlevel\\*" <---- remainder
// "mylevel\\" <--- mount point remainder.
@@ -249,7 +249,7 @@ namespace AZ::IO
// which means we may search inside the pack.
ScanInZip(it->pZip.get(), sourcePathRemainder.Native());
}
}
}
@@ -94,7 +94,7 @@ namespace AZ::IO::Internal
}
AZStd::smatch matches;
const AZStd::regex lodRegex("@assets@\\\\(.*)_lod[0-9]+(\\.cgfm?)");
const AZStd::regex lodRegex("@products@\\\\(.*)_lod[0-9]+(\\.cgfm?)");
if (!AZStd::regex_match(szPath, matches, lodRegex) || matches.size() != 3)
{
// The current file is not a valid LOD file
@@ -725,7 +725,7 @@ namespace AzFramework
if (!info.m_relativePath.empty())
{
const char* devAssetRoot = fileIO->GetAlias("@devassets@");
const char* devAssetRoot = fileIO->GetAlias("@projectroot@");
if (devAssetRoot)
{
AZ::Data::AssetStreamInfo streamInfo;
@@ -61,7 +61,7 @@ namespace AzFramework
AZ::IO::Path& gemAbsPath = gemInfo.m_absoluteSourcePaths.emplace_back(value);
// Resolve any file aliases first - Do not use ResolvePath() as that assumes
// any relative path is underneath the @assets@ alias
// any relative path is underneath the @products@ alias
if (auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); fileIoBase != nullptr)
{
AZ::IO::FixedMaxPath replacedAliasPath;
@@ -12,10 +12,12 @@
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Casting/lossy_cast.h>
#include <AzCore/std/containers/fixed_unordered_set.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/std/string/string_view.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h>
#include <cctype>
namespace AZ
@@ -292,7 +294,7 @@ namespace AZ
void LocalFileIO::CheckInvalidWrite([[maybe_unused]] const char* path)
{
#if defined(AZ_ENABLE_TRACING)
const char* assetAliasPath = GetAlias("@assets@");
const char* assetAliasPath = GetAlias("@products@");
if (path && assetAliasPath)
{
const AZ::IO::PathView pathView(path);
@@ -478,17 +480,15 @@ namespace AZ
return false;
}
if (IsAbsolutePath(path))
if (AZ::IO::PathView(path).HasRootPath())
{
size_t pathLen = strlen(path);
if (pathLen + 1 < resolvedPathSize)
{
azstrncpy(resolvedPath, resolvedPathSize, path, pathLen + 1);
//see if the absolute path uses @assets@ or @root@, if it does lowercase the relative part
[[maybe_unused]] bool lowercasePath = LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@assets@"))
|| LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@root@"))
|| LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@projectplatformcache@"));
//see if the absolute path matches the resolved value of @products@, if it does lowercase the relative part
LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@products@"));
ToUnixSlashes(resolvedPath, resolvedPathSize);
return true;
@@ -499,34 +499,39 @@ namespace AZ
}
}
char rootedPathBuffer[AZ_MAX_PATH_LEN] = {0};
constexpr AZStd::string_view productAssetAlias = "@products@";
// Add plus one for the path separator: <alias>/<path>
constexpr size_t MaxPathSizeWithProductAssetAlias = AZ::IO::MaxPathLength + productAssetAlias.size() + 1;
using RootedPathString = AZStd::fixed_string<MaxPathSizeWithProductAssetAlias>;
RootedPathString rootedPathBuffer;
const char* rootedPath = path;
// if the path does not begin with an alias, then it is assumed to begin with @assets@
// if the path does not begin with an alias, then it is assumed to begin with @products@
if (path[0] != '@')
{
if (GetAlias("@assets@"))
if (GetAlias("@products@"))
{
const int rootLength = 9;// strlen("@assets@/")
azstrncpy(rootedPathBuffer, AZ_MAX_PATH_LEN, "@assets@/", rootLength);
size_t pathLen = strlen(path);
size_t rootedPathBufferlength = rootLength + pathLen + 1;// +1 for null terminator
if (rootedPathBufferlength > resolvedPathSize)
if (const size_t requiredSize = productAssetAlias.size() + strlen(path) + 1;
requiredSize > rootedPathBuffer.capacity())
{
AZ_Assert(rootedPathBufferlength < resolvedPathSize, "Constructed path length is wrong:%s", rootedPathBuffer);//path constructed is wrong
size_t remainingSize = resolvedPathSize - rootLength - 1;// - 1 for null terminator
azstrncpy(rootedPathBuffer + rootLength, AZ_MAX_PATH_LEN, path, remainingSize);
rootedPathBuffer[resolvedPathSize - 1] = '\0';
AZ_Error("FileIO", false, "Prepending the %.*s alias to the input path results in a path longer than the"
" AZ::IO::MaxPathLength + the alias size of %zu. The size of the potential failed path is %zu",
AZ_STRING_ARG(productAssetAlias), rootedPathBuffer.capacity(), requiredSize)
}
else
{
azstrncpy(rootedPathBuffer + rootLength, AZ_MAX_PATH_LEN - rootLength, path, pathLen + 1);
rootedPathBuffer = RootedPathString::format("%.*s/%s", AZ_STRING_ARG(productAssetAlias), path);
}
}
else
{
ConvertToAbsolutePath(path, rootedPathBuffer, AZ_MAX_PATH_LEN);
if (ConvertToAbsolutePath(path, rootedPathBuffer.data(), rootedPathBuffer.capacity()))
{
// Recalculate the internal string length
rootedPathBuffer.resize_no_construct(AZStd::char_traits<char>::length(rootedPathBuffer.data()));
}
}
rootedPath = rootedPathBuffer;
rootedPath = rootedPathBuffer.c_str();
}
if (ResolveAliases(rootedPath, resolvedPath, resolvedPathSize))
@@ -561,11 +566,57 @@ namespace AZ
const char* LocalFileIO::GetAlias(const char* key) const
{
const auto it = m_aliases.find(key);
if (it != m_aliases.end())
if (const auto it = m_aliases.find(key); it != m_aliases.end())
{
return it->second.c_str();
}
else if (const auto deprecatedIt = m_deprecatedAliases.find(key);
deprecatedIt != m_deprecatedAliases.end())
{
AZ_Error("FileIO", false, R"(Alias "%s" is deprecated. Please use alias "%s" instead)",
key, deprecatedIt->second.c_str());
AZStd::string_view aliasValue = deprecatedIt->second;
// Contains the list of aliases resolved so far
// If max_size is hit, than an error is logged and nullptr is returned
using VisitedAliasSet = AZStd::fixed_unordered_set<AZStd::string_view, 8, 8>;
VisitedAliasSet visitedAliasSet;
while (aliasValue.starts_with("@"))
{
if (visitedAliasSet.contains(aliasValue))
{
AZ_Error("FileIO", false, "Cycle found with for alias %.*s when trying to resolve deprecated alias %s",
AZ_STRING_ARG(aliasValue), key);
return nullptr;
}
if(visitedAliasSet.size() == visitedAliasSet.max_size())
{
AZ_Error("FileIO", false, "Unable to resolve path to deprecated alias %s within %zu steps",
key, visitedAliasSet.max_size());
return nullptr;
}
// Add the current alias value to the visited set
visitedAliasSet.emplace(aliasValue);
// Check if the alias value corresponds to another alias
if (auto resolvedIter = m_aliases.find(aliasValue); resolvedIter != m_aliases.end())
{
aliasValue = resolvedIter->second;
}
else if (resolvedIter = m_deprecatedAliases.find(aliasValue);
resolvedIter != m_deprecatedAliases.end())
{
aliasValue = resolvedIter->second;
}
else
{
return nullptr;
}
}
return aliasValue.data();
}
return nullptr;
}
@@ -574,6 +625,11 @@ namespace AZ
m_aliases.erase(key);
}
void LocalFileIO::SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias)
{
m_deprecatedAliases[oldAlias] = newAlias;
}
AZStd::optional<AZ::u64> LocalFileIO::ConvertToAliasBuffer(char* outBuffer, AZ::u64 outBufferLength, AZStd::string_view inBuffer) const
{
size_t longestMatch = 0;
@@ -675,7 +731,9 @@ namespace AZ
: string_view_pair{};
size_t requiredResolvedPathSize = pathView.size() - aliasKey.size() + aliasValue.size() + 1;
AZ_Assert(path != resolvedPath && resolvedPathSize >= requiredResolvedPathSize, "Resolved path is incorrect");
AZ_Assert(path != resolvedPath, "ResolveAliases does not support inplace update of the path");
AZ_Assert(resolvedPathSize >= requiredResolvedPathSize, "Resolved path size %llu not large enough. It needs to be %zu",
resolvedPathSize, requiredResolvedPathSize);
// we assert above, but we also need to properly handle the case when the resolvedPath buffer size
// is too small to copy the source into.
if (path == resolvedPath || (resolvedPathSize < requiredResolvedPathSize))
@@ -699,13 +757,9 @@ namespace AZ
resolvedPath[resolvedPathLen] = '\0';
// If the path started with one of the "asset cache" path aliases, lowercase the path
const char* assetAliasPath = GetAlias("@assets@");
const char* rootAliasPath = GetAlias("@root@");
const char* projectPlatformCacheAliasPath = GetAlias("@projectplatformcache@");
const char* projectPlatformCacheAliasPath = GetAlias("@products@");
const bool lowercasePath = (assetAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, assetAliasPath)) ||
(rootAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, rootAliasPath)) ||
(projectPlatformCacheAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, projectPlatformCacheAliasPath));
const bool lowercasePath = projectPlatformCacheAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, projectPlatformCacheAliasPath);
if (lowercasePath)
{
@@ -822,5 +876,10 @@ namespace AZ
return pathStr + "/";
}
bool LocalFileIO::ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const
{
return AZ::Utils::ConvertToAbsolutePath(path, absolutePath, maxLength);
}
} // namespace IO
} // namespace AZ
@@ -61,6 +61,8 @@ namespace AZ
void SetAlias(const char* alias, const char* path) override;
void ClearAlias(const char* alias) override;
const char* GetAlias(const char* alias) const override;
void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override;
AZStd::optional<AZ::u64> ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override;
bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override;
using FileIOBase::ConvertToAlias;
@@ -71,7 +73,7 @@ namespace AZ
bool GetFilename(HandleType fileHandle, char* filename, AZ::u64 filenameSize) const override;
bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const;
private:
SystemFile* GetFilePointerFromHandle(HandleType fileHandle);
@@ -79,7 +81,6 @@ namespace AZ
AZStd::optional<AZ::u64> ConvertToAliasBuffer(char* outBuffer, AZ::u64 outBufferLength, AZStd::string_view inBuffer) const;
bool ResolveAliases(const char* path, char* resolvedPath, AZ::u64 resolvedPathSize) const;
bool IsAbsolutePath(const char* path) const;
bool LowerIfBeginsWith(char* inOutBuffer, AZ::u64 bufferLen, const char* alias) const;
@@ -91,6 +92,7 @@ namespace AZ
AZStd::atomic<HandleType> m_nextHandle;
AZStd::unordered_map<HandleType, SystemFile> m_openFiles;
AZStd::unordered_map<AZStd::string, AZStd::string> m_aliases;
AZStd::unordered_map<AZStd::string, AZStd::string> m_deprecatedAliases;
void CheckInvalidWrite(const char* path);
};
@@ -49,14 +49,14 @@ namespace AZ
s_IOLog.append(m_name);
s_IOLog.append("\r\n");
}
void Append(const char* line)
{
s_IOLog.append(AZStd::string::format("%u ", m_fileOperation));
s_IOLog.append(line);
s_IOLog.append("\r\n");
}
~LogCall()
{
s_IOLog.append(AZStd::string::format("%u End ", m_fileOperation));
@@ -251,7 +251,7 @@ namespace AZ
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Size(filePath=%s) size request failed. return Error", filePath).c_str());
return ResultCode::Error;
}
size = response.m_size;
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Size(filePath=%s) size=%u. return Success", filePath, size).c_str());
return ResultCode::Success;
@@ -793,6 +793,12 @@ namespace AZ
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::ClearAlias(alias=%s)", alias?alias:"nullptr").c_str());
}
void NetworkFileIO::SetDeprecatedAlias([[maybe_unused]] AZStd::string_view oldAlias, [[maybe_unused]] AZStd::string_view newAlias)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::SetDeprecatedAlias(oldAlias=%.*s, newAlias=%.*s)",
AZ_STRING_ARG(oldAlias), AZ_STRING_ARG(newAlias)).c_str());
}
AZStd::optional<AZ::u64> NetworkFileIO::ConvertToAlias(char* inOutBuffer, [[maybe_unused]] AZ::u64 bufferLength) const
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::ConvertToAlias(inOutBuffer=%s, bufferLength=%u)", inOutBuffer?inOutBuffer:"nullptr", bufferLength).c_str());
@@ -927,7 +933,7 @@ namespace AZ
{
m_cacheLookaheadPos = filePosition - CacheStartFilePosition();
}
void RemoteFileCache::SyncCheck()
{
#ifdef REMOTEFILEIO_SYNC_CHECK
@@ -955,7 +961,7 @@ namespace AZ
AZ_TracePrintf(RemoteFileCacheChannel, "RemoteFileCache::SyncCheck(m_fileHandle=%u) tell request failed.", m_fileHandle);
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileCache::SyncCheck(m_fileHandle=%u) tell request failed.", m_fileHandle).c_str());
}
if (responce.m_offset != m_filePosition)
{
AZ_TracePrintf(RemoteFileCacheChannel, "RemoteFileCache::SyncCheck(m_fileHandle=%u) failed!!! m_filePosition=%u tell=%u", m_fileHandle, m_filePosition, responce.m_offset);
@@ -1028,7 +1034,7 @@ namespace AZ
{
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::Close(fileHandle=%u)", fileHandle).c_str());
Result returnValue = NetworkFileIO::Close(fileHandle);
if (returnValue == ResultCode::Success)
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFileCacheGuard);
@@ -1160,7 +1166,7 @@ namespace AZ
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::Read(fileHandle=%u, buffer=OUT, size=%u, failOnFewerThanSizeBytesRead=%s, bytesRead=OUT)", fileHandle, size, failOnFewerThanSizeBytesRead ? "True" : "False").c_str());
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFileCacheGuard);
RemoteFileCache& cache = GetCache(fileHandle);
AZ::u64 remainingBytesToRead = size;
AZ::u64 bytesReadFromCache = 0;
AZ::u64 remainingBytesInCache = cache.RemainingBytes();
@@ -1263,7 +1269,7 @@ namespace AZ
RemoteFileCache& cache = GetCache(fileHandle);
if (cache.m_cacheLookaheadBuffer.size() && cache.RemainingBytes())
{
// find out where we are
// find out where we are
AZ::u64 seekPosition = cache.CacheFilePosition();
// note, seeks are predicted, and do not ask for a response.
@@ -1361,6 +1367,14 @@ namespace AZ
}
}
void RemoteFileIO::SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias)
{
if (m_excludedFileIO)
{
m_excludedFileIO->SetDeprecatedAlias(oldAlias, newAlias);
}
}
AZStd::optional<AZ::u64> RemoteFileIO::ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const
{
return m_excludedFileIO ? m_excludedFileIO->ConvertToAlias(inOutBuffer, bufferLength) : strlen(inOutBuffer);
@@ -102,6 +102,7 @@ namespace AZ
Result FindFiles(const char* filePath, const char* filter, FindFilesCallbackType callback) override;
void SetAlias(const char* alias, const char* path) override;
void ClearAlias(const char* alias) override;
void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override;
AZStd::optional<AZ::u64> ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override;
bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override;
using FileIOBase::ConvertToAlias;
@@ -194,6 +195,7 @@ namespace AZ
void SetAlias(const char* alias, const char* path) override;
const char* GetAlias(const char* alias) const override;
void ClearAlias(const char* alias) override;
void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override;
AZStd::optional<AZ::u64> ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override;
bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override;
using FileIOBase::ConvertToAlias;
@@ -13,7 +13,6 @@
#include <AzCore/Android/Utils.h>
#include <AzCore/IO/IOUtils.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/functional.h>
#include <android/api-level.h>
@@ -42,10 +41,10 @@ namespace AZ
{
Result LocalFileIO::Copy(const char* sourceFilePath, const char* destinationFilePath)
{
char resolvedSourcePath[AZ_MAX_PATH_LEN];
char resolvedDestPath[AZ_MAX_PATH_LEN];
ResolvePath(sourceFilePath, resolvedSourcePath, AZ_MAX_PATH_LEN);
ResolvePath(destinationFilePath, resolvedDestPath, AZ_MAX_PATH_LEN);
char resolvedSourcePath[AZ::IO::MaxPathLength];
char resolvedDestPath[AZ::IO::MaxPathLength];
ResolvePath(sourceFilePath, resolvedSourcePath, AZ::IO::MaxPathLength);
ResolvePath(destinationFilePath, resolvedDestPath, AZ::IO::MaxPathLength);
if (AZ::Android::Utils::IsApkPath(sourceFilePath) || AZ::Android::Utils::IsApkPath(destinationFilePath))
{
@@ -77,18 +76,17 @@ namespace AZ
{
ANDROID_IO_PROFILE_SECTION_ARGS("FindFiles:%s", filePath);
char resolvedPath[AZ_MAX_PATH_LEN];
ResolvePath(filePath, resolvedPath, AZ_MAX_PATH_LEN);
char resolvedPath[AZ::IO::MaxPathLength];
ResolvePath(filePath, resolvedPath, AZ::IO::MaxPathLength);
AZStd::string pathWithoutSlash = RemoveTrailingSlash(resolvedPath);
bool isInAPK = AZ::Android::Utils::IsApkPath(pathWithoutSlash.c_str());
AZ::IO::FixedMaxPath tempBuffer;
if (isInAPK)
{
AZ::IO::FixedMaxPath strippedPath = AZ::Android::Utils::StripApkPrefix(pathWithoutSlash.c_str());
char tempBuffer[AZ_MAX_PATH_LEN] = {0};
AZ::Android::APKFileHandler::ParseDirectory(strippedPath.c_str(), [&](const char* name)
{
AZStd::string_view filenameView = name;
@@ -98,10 +96,9 @@ namespace AZ
AZStd::string foundFilePath = CheckForTrailingSlash(resolvedPath);
foundFilePath += name;
// if aliased, de-alias!
azstrcpy(tempBuffer, AZ_MAX_PATH_LEN, foundFilePath.c_str());
ConvertToAlias(tempBuffer, AZ_MAX_PATH_LEN);
ConvertToAlias(tempBuffer, AZ::IO::PathView{ foundFilePath });
if (!callback(tempBuffer))
if (!callback(tempBuffer.c_str()))
{
return false;
}
@@ -115,10 +112,6 @@ namespace AZ
if (dir != nullptr)
{
// because the absolute path might actually be SHORTER than the alias ("c:/r/dev" -> "@devroot@"), we need to
// use a static buffer here.
char tempBuffer[AZ_MAX_PATH_LEN];
// clear the errno state so we can distinguish between errors and end of stream
errno = 0;
struct dirent* entry = readdir(dir);
@@ -133,10 +126,9 @@ namespace AZ
AZStd::string foundFilePath = CheckForTrailingSlash(resolvedPath);
foundFilePath += entry->d_name;
// if aliased, de-alias!
azstrcpy(tempBuffer, AZ_MAX_PATH_LEN, foundFilePath.c_str());
ConvertToAlias(tempBuffer, AZ_MAX_PATH_LEN);
ConvertToAlias(tempBuffer, AZ::IO::PathView{ foundFilePath });
if (!callback(tempBuffer))
if (!callback(tempBuffer.c_str()))
{
break;
}
@@ -163,8 +155,8 @@ namespace AZ
Result LocalFileIO::CreatePath(const char* filePath)
{
char resolvedPath[AZ_MAX_PATH_LEN];
ResolvePath(filePath, resolvedPath, AZ_MAX_PATH_LEN);
char resolvedPath[AZ::IO::MaxPathLength];
ResolvePath(filePath, resolvedPath, AZ::IO::MaxPathLength);
if (AZ::Android::Utils::IsApkPath(resolvedPath))
{
@@ -201,33 +193,5 @@ namespace AZ
mkdir(pathBuffer.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
return IsDirectory(resolvedPath) ? ResultCode::Success : ResultCode::Error;
}
bool LocalFileIO::IsAbsolutePath(const char* path) const
{
return path && path[0] == '/';
}
bool LocalFileIO::ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const
{
if (AZ::Android::Utils::IsApkPath(path))
{
azstrncpy(absolutePath, maxLength, path, maxLength);
return true;
}
AZ_Assert(maxLength >= AZ_MAX_PATH_LEN, "Path length is larger than AZ_MAX_PATH_LEN");
if (!IsAbsolutePath(path))
{
// note that realpath fails if the path does not exist and actually changes the return value
// to be the actual place that FAILED, which we don't want.
// if we fail, we'd prefer to fall through and at least use the original path.
const char* result = realpath(path, absolutePath);
if (result)
{
return true;
}
}
azstrcpy(absolutePath, maxLength, path);
return IsAbsolutePath(absolutePath);
}
} // namespace IO
}//namespace AZ
@@ -10,7 +10,7 @@
#include <dirent.h>
#include <unistd.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/functional.h>
namespace AZ
@@ -19,11 +19,11 @@ namespace AZ
{
Result LocalFileIO::Copy(const char* sourceFilePath, const char* destinationFilePath)
{
char resolvedSourceFilePath[AZ_MAX_PATH_LEN] = {0};
ResolvePath(sourceFilePath, resolvedSourceFilePath, AZ_MAX_PATH_LEN);
char resolvedSourceFilePath[AZ::IO::MaxPathLength] = {0};
ResolvePath(sourceFilePath, resolvedSourceFilePath, AZ::IO::MaxPathLength);
char resolvedDestinationFilePath[AZ_MAX_PATH_LEN] = {0};
ResolvePath(destinationFilePath, resolvedDestinationFilePath, AZ_MAX_PATH_LEN);
char resolvedDestinationFilePath[AZ::IO::MaxPathLength] = {0};
ResolvePath(destinationFilePath, resolvedDestinationFilePath, AZ::IO::MaxPathLength);
// Use standard C++ method of file copy.
{
@@ -45,17 +45,15 @@ namespace AZ
Result LocalFileIO::FindFiles(const char* filePath, const char* filter, FindFilesCallbackType callback)
{
char resolvedPath[AZ_MAX_PATH_LEN] = {0};
ResolvePath(filePath, resolvedPath, AZ_MAX_PATH_LEN);
char resolvedPath[AZ::IO::MaxPathLength] = {0};
ResolvePath(filePath, resolvedPath, AZ::IO::MaxPathLength);
AZStd::string withoutSlash = RemoveTrailingSlash(resolvedPath);
DIR* dir = opendir(withoutSlash.c_str());
if (dir != nullptr)
{
// because the absolute path might actually be SHORTER than the alias ("c:/r/dev" -> "@devroot@"), we need to
// use a static buffer here.
char tempBuffer[AZ_MAX_PATH_LEN];
AZ::IO::FixedMaxPath tempBuffer;
errno = 0;
struct dirent* entry = readdir(dir);
@@ -70,10 +68,9 @@ namespace AZ
AZStd::string foundFilePath = CheckForTrailingSlash(resolvedPath);
foundFilePath += entry->d_name;
// if aliased, dealias!
azstrcpy(tempBuffer, AZ_MAX_PATH_LEN, foundFilePath.c_str());
ConvertToAlias(tempBuffer, AZ_MAX_PATH_LEN);
ConvertToAlias(tempBuffer, AZ::IO::PathView{ foundFilePath });
if (!callback(tempBuffer))
if (!callback(tempBuffer.c_str()))
{
break;
}
@@ -92,8 +89,8 @@ namespace AZ
Result LocalFileIO::CreatePath(const char* filePath)
{
char resolvedPath[AZ_MAX_PATH_LEN] = {0};
ResolvePath(filePath, resolvedPath, AZ_MAX_PATH_LEN);
char resolvedPath[AZ::IO::MaxPathLength] = {0};
ResolvePath(filePath, resolvedPath, AZ::IO::MaxPathLength);
// create all paths up to that directory.
// its not an error if the path exists.
@@ -125,28 +122,5 @@ namespace AZ
mkdir(buf.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
return IsDirectory(resolvedPath) ? ResultCode::Success : ResultCode::Error;
}
bool LocalFileIO::IsAbsolutePath(const char* path) const
{
return path && path[0] == '/';
}
bool LocalFileIO::ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const
{
AZ_Assert(maxLength >= AZ_MAX_PATH_LEN, "Path length is larger than AZ_MAX_PATH_LEN");
if (!IsAbsolutePath(path))
{
// note that realpath fails if the path does not exist and actually changes the return value
// to be the actual place that FAILED, which we don't want.
// if we fail, we'd prefer to fall through and at least use the original path.
const char* result = realpath(path, absolutePath);
if (result)
{
return true;
}
}
azstrcpy(absolutePath, maxLength, path);
return IsAbsolutePath(absolutePath);
}
} // namespace IO
} // namespace AZ
@@ -47,7 +47,7 @@ namespace AZ
if (hFind != INVALID_HANDLE_VALUE)
{
// because the absolute path might actually be SHORTER than the alias ("c:/r/dev" -> "@devroot@"), we need to
// because the absolute path might actually be SHORTER than the alias ("D:/o3de" -> "@engroot@"), we need to
// use a static buffer here.
char tempBuffer[AZ_MAX_PATH_LEN];
do
@@ -133,36 +133,5 @@ namespace AZ
return SystemFile::CreateDir(buf.c_str()) ? ResultCode::Success : ResultCode::Error;
}
bool LocalFileIO::ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const
{
char* result = _fullpath(absolutePath, path, maxLength);
size_t len = ::strlen(absolutePath);
if (len > 0)
{
// strip trailing slash
if (absolutePath[len - 1] == '/' || absolutePath[len - 1] == '\\')
{
absolutePath[len - 1] = 0;
}
// For some reason, at least on windows, _fullpath returns a lowercase drive letter even though other systems like Qt, use upper case.
if (len > 2)
{
if (absolutePath[1] == ':')
{
absolutePath[0] = (char)toupper(absolutePath[0]);
}
}
}
return result != nullptr;
}
bool LocalFileIO::IsAbsolutePath(const char* path) const
{
char drive[16] = { 0 };
_splitpath_s(path, drive, 16, nullptr, 0, nullptr, 0, nullptr, 0);
return strlen(drive) > 0;
}
} // namespace IO
}//namespace AZ
@@ -7,26 +7,24 @@
*/
#include <AzCore/PlatformIncl.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/IO/SystemFile.h>
namespace AZ
namespace AZ::IO
{
namespace IO
Result LocalFileIO::Copy(const char* sourceFilePath, const char* destinationFilePath)
{
AZ::IO::FixedMaxPath resolvedSourcePath;
ResolvePath(resolvedSourcePath, sourceFilePath);
AZ::IO::FixedMaxPath resolvedDestPath;
ResolvePath(resolvedDestPath, destinationFilePath);
Result LocalFileIO::Copy(const char* sourceFilePath, const char* destinationFilePath)
{
char resolvedSourcePath[AZ_MAX_PATH_LEN];
ResolvePath(sourceFilePath, resolvedSourcePath, AZ_MAX_PATH_LEN);
char resolvedDestPath[AZ_MAX_PATH_LEN];
ResolvePath(destinationFilePath, resolvedDestPath, AZ_MAX_PATH_LEN);
AZStd::fixed_wstring<AZ::IO::MaxPathLength> resolvedSourcePathW;
AZStd::fixed_wstring<AZ::IO::MaxPathLength> resolvedDestPathW;
AZStd::to_wstring(resolvedSourcePathW, resolvedSourcePath.Native());
AZStd::to_wstring(resolvedDestPathW, resolvedDestPath.Native());
if (::CopyFileA(resolvedSourcePath, resolvedDestPath, false) == 0)
{
return ResultCode::Error;
}
return ResultCode::Success;
}
} // namespace IO
}//namespace AZ
return ::CopyFileW(resolvedSourcePathW.c_str(), resolvedDestPathW.c_str(), false) != 0 ? ResultCode::Success : ResultCode::Error;
}
}//namespace AZ::IO
@@ -26,7 +26,7 @@ protected:
}
if (auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); fileIoBase != nullptr)
{
fileIoBase->SetAlias("@assets@", m_tempDirectory.GetDirectory());
fileIoBase->SetAlias("@products@", m_tempDirectory.GetDirectory());
}
}
@@ -50,7 +50,7 @@ namespace UnitTest
m_application->Start({});
// 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
// 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);
}
@@ -262,7 +262,7 @@ namespace UnitTest
pArchive.reset();
EXPECT_TRUE(IsPackValid(testArchivePath_withSubfolders.c_str()));
EXPECT_TRUE(archive->OpenPack("@assets@", testArchivePath_withSubfolders.c_str()));
EXPECT_TRUE(archive->OpenPack("@products@", testArchivePath_withSubfolders.c_str()));
EXPECT_TRUE(archive->IsFileExist(fileInArchiveFile));
}
@@ -353,7 +353,7 @@ namespace UnitTest
// and be able to IMMEDIATELY
// * read the file in the subfolder
// * enumerate the folders (including that subfolder) even though they are 'virtual', not real folders on physical media
// * all of the above even though the mount point for the archive is @assets@ wheras the physical pack lives in @usercache@
// * all of the above even though the mount point for the archive is @products@ wheras the physical pack lives in @usercache@
// finally, we're going to repeat the above test but with files mounted with subfolders
// so for example, the pack will contain levelinfo.xml at the root of it
// but it will be mounted at a subfolder (levels/mylevel).
@@ -388,7 +388,7 @@ namespace UnitTest
pArchive.reset();
EXPECT_TRUE(IsPackValid(testArchivePath_withSubfolders));
EXPECT_TRUE(archive->OpenPack("@assets@", testArchivePath_withSubfolders));
EXPECT_TRUE(archive->OpenPack("@products@", testArchivePath_withSubfolders));
// ---- BARRAGE OF TESTS
EXPECT_TRUE(archive->IsFileExist("levels\\mylevel\\levelinfo.xml"));
EXPECT_TRUE(archive->IsFileExist("levels//mylevel//levelinfo.xml"));
@@ -484,7 +484,7 @@ namespace UnitTest
pArchive.reset();
EXPECT_TRUE(IsPackValid(testArchivePath_withMountPoint));
EXPECT_TRUE(archive->OpenPack("@assets@\\uniquename\\mylevel2", testArchivePath_withMountPoint));
EXPECT_TRUE(archive->OpenPack("@products@\\uniquename\\mylevel2", testArchivePath_withMountPoint));
// ---- BARRAGE OF TESTS
EXPECT_TRUE(archive->IsFileExist("uniquename\\mylevel2\\levelinfo.xml"));
@@ -543,7 +543,7 @@ namespace UnitTest
archive->ClosePack(testArchivePath_withMountPoint);
// --- test to make sure that when you iterate only the first component is found, so bury it deep and ask for the root
EXPECT_TRUE(archive->OpenPack("@assets@\\uniquename\\mylevel2\\mylevel3\\mylevel4", testArchivePath_withMountPoint));
EXPECT_TRUE(archive->OpenPack("@products@\\uniquename\\mylevel2\\mylevel3\\mylevel4", testArchivePath_withMountPoint));
found_mylevel_folder = false;
handle = archive->FindFirst("uniquename\\*");
@@ -574,9 +574,9 @@ namespace UnitTest
found_mylevel_folder = false;
// now make sure no red herrings appear
// for example, if a file is mounted at "@assets@\\uniquename\\mylevel2\\mylevel3\\mylevel4"
// and the file "@assets@\\somethingelse" is requested it should not be found
// in addition if the file "@assets@\\uniquename\\mylevel3" is requested it should not be found
// for example, if a file is mounted at "@products@\\uniquename\\mylevel2\\mylevel3\\mylevel4"
// and the file "@products@\\somethingelse" is requested it should not be found
// in addition if the file "@products@\\uniquename\\mylevel3" is requested it should not be found
handle = archive->FindFirst("somethingelse\\*");
EXPECT_FALSE(static_cast<bool>(handle));
@@ -610,7 +610,7 @@ namespace UnitTest
cpfio.Remove(genericArchiveFileName);
// create the asset alias directory
cpfio.CreatePath("@assets@");
cpfio.CreatePath("@products@");
// create generic file
@@ -635,11 +635,11 @@ namespace UnitTest
pArchive.reset();
EXPECT_TRUE(IsPackValid(genericArchiveFileName));
EXPECT_TRUE(archive->OpenPack("@assets@", genericArchiveFileName));
EXPECT_TRUE(archive->OpenPack("@products@", genericArchiveFileName));
// ---- BARRAGE OF TESTS
EXPECT_TRUE(cpfio.Exists("testfile.xml"));
EXPECT_TRUE(cpfio.Exists("@assets@/testfile.xml")); // this should be hte same file
EXPECT_TRUE(cpfio.Exists("@products@/testfile.xml")); // this should be hte same file
EXPECT_TRUE(!cpfio.Exists("@log@/testfile.xml"));
EXPECT_TRUE(!cpfio.Exists("@usercache@/testfile.xml"));
EXPECT_TRUE(cpfio.Exists("@log@/unittesttemp/realfileforunittest.xml"));
@@ -685,9 +685,9 @@ namespace UnitTest
EXPECT_EQ(ResultCode::Success, cpfio.Close(normalFileHandle));
EXPECT_TRUE(!cpfio.IsDirectory("testfile.xml"));
EXPECT_TRUE(cpfio.IsDirectory("@assets@"));
EXPECT_TRUE(cpfio.IsDirectory("@products@"));
EXPECT_TRUE(cpfio.IsReadOnly("testfile.xml"));
EXPECT_TRUE(cpfio.IsReadOnly("@assets@/testfile.xml"));
EXPECT_TRUE(cpfio.IsReadOnly("@products@/testfile.xml"));
EXPECT_TRUE(!cpfio.IsReadOnly("@log@/unittesttemp/realfileforunittest.xml"));
@@ -714,10 +714,10 @@ namespace UnitTest
// find files test.
AZ::IO::FixedMaxPath resolvedTestFilePath;
EXPECT_TRUE(cpfio.ResolvePath(resolvedTestFilePath, AZ::IO::PathView("@assets@/testfile.xml")));
EXPECT_TRUE(cpfio.ResolvePath(resolvedTestFilePath, AZ::IO::PathView("@products@/testfile.xml")));
bool foundIt = false;
// note that this file exists only in the archive.
cpfio.FindFiles("@assets@", "*.xml", [&foundIt, &cpfio, &resolvedTestFilePath](const char* foundName)
cpfio.FindFiles("@products@", "*.xml", [&foundIt, &cpfio, &resolvedTestFilePath](const char* foundName)
{
AZ::IO::FixedMaxPath resolvedFoundPath;
EXPECT_TRUE(cpfio.ResolvePath(resolvedFoundPath, AZ::IO::PathView(foundName)));
@@ -734,10 +734,10 @@ namespace UnitTest
// The following test is disabled because it will trigger an AZ_ERROR which will affect the outcome of this entire test
// EXPECT_NE(ResultCode::Success, cpfio.Remove("@assets@/testfile.xml")); // may not delete archive files
// EXPECT_NE(ResultCode::Success, cpfio.Remove("@products@/testfile.xml")); // may not delete archive files
// make sure it works with and without alias:
EXPECT_TRUE(cpfio.Exists("@assets@/testfile.xml"));
EXPECT_TRUE(cpfio.Exists("@products@/testfile.xml"));
EXPECT_TRUE(cpfio.Exists("testfile.xml"));
EXPECT_TRUE(cpfio.Exists("@log@/unittesttemp/realfileforunittest.xml"));
@@ -788,22 +788,22 @@ namespace UnitTest
EXPECT_TRUE(archive->ClosePack(realNameBuf));
// change its actual location:
EXPECT_TRUE(archive->OpenPack("@assets@", realNameBuf));
EXPECT_TRUE(archive->IsFileExist("@assets@/foundit.dat"));
EXPECT_TRUE(archive->OpenPack("@products@", realNameBuf));
EXPECT_TRUE(archive->IsFileExist("@products@/foundit.dat"));
EXPECT_FALSE(archive->IsFileExist("@usercache@/foundit.dat")); // do not find it in the previous location!
EXPECT_FALSE(archive->IsFileExist("@assets@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
EXPECT_FALSE(archive->IsFileExist("@assets@/notfoundit.dat"));
EXPECT_FALSE(archive->IsFileExist("@products@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
EXPECT_FALSE(archive->IsFileExist("@products@/notfoundit.dat"));
EXPECT_TRUE(archive->ClosePack(realNameBuf));
// try sub-folders
EXPECT_TRUE(archive->OpenPack("@assets@/mystuff", realNameBuf));
EXPECT_TRUE(archive->IsFileExist("@assets@/mystuff/foundit.dat"));
EXPECT_FALSE(archive->IsFileExist("@assets@/foundit.dat")); // do not find it in the previous locations!
EXPECT_TRUE(archive->OpenPack("@products@/mystuff", realNameBuf));
EXPECT_TRUE(archive->IsFileExist("@products@/mystuff/foundit.dat"));
EXPECT_FALSE(archive->IsFileExist("@products@/foundit.dat")); // do not find it in the previous locations!
EXPECT_FALSE(archive->IsFileExist("@usercache@/foundit.dat")); // do not find it in the previous locations!
EXPECT_FALSE(archive->IsFileExist("@assets@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
EXPECT_FALSE(archive->IsFileExist("@assets@/mystuff/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
EXPECT_FALSE(archive->IsFileExist("@assets@/notfoundit.dat")); // non-existent file
EXPECT_FALSE(archive->IsFileExist("@assets@/mystuff/notfoundit.dat")); // non-existent file
EXPECT_FALSE(archive->IsFileExist("@products@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
EXPECT_FALSE(archive->IsFileExist("@products@/mystuff/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
EXPECT_FALSE(archive->IsFileExist("@products@/notfoundit.dat")); // non-existent file
EXPECT_FALSE(archive->IsFileExist("@products@/mystuff/notfoundit.dat")); // non-existent file
EXPECT_TRUE(archive->ClosePack(realNameBuf));
}
@@ -861,7 +861,7 @@ namespace UnitTest
AZ::IO::FileIOBase* ioBase = AZ::IO::FileIOBase::GetInstance();
ASSERT_NE(nullptr, ioBase);
const char* assetsPath = ioBase->GetAlias("@assets@");
const char* assetsPath = ioBase->GetAlias("@products@");
ASSERT_NE(nullptr, assetsPath);
auto stringToAdd = AZ::IO::Path(assetsPath) / "textures" / "test.dds";
@@ -872,7 +872,7 @@ namespace UnitTest
// it normalizes the string, so the slashes flip and everything is lowercased.
AZ::IO::FixedMaxPath resolvedAddedPath;
AZ::IO::FixedMaxPath resolvedResourcePath;
EXPECT_TRUE(ioBase->ReplaceAlias(resolvedAddedPath, "@assets@/textures/test.dds"));
EXPECT_TRUE(ioBase->ReplaceAlias(resolvedAddedPath, "@products@/textures/test.dds"));
EXPECT_TRUE(ioBase->ReplaceAlias(resolvedResourcePath, reslist->GetFirst()));
EXPECT_EQ(resolvedAddedPath, resolvedResourcePath);
reslist->Clear();
@@ -802,6 +802,51 @@ namespace UnitTest
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
TEST_F(AliasTest, GetAlias_LogsError_WhenAccessingDeprecatedAlias_Succeeds)
{
AZ::IO::LocalFileIO local;
AZ::IO::FixedMaxPathString aliasFolder;
EXPECT_TRUE(local.ConvertToAbsolutePath("/temp", aliasFolder.data(), aliasFolder.capacity()));
aliasFolder.resize_no_construct(AZStd::char_traits<char>::length(aliasFolder.data()));
local.SetAlias("@test@", aliasFolder.c_str());
local.SetDeprecatedAlias("@deprecated@", "@test@");
local.SetDeprecatedAlias("@deprecatednonexistent@", "@nonexistent@");
local.SetDeprecatedAlias("@deprecatedsecond@", "@deprecated@");
local.SetDeprecatedAlias("@deprecatednonaliaspath@", aliasFolder);
AZ_TEST_START_TRACE_SUPPRESSION;
const char* testAlias = local.GetAlias("@test@");
ASSERT_NE(nullptr, testAlias);
EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias));
AZ_TEST_STOP_TRACE_SUPPRESSION(0);
// Validate that accessing Deprecated Alias results in AZ_Error
AZ_TEST_START_TRACE_SUPPRESSION;
testAlias = local.GetAlias("@deprecated@");
ASSERT_NE(nullptr, testAlias);
EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias));
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
AZ_TEST_START_TRACE_SUPPRESSION;
testAlias = local.GetAlias("@deprecatednonexistent@");
EXPECT_EQ(nullptr, testAlias);
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
AZ_TEST_START_TRACE_SUPPRESSION;
testAlias = local.GetAlias("@deprecatedsecond@");
ASSERT_NE(nullptr, testAlias);
EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias));
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
AZ_TEST_START_TRACE_SUPPRESSION;
testAlias = local.GetAlias("@deprecatednonaliaspath@");
ASSERT_NE(nullptr, testAlias);
EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias));
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
class SmartMoveTests
: public FolderFixture
{
@@ -27,7 +27,7 @@ namespace UnitTest
const char DummyFile[] = "dummy.txt";
const char AnotherDummyFile[] = "Foo/Dummy.txt";
const char DummyPattern[] = R"(^(.+)_([a-z]+)\..+$)";
const char MatchingPatternFile[] = "Foo/dummy_abc.txt";
const char NonMatchingPatternFile[] = "Foo/dummy_a8c.txt";
@@ -75,7 +75,7 @@ namespace UnitTest
: public AllocatorsFixture
{
public:
void SetUp() override
{
AllocatorsFixture::SetUp();
@@ -89,7 +89,7 @@ namespace UnitTest
const char* testAssetRoot = m_tempDirectory.GetDirectory();
// 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
// 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);
@@ -98,7 +98,7 @@ namespace UnitTest
AZ::IO::FileIOBase::SetInstance(nullptr);
AZ::IO::FileIOBase::SetInstance(m_data->m_localFileIO.get());
AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", testAssetRoot);
AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", testAssetRoot);
m_data->m_excludeFileQueryManager = AZStd::make_unique<FileTagQueryManagerTest>(FileTagType::Exclude);
m_data->m_includeFileQueryManager = AZStd::make_unique<FileTagQueryManagerTest>(FileTagType::Include);
@@ -114,7 +114,7 @@ namespace UnitTest
AZStd::vector<AZStd::string> includedWildcardTags = { DummyFileTags[DummyFileTagIndex::GIdx] };
EXPECT_TRUE(m_data->m_fileTagManager.AddFilePatternTags(DummyWildcard, FilePatternType::Wildcard, FileTagType::Include, includedWildcardTags).IsSuccess());
AzFramework::StringFunc::Path::Join(testAssetRoot, AZStd::string::format("%s.%s", ExcludeFile, FileTagAsset::Extension()).c_str(), m_data->m_excludeFile);
AzFramework::StringFunc::Path::Join(testAssetRoot, AZStd::string::format("%s.%s", IncludeFile, FileTagAsset::Extension()).c_str(), m_data->m_includeFile);
@@ -184,7 +184,7 @@ namespace UnitTest
TEST_F(FileTagTest, FileTags_QueryByAbsoluteFilePath_Valid)
{
AZStd::string absoluteDummyFilePath = DummyFile;
EXPECT_TRUE(AzFramework::StringFunc::AssetDatabasePath::Join("@assets@", absoluteDummyFilePath.c_str(), absoluteDummyFilePath));
EXPECT_TRUE(AzFramework::StringFunc::AssetDatabasePath::Join("@products@", absoluteDummyFilePath.c_str(), absoluteDummyFilePath));
AZStd::set<AZStd::string> tags = m_data->m_excludeFileQueryManager->GetTags(absoluteDummyFilePath);
@@ -196,7 +196,7 @@ namespace UnitTest
ASSERT_EQ(tags.size(), 0);
AZStd::string absoluteAnotherDummyFilePath = AnotherDummyFile;
EXPECT_TRUE(AzFramework::StringFunc::AssetDatabasePath::Join("@assets@", absoluteAnotherDummyFilePath.c_str(), absoluteAnotherDummyFilePath));
EXPECT_TRUE(AzFramework::StringFunc::AssetDatabasePath::Join("@products@", absoluteAnotherDummyFilePath.c_str(), absoluteAnotherDummyFilePath));
tags = m_data->m_includeFileQueryManager->GetTags(absoluteAnotherDummyFilePath);
ASSERT_EQ(tags.size(), 2);
@@ -213,7 +213,7 @@ namespace UnitTest
// Set the customized alias
AZStd::string customizedAliasFilePath;
const char* assetsAlias = AZ::IO::FileIOBase::GetInstance()->GetAlias("@assets@");
const char* assetsAlias = AZ::IO::FileIOBase::GetInstance()->GetAlias("@products@");
AzFramework::StringFunc::AssetDatabasePath::Join(assetsAlias, "foo", customizedAliasFilePath);
AZ::IO::FileIOBase::GetInstance()->SetAlias("@customizedalias@", customizedAliasFilePath.c_str());
@@ -305,7 +305,7 @@ namespace UnitTest
m_data->m_excludeFileQueryManager->ClearData();
EXPECT_TRUE(m_data->m_excludeFileQueryManager->Load(m_data->m_excludeFile));
AZStd::set<AZStd::string> outputTags = m_data->m_excludeFileQueryManager->GetTags(MatchingWildcardFile);
EXPECT_EQ(outputTags.size(), 2);
@@ -6,81 +6,19 @@
*
*/
#include <AzCore/IO/FileIO.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzTest/Utils.h>
#include <AzFramework/Application/Application.h>
namespace UnitTest
{
using namespace AZ;
class FileIOBaseRAII
{
public:
FileIOBaseRAII(AZ::IO::FileIOBase& fileIO)
: m_prevFileIO(AZ::IO::FileIOBase::GetInstance())
{
AZ::IO::FileIOBase::SetInstance(&fileIO);
}
~FileIOBaseRAII()
{
AZ::IO::FileIOBase::SetInstance(m_prevFileIO);
}
private:
AZ::IO::FileIOBase* m_prevFileIO;
};
class GenAppDescriptors
: public AllocatorsTestFixture
{
public:
void run()
{
struct Config
{
const char* platformName;
const char* configName;
const char* libSuffix;
};
ComponentApplication app;
SerializeContext serializeContext;
AZ::ComponentApplication::Descriptor::Reflect(&serializeContext, &app);
AZ::Entity::Reflect(&serializeContext);
DynamicModuleDescriptor::Reflect(&serializeContext);
AZ::Entity dummySystemEntity(AZ::SystemEntityId, "SystemEntity");
const Config config = {"Platform", "Config", "libSuffix"};
AZ::ComponentApplication::Descriptor descriptor;
if (config.libSuffix && config.libSuffix[0])
{
FakePopulateModules(descriptor, config.libSuffix);
}
const AZStd::string filename = AZStd::string::format("LYConfig_%s%s.xml", config.platformName, config.configName);
IO::FileIOStream stream(filename.c_str(), IO::OpenMode::ModeWrite);
ObjectStream* objStream = ObjectStream::Create(&stream, serializeContext, ObjectStream::ST_XML);
bool descWriteOk = objStream->WriteClass(&descriptor);
(void)descWriteOk;
AZ_Warning("ComponentApplication", descWriteOk, "Failed to write memory descriptor to application descriptor file %s!", filename.c_str());
bool entityWriteOk = objStream->WriteClass(&dummySystemEntity);
(void)entityWriteOk;
AZ_Warning("ComponentApplication", entityWriteOk, "Failed to write system entity to application descriptor file %s!", filename.c_str());
bool flushOk = objStream->Finalize();
(void)flushOk;
AZ_Warning("ComponentApplication", flushOk, "Failed finalizing application descriptor file %s!", filename.c_str());
}
void FakePopulateModules(AZ::ComponentApplication::Descriptor& desc, const char* libSuffix)
{
static const char* modules[] =
@@ -100,10 +38,44 @@ namespace UnitTest
}
};
TEST_F(GenAppDescriptors, Test)
TEST_F(GenAppDescriptors, WriteDescriptor_ToXML_Succeeds)
{
AZ::IO::LocalFileIO fileIO;
FileIOBaseRAII restoreFileIOScope(fileIO);
run();
struct Config
{
const char* platformName;
const char* configName;
const char* libSuffix;
};
AzFramework::Application app;
AZ::SerializeContext serializeContext;
AZ::ComponentApplication::Descriptor::Reflect(&serializeContext, &app);
AZ::Entity::Reflect(&serializeContext);
AZ::DynamicModuleDescriptor::Reflect(&serializeContext);
AZ::Entity dummySystemEntity(AZ::SystemEntityId, "SystemEntity");
const Config config = {"Platform", "Config", "libSuffix"};
AZ::ComponentApplication::Descriptor descriptor;
if (config.libSuffix && config.libSuffix[0])
{
FakePopulateModules(descriptor, config.libSuffix);
}
AZ::Test::ScopedAutoTempDirectory tempDirectory;
const auto filename = AZ::IO::Path(tempDirectory.GetDirectory()) /
AZStd::string::format("LYConfig_%s%s.xml", config.platformName, config.configName);
AZ::IO::FileIOStream stream(filename.c_str(), AZ::IO::OpenMode::ModeWrite);
auto objStream = AZ::ObjectStream::Create(&stream, serializeContext, AZ::ObjectStream::ST_XML);
const bool descWriteOk = objStream->WriteClass(&descriptor);
EXPECT_TRUE(descWriteOk) << "Failed to write memory descriptor to application descriptor file " << filename.c_str() << "!";
const bool entityWriteOk = objStream->WriteClass(&dummySystemEntity);
EXPECT_TRUE(entityWriteOk) << "Failed to write system entity to application descriptor file " << filename.c_str() << "!";
const bool flushOk = objStream->Finalize();
EXPECT_TRUE(flushOk) << "Failed finalizing application descriptor file " << filename.c_str() << "!";
}
}
@@ -38,12 +38,12 @@ namespace AzGameFramework
{
// fall back to checking Project Cache Root.
enginePakPath /= "engine.pak";
enginePakOpened = m_archive->OpenPack("@projectproductassets@", enginePakPath.Native());
enginePakOpened = m_archive->OpenPack("@products@", enginePakPath.Native());
}
if (!enginePakOpened)
{
enginePakPath = AZ::IO::FixedMaxPath(AZ::Utils::GetExecutableDirectory()) / "engine.pak";
m_archive->OpenPack("@projectproductassets@", enginePakPath.Native());
m_archive->OpenPack("@products@", enginePakPath.Native());
}
}
@@ -107,7 +107,7 @@ namespace AzNetworking
if (AZ::IO::FileIOBase::GetInstance() != nullptr)
{
char buffer[AZ_MAX_PATH_LEN];
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@assets@/", buffer, sizeof(buffer));
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@products@/", buffer, sizeof(buffer));
assetDir = AZStd::string(buffer);
}
@@ -47,14 +47,6 @@ namespace AzToolsFramework
//! Retrieve the absolute path for the Asset Database Location
virtual bool GetAbsoluteAssetDatabaseLocation(AZStd::string& /*result*/) { return false; }
//! Retrieve the absolute folder path to the current game's source assets (the ones that go into source control)
//! This may include the current mod path, if a mod is being edited by the editor
virtual const char* GetAbsoluteDevGameFolderPath() = 0;
//! Retrieve the absolute folder path to the current developer root ('dev'), which contains source artifacts
//! and is generally checked into source control.
virtual const char* GetAbsoluteDevRootFolderPath() = 0;
/// Convert a full source path like "c:\\dev\\gamename\\blah\\test.tga" into a relative product path.
/// asset paths never mention their alias and are relative to the asset cache root
@@ -354,26 +354,6 @@ namespace AzToolsFramework
}
}
const char* AssetSystemComponent::GetAbsoluteDevGameFolderPath()
{
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
if (fileIO)
{
return fileIO->GetAlias("@devassets@");
}
return "";
}
const char* AssetSystemComponent::GetAbsoluteDevRootFolderPath()
{
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
if (fileIO)
{
return fileIO->GetAlias("@devroot@");
}
return "";
}
void AssetSystemComponent::OnSystemTick()
{
AssetSystemBus::ExecuteQueuedEvents();
@@ -56,8 +56,6 @@ namespace AzToolsFramework
//////////////////////////////////////////////////////////////////////////
// AzToolsFramework::AssetSystemRequestBus::Handler overrides
bool GetAbsoluteAssetDatabaseLocation(AZStd::string& result) override;
const char* GetAbsoluteDevGameFolderPath() override;
const char* GetAbsoluteDevRootFolderPath() override;
bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& outputPath) override;
bool GenerateRelativeSourcePath(
const AZStd::string& sourcePath, AZStd::string& outputPath, AZStd::string& watchFolder) override;
@@ -137,7 +137,7 @@ namespace AzToolsFramework
AssetEditorWidgetUserSettings::AssetEditorWidgetUserSettings()
{
char assetRoot[AZ_MAX_PATH_LEN] = { 0 };
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", assetRoot, AZ_MAX_PATH_LEN);
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@projectroot@", assetRoot, AZ_MAX_PATH_LEN);
m_lastSavePath = assetRoot;
}
@@ -39,7 +39,7 @@ namespace AzToolsFramework
// the TraceContextLogFormatter.
//
// Usage example:
// const char* gameFolder = m_context.pRC->GetSystemEnvironment()->pFileIO->GetAlias("@devassets@");
// const char* gameFolder = m_context.pRC->GetSystemEnvironment()->pFileIO->GetAlias("@projectroot@");
// AZ_TraceContext("Game folder", gameFolder);
//
// for (int i=0; i<subMeshCount; ++i)
@@ -66,7 +66,7 @@ namespace AzToolsFramework
StringFunc::Path::Join(resolveBuffer, "log", logDirectory);
fileIO->SetAlias("@log@", logDirectory.c_str());
fileIO->CreatePath("@root@");
fileIO->CreatePath("@products@");
fileIO->CreatePath("@user@");
fileIO->CreatePath("@log@");
@@ -8,6 +8,7 @@
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/IO/FileOperations.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
@@ -78,13 +79,9 @@ namespace AzToolsFramework
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(entitiesAndDescendants,
&AzToolsFramework::ToolsApplicationRequestBus::Events::GatherEntitiesAndAllDescendents, AzToolsFramework::EntityIdList{ entityId });
// Retrieve the game folder so we can use that as a root with the passed in relative path
const char* gameFolder = nullptr;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(gameFolder, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetAbsoluteDevGameFolderPath);
// Join our relative path with the game folder to get a full path to the desired asset
AZStd::string assetFullPath;
AzFramework::StringFunc::Path::Join(gameFolder, assetPath, assetFullPath);
AZ::IO::FixedMaxPath assetFullPath = AZ::Utils::GetProjectPath();
assetFullPath /= assetPath;
// Call SliceUtilities::MakeNewSlice with all user input prompts disabled
bool success = AzToolsFramework::SliceUtilities::MakeNewSlice(entitiesAndDescendants,
@@ -718,7 +718,7 @@ namespace AzToolsFramework
if (!fullPathFound)
{
assetFullPath = AZStd::string::format("@devassets@/%s", sliceAssetPath.c_str());
assetFullPath = AZStd::string::format("@projectroot@/%s", sliceAssetPath.c_str());
}
return Commit(assetFullPath.c_str(), preSaveCallback, postSaveCallback, sliceCommitFlags);
@@ -1020,13 +1020,13 @@ namespace AzToolsFramework
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
AZ_Assert(fileIO, "File IO is not initialized.");
AZStd::string devAssetPath = fileIO->GetAlias("@devassets@");
AZStd::string devAssetPath = fileIO->GetAlias("@projectroot@");
AZStd::string userPath = fileIO->GetAlias("@user@");
AZStd::string tempPath = fullPath;
EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, devAssetPath);
EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, userPath);
EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, tempPath);
AzFramework::StringFunc::Replace(tempPath, "@devassets@", devAssetPath.c_str());
AzFramework::StringFunc::Replace(tempPath, "@projectroot@", devAssetPath.c_str());
AzFramework::StringFunc::Replace(tempPath, devAssetPath.c_str(), userPath.c_str());
tempPath.append(".slicetemp");
@@ -378,11 +378,11 @@ namespace AzToolsFramework
// If this layer is being loaded, it won't have a level save dependency yet, so clear that flag.
m_mustSaveLevelWhenLayerSaves = false;
QString fullPathName = levelPakFile;
if (fullPathName.contains("@devassets@"))
if (fullPathName.contains("@projectroot@"))
{
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
// Resolving the path through resolvepath would normalize and lowcase it, and in this case, we don't want that.
fullPathName.replace("@devassets@", fileIO->GetAlias("@devassets@"));
fullPathName.replace("@projectroot@", fileIO->GetAlias("@projectroot@"));
}
QFileInfo fileNameInfo(fullPathName);
@@ -315,7 +315,7 @@ namespace AzToolsFramework
// temporarily null after QFileDialogs close, which we need in order to
// be able to parent our message dialogs properly
QWidget* activeWindow = QApplication::activeWindow();
const AZStd::string prefabFilesPath = "@devassets@/Prefabs";
const AZStd::string prefabFilesPath = "@projectroot@/Prefabs";
// Remove Level entity if it's part of the list
@@ -131,13 +131,13 @@ namespace UnitTest
m_app.reset(aznew ToolsTestApplication("ArchiveComponentTest"));
m_app->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
// 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 (auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); fileIoBase != nullptr)
{
fileIoBase->SetAlias("@assets@", m_tempDir.GetDirectory());
fileIoBase->SetAlias("@products@", m_tempDir.GetDirectory());
}
}
@@ -37,10 +37,10 @@ namespace // anonymous
bool Search(const AzToolsFramework::AssetFileInfoList& assetList, const AZ::Data::AssetId& assetId)
{
return AZStd::find_if(assetList.m_fileInfoList.begin(), assetList.m_fileInfoList.end(),
[&](AzToolsFramework::AssetFileInfo fileInfo)
{
return fileInfo.m_assetId == assetId;
return AZStd::find_if(assetList.m_fileInfoList.begin(), assetList.m_fileInfoList.end(),
[&](AzToolsFramework::AssetFileInfo fileInfo)
{
return fileInfo.m_assetId == assetId;
});
}
}
@@ -74,11 +74,11 @@ namespace UnitTest
m_application->Start(AzFramework::Application::Descriptor());
// By default @assets@ is setup to include the platform at the end. But this test is going to
// By default @products@ is setup to include the platform at the end. But this test is going to
// loop over platforms and it will be included as part of the relative path of the file.
// So the asset folder for these tests have to point to the cache project root folder, which
// doesn't include the platform.
AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", cacheProjectRootFolder.c_str());
AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", cacheProjectRootFolder.c_str());
for (int idx = 0; idx < s_totalAssets; idx++)
{
@@ -158,17 +158,17 @@ namespace UnitTest
m_assetRegistry->RegisterAssetDependency(assets[5], AZ::Data::ProductDependency(assets[6], 0));
m_assetRegistry->RegisterAssetDependency(assets[6], AZ::Data::ProductDependency(assets[7], 0));
// asset8 -> asset6
// asset8 -> asset6
m_assetRegistry->RegisterAssetDependency(assets[8], AZ::Data::ProductDependency(assets[6], 0));
// asset10 -> asset11
// asset10 -> asset11
m_assetRegistry->RegisterAssetDependency(assets[10], AZ::Data::ProductDependency(assets[11], 0));
// asset11 -> asset10
// asset11 -> asset10
m_assetRegistry->RegisterAssetDependency(assets[11], AZ::Data::ProductDependency(assets[10], 0));
// 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
// 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);
@@ -203,10 +203,6 @@ namespace UnitTest
const AZStd::string engroot = AZ::Test::GetEngineRootPath();
AZ::IO::FileIOBase::GetInstance()->SetAlias("@engroot@", engroot.c_str());
AZ::IO::Path assetRoot(AZ::Utils::GetProjectPath());
assetRoot /= "Cache";
AZ::IO::FileIOBase::GetInstance()->SetAlias("@root@", assetRoot.c_str());
}
void TearDown() override
@@ -219,7 +215,7 @@ namespace UnitTest
delete m_application;
}
AZ::Data::AssetInfo GetAssetInfoById(const AZ::Data::AssetId& id) override
AZ::Data::AssetInfo GetAssetInfoById(const AZ::Data::AssetId& id) override
{
auto foundIter = m_assetRegistry->m_assetIdToInfo.find(id);
if (foundIter != m_assetRegistry->m_assetIdToInfo.end())
@@ -540,7 +536,7 @@ namespace UnitTest
EXPECT_TRUE(Search(assetList, assets[7]));
EXPECT_TRUE(Search(assetList, assets[8]));
// Removing the android flag from the asset should still produce the same result
// Removing the android flag from the asset should still produce the same result
m_assetSeedManager->RemoveSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ANDROID);
assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::PC);
@@ -564,7 +560,7 @@ namespace UnitTest
EXPECT_TRUE(Search(assetList, assets[3]));
EXPECT_TRUE(Search(assetList, assets[4]));
// Adding the android flag again to the asset
// Adding the android flag again to the asset
m_assetSeedManager->AddSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ANDROID);
assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::ANDROID_ID);
@@ -624,7 +620,7 @@ namespace UnitTest
EXPECT_EQ(assetList1.m_fileInfoList[0].m_assetId, assetList2.m_fileInfoList[0].m_assetId);
EXPECT_GE(assetList2.m_fileInfoList[0].m_modificationTime, assetList1.m_fileInfoList[0].m_modificationTime); // file mod time should change
// file hash should not change
for (int idx = 0; idx < 5; idx++)
{
@@ -680,7 +676,7 @@ namespace UnitTest
m_assetSeedManager->AddSeedAsset(assets[validFileIndex], AzFramework::PlatformFlags::Platform_PC, m_assetsPath[invalidFileIndex]);
const AzFramework::AssetSeedList& oldSeedList = m_assetSeedManager->GetAssetSeedList();
for (const auto& seedInfo : oldSeedList)
{
if (seedInfo.m_assetId == assets[validFileIndex])
@@ -18,8 +18,6 @@ namespace UnitTests
{
public:
MOCK_METHOD1(GetAbsoluteAssetDatabaseLocation, bool(AZStd::string&));
MOCK_METHOD0(GetAbsoluteDevGameFolderPath, const char* ());
MOCK_METHOD0(GetAbsoluteDevRootFolderPath, const char* ());
MOCK_METHOD2(GetRelativeProductPathFromFullSourceOrProductPath, bool(const AZStd::string& fullPath, AZStd::string& relativeProductPath));
MOCK_METHOD3(GenerateRelativeSourcePath,
bool(const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& watchFolder));
@@ -181,8 +181,8 @@ namespace UnitTest
const char* dir = m_componentApplication->GetExecutableFolder();
m_localFileIO.SetAlias("@assets@", dir);
m_localFileIO.SetAlias("@devassets@", dir);
m_localFileIO.SetAlias("@products@", dir);
m_localFileIO.SetAlias("@projectroot@", dir);
}
void Destroy()
@@ -22,7 +22,7 @@
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <Utils/Utils.h>
namespace
namespace
{
static const int s_totalAssets = 12;
}
@@ -53,15 +53,15 @@ namespace UnitTest
m_application->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
// 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);
// By default @assets@ is setup to include the platform at the end. But this test is going to
// By default @products@ is setup to include the platform at the end. But this test is going to
// loop over all platforms and it will be included as part of the relative path of the file.
// So the asset folder for these tests have to point to the cache project root folder, which
// doesn't include the platform.
AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", cacheProjectRootFolder.c_str());
AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", cacheProjectRootFolder.c_str());
for (int platformNum = AzFramework::PlatformId::PC; platformNum < AzFramework::PlatformId::NumPlatformIds; ++platformNum)
{
@@ -142,8 +142,6 @@ namespace UnitTest
/*
* AssetSystemRequestBus
*/
const char* GetAbsoluteDevGameFolderPath() override { return ""; }
const char* GetAbsoluteDevRootFolderPath() override { return ""; }
bool GetRelativeProductPathFromFullSourceOrProductPath([[maybe_unused]] const AZStd::string& fullPath, [[maybe_unused]] AZStd::string& relativeProductPath) override { return false; }
bool GenerateRelativeSourcePath(
[[maybe_unused]] const AZStd::string& sourcePath, [[maybe_unused]] AZStd::string& relativePath,