diff --git a/Code/Framework/AzCore/AzCore/IO/Path/Path.inl b/Code/Framework/AzCore/AzCore/IO/Path/Path.inl index 309a4fa050..6354324136 100644 --- a/Code/Framework/AzCore/AzCore/IO/Path/Path.inl +++ b/Code/Framework/AzCore/AzCore/IO/Path/Path.inl @@ -11,6 +11,7 @@ #include #include #include +#include // extern instantiations of Path templates to prevent implicit instantiations namespace AZ::IO @@ -92,11 +93,23 @@ namespace AZ::IO::Internal constexpr auto ConsumeRootName(InputIt entryBeginIter, InputIt entryEndIter, const char preferredSeparator) -> AZStd::enable_if_t, InputIt> { - if (preferredSeparator == '/') + if (preferredSeparator == PosixPathSeparator) { // If the preferred separator is forward slash the parser is in posix path - // parsing mode, which doesn't have a root name + // parsing mode, which doesn't have a root name, + // unless we're on a posix platform that uses a custom path root separator + #if defined(AZ_TRAIT_CUSTOM_PATH_ROOT_SEPARATOR) + const AZStd::string_view path{ entryBeginIter, entryEndIter }; + const auto positionOfPathSeparator = path.find(AZ_TRAIT_CUSTOM_PATH_ROOT_SEPARATOR); + if (positionOfPathSeparator == AZStd::string_view::npos) + { + return entryBeginIter; + } + const AZStd::string_view rootName{ path.substr(0, positionOfPathSeparator + 1) }; + return AZStd::next(entryBeginIter, rootName.size()); + #else return entryBeginIter; + #endif } else { @@ -185,13 +198,18 @@ namespace AZ::IO::Internal template >> static constexpr bool IsAbsolute(InputIt first, EndIt last, const char preferredSeparator) { - size_t pathSize = AZStd::distance(first, last); - // If the preferred separator is a forward slash - // than an absolute path is simply one that starts with a forward slash - if (preferredSeparator == '/') - { + // than an absolute path is simply one that starts with a forward slash, + // unless we're on a posix platform that uses a custom path root separator + if (preferredSeparator == PosixPathSeparator) + { + #if defined(AZ_TRAIT_CUSTOM_PATH_ROOT_SEPARATOR) + const AZStd::string_view path{ first, last }; + return path.find(AZ_TRAIT_CUSTOM_PATH_ROOT_SEPARATOR) != AZStd::string_view::npos; + #else + const size_t pathSize = AZStd::distance(first, last); return pathSize > 0 && IsSeparator(*first); + #endif } else { @@ -199,6 +217,7 @@ namespace AZ::IO::Internal { // If a windows path ends starts with C:foo it is a root relative path // A path is absolute root absolute on windows if it starts with + const size_t pathSize = AZStd::distance(first, last); return pathSize > 2 && Internal::IsSeparator(*AZStd::next(first, 2)); } diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp index 6b89b8c044..0ca354ac95 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp @@ -550,7 +550,7 @@ namespace AZ::SettingsRegistryMergeUtils } registry.Set(FilePathKey_ProjectUserPath, projectUserPath.Native()); - // Set the user directory with the provided path or using project/user as default + // Set the log directory with the provided path or using project/user/log as default auto projectLogPathKey = FixedValueString::format("%s/project_log_path", BootstrapSettingsRootKey); AZ::IO::FixedMaxPath projectLogPath; if (!registry.Get(projectLogPath.Native(), projectLogPathKey)) @@ -640,7 +640,7 @@ namespace AZ::SettingsRegistryMergeUtils } #if !AZ_TRAIT_OS_IS_HOST_OS_PLATFORM - // Setup the cache and user paths when to platform specific locations when running on non-host platforms + // Setup the cache, user, and log paths to platform specific locations when running on non-host platforms path = engineRoot; if (AZStd::optional nonHostCacheRoot = Utils::GetDefaultAppRootPath(); nonHostCacheRoot) @@ -656,13 +656,16 @@ namespace AZ::SettingsRegistryMergeUtils if (AZStd::optional devWriteStorage = Utils::GetDevWriteStoragePath(); devWriteStorage) { - registry.Set(FilePathKey_DevWriteStorage, *devWriteStorage); - registry.Set(FilePathKey_ProjectUserPath, *devWriteStorage); + const AZ::IO::FixedMaxPath devWriteStoragePath(*devWriteStorage); + registry.Set(FilePathKey_DevWriteStorage, devWriteStoragePath.LexicallyNormal().Native()); + registry.Set(FilePathKey_ProjectUserPath, (devWriteStoragePath / "user").LexicallyNormal().Native()); + registry.Set(FilePathKey_ProjectLogPath, (devWriteStoragePath / "user/log").LexicallyNormal().Native()); } else { registry.Set(FilePathKey_DevWriteStorage, path.LexicallyNormal().Native()); registry.Set(FilePathKey_ProjectUserPath, (path / "user").LexicallyNormal().Native()); + registry.Set(FilePathKey_ProjectLogPath, (path / "user/log").LexicallyNormal().Native()); } #endif // AZ_TRAIT_OS_IS_HOST_OS_PLATFORM } diff --git a/scripts/commit_validation/commit_validation/tests/validators/test_az_trait_validator.py b/scripts/commit_validation/commit_validation/tests/validators/test_az_trait_validator.py deleted file mode 100755 index 9474fd00b6..0000000000 --- a/scripts/commit_validation/commit_validation/tests/validators/test_az_trait_validator.py +++ /dev/null @@ -1,92 +0,0 @@ -# -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# - -import unittest -from unittest.mock import patch, mock_open - -from commit_validation import pal_allowedlist -from commit_validation.tests.mocks.mock_commit import MockCommit -from commit_validation.validators.az_trait_validator import AzTraitValidator - -import pytest - - -class Test_AzTraitValidatorTests(): - def test_fileDoesntCheckAzTraitIsDefined_passes(self): - commit = MockCommit( - files=['someCppFile.cpp'], - file_diffs={ 'someCppFile.cpp' : ''} - ) - error_list = [] - assert AzTraitValidator().run(commit, error_list) - assert len(error_list) == 0, f"Unexpected errors: {error_list}" - - @pytest.mark.parametrize( - 'file_diffs,expect_success', [ - pytest.param('+This file does contain\n' - '+a trait existence check\n' - '+#ifdef AZ_TRAIT_USED_INCORRECTLY\n', - False, - id="AZ_TRAIT_inside_ifdef_fails" ), # gives the test a friendly name! - - pytest.param('+This file does contain\n' - '+a trait existence check\n' - '+#if defined(AZ_TRAIT_USED_INCORRECTLY)\n', - False, - id="AZ_TRAIT_inside_if_defined_fails" ), - - pytest.param('+This file does contain\n' - '+a trait existence check\n' - '+#ifndef AZ_TRAIT_USED_INCORRECTLY\n', - False, - id="AZ_TRAIT_inside_ifndef_fails" ), - - pytest.param('+This file contains a diff which REMOVES an incorrect usage\n' - '-#ifndef AZ_TRAIT_USED_INCORRECTLY\n', - True, - id="AZ_TRAIT_removed_in_diff_passes" ), - - pytest.param('+This file contains a diff which has an old already okayed usage\n' - '+which is not actually part of the diff.\n' - '#ifndef AZ_TRAIT_USED_INCORRECTLY\n', - True, - id="AZ_TRAIT_in_unmodified_section_passes"), - - pytest.param('+This file contains the correct usage\n' - '+#if AZ_TRAIT_USED_CORRECTLY\n', - True, - id="AZ_TRAIT_correct_usage_passes"), - ]) - def test_fileChecksAzTraitIsDefined(self, file_diffs, expect_success): - commit = MockCommit( - files=['someCppFile.cpp'], - file_diffs={ 'someCppFile.cpp' : file_diffs }) - - error_list = [] - if expect_success: - assert AzTraitValidator().run(commit, error_list) - assert len(error_list) == 0, f"Unexpected errors: {error_list}" - else: - assert not AzTraitValidator().run(commit, error_list) - assert len(error_list) != 0, f"Errors were expected but none were returned." - - def test_fileExtensionIgnored_passes(self): - commit = MockCommit(files=['someCppFile.waf_files']) - error_list = [] - assert AzTraitValidator().run(commit, error_list) - assert len(error_list) == 0, f"Unexpected errors: {error_list}" - - @patch('commit_validation.pal_allowedlist.load', return_value=pal_allowedlist.PALAllowedlist(['*/some/path/*'])) - def test_fileAllowedlisted_passes(self, mocked_load): - commit = MockCommit(files=['/path/to/some/path/someCppFile.cpp']) - error_list = [] - assert AzTraitValidator().run(commit, error_list) - assert len(error_list) == 0, f"Unexpected errors: {error_list}" - -if __name__ == '__main__': - unittest.main() diff --git a/scripts/commit_validation/commit_validation/validators/az_trait_validator.py b/scripts/commit_validation/commit_validation/validators/az_trait_validator.py deleted file mode 100755 index 595cea8720..0000000000 --- a/scripts/commit_validation/commit_validation/validators/az_trait_validator.py +++ /dev/null @@ -1,57 +0,0 @@ -# -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# - -import os -import re -from typing import Type, List - -import commit_validation.pal_allowedlist as pal_allowedlist -from commit_validation.commit_validation import Commit, CommitValidator, SOURCE_FILE_EXTENSIONS, VERBOSE - -ifdef_regex = re.compile(r'^\+\s*#\s*ifn?def\s+AZ_TRAIT_') -defined_regex = re.compile(r'\sdefined\s*\(\s*AZ_TRAIT_') - - -class AzTraitValidator(CommitValidator): - """A file-level validator that makes sure a file does not contain existence checks for AZ_TRAIT macros""" - - def __init__(self) -> None: - self.pal_allowedlist = pal_allowedlist.load() - - def run(self, commit: Commit, errors: List[str]) -> bool: - for file_name in commit.get_files(): - if os.path.splitext(file_name)[1].lower() not in SOURCE_FILE_EXTENSIONS: - if VERBOSE: print(f'{file_name}::{self.__class__.__name__} SKIPPED - File excluded based on extension.') - continue - if self.pal_allowedlist.is_match(file_name): - if VERBOSE: print(f'{file_name}::{self.__class__.__name__} SKIPPED - File excluded based on PAL allowedlist.') - continue - - file_diff = commit.get_file_diff(file_name) - previous_line_context = "" - - for line in file_diff.splitlines(): - # we only care about added lines. - if line.startswith('+'): - if ifdef_regex.search(line) or defined_regex.search(line): - error_message = str( - f'{file_name}::{self.__class__.__name__} FAILED - Source file contains an existence ' - f'check for an AZ_TRAIT macro in this code: \n' - f' {previous_line_context}\n' - f' ----> {line}\n' - f'Traits should be tested for true/false, since they are guaranteed to exist on all platforms.') - if VERBOSE: print(error_message) - errors.append(error_message) - previous_line_context = line - - return (not errors) - - -def get_validator() -> Type[AzTraitValidator]: - """Returns the validator class for this module""" - return AzTraitValidator