diff --git a/azure-pipelines/e2e-ports/duplicate-file-a/portfile.cmake b/azure-pipelines/e2e-ports/duplicate-file-a/portfile.cmake new file mode 100644 index 0000000000..98d000daaf --- /dev/null +++ b/azure-pipelines/e2e-ports/duplicate-file-a/portfile.cmake @@ -0,0 +1,4 @@ +set(VCPKG_BUILD_TYPE release) + +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/include/") +file(WRITE "${CURRENT_PACKAGES_DIR}/include/COMMON.H" "// THIS IS A HEADER FILE") diff --git a/azure-pipelines/e2e-ports/duplicate-file-a/vcpkg.json b/azure-pipelines/e2e-ports/duplicate-file-a/vcpkg.json new file mode 100644 index 0000000000..55fb068b2a --- /dev/null +++ b/azure-pipelines/e2e-ports/duplicate-file-a/vcpkg.json @@ -0,0 +1,4 @@ +{ + "name": "duplicate-file-a", + "version": "0.0.1" +} diff --git a/azure-pipelines/e2e-ports/duplicate-file-b/portfile.cmake b/azure-pipelines/e2e-ports/duplicate-file-b/portfile.cmake new file mode 100644 index 0000000000..c82628c96b --- /dev/null +++ b/azure-pipelines/e2e-ports/duplicate-file-b/portfile.cmake @@ -0,0 +1,4 @@ +set(VCPKG_BUILD_TYPE release) + +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/include/") +file(WRITE "${CURRENT_PACKAGES_DIR}/include/common.h" "// THIS IS A HEADER FILE") diff --git a/azure-pipelines/e2e-ports/duplicate-file-b/vcpkg.json b/azure-pipelines/e2e-ports/duplicate-file-b/vcpkg.json new file mode 100644 index 0000000000..ae41528023 --- /dev/null +++ b/azure-pipelines/e2e-ports/duplicate-file-b/vcpkg.json @@ -0,0 +1,4 @@ +{ + "name": "duplicate-file-b", + "version": "0.0.1" +} diff --git a/azure-pipelines/end-to-end-tests-dir/post-build-checks.ps1 b/azure-pipelines/end-to-end-tests-dir/post-build-checks.ps1 index 2ae56da212..2ce8fac406 100644 --- a/azure-pipelines/end-to-end-tests-dir/post-build-checks.ps1 +++ b/azure-pipelines/end-to-end-tests-dir/post-build-checks.ps1 @@ -5,6 +5,14 @@ if ($IsWindows) { $NativeSlash = '\' } +# Fail if installed files exist +Refresh-TestRoot +[string]$dupOutput = Run-VcpkgAndCaptureOutput install @commonArgs "--overlay-ports=$PSScriptRoot/../e2e-ports" duplicate-file-a duplicate-file-b +Throw-IfNotFailed +if (-not $dupOutput.Contains('The following files are already installed')) { + throw ('Incorrect error message for due to duplicate files; output was ' + $dupOutput) +} + # Empty package / disable all checks Refresh-TestRoot [string]$buildOutput = Run-VcpkgAndCaptureStderr install @commonArgs --overlay-ports="$PSScriptRoot/../e2e-ports" vcpkg-policy-set-incorrectly diff --git a/include/vcpkg/base/strings.h b/include/vcpkg/base/strings.h index 8a0dc3d3ef..2d27d27b29 100644 --- a/include/vcpkg/base/strings.h +++ b/include/vcpkg/base/strings.h @@ -123,6 +123,7 @@ namespace vcpkg::Strings const char* case_insensitive_ascii_search(StringView s, StringView pattern); bool case_insensitive_ascii_contains(StringView s, StringView pattern); bool case_insensitive_ascii_equals(StringView left, StringView right); + bool case_insensitive_ascii_less(StringView left, StringView right); void inplace_ascii_to_lowercase(char* first, char* last); void inplace_ascii_to_lowercase(std::string& s); diff --git a/src/vcpkg-test/strings.cpp b/src/vcpkg-test/strings.cpp index 31db934147..de6a284db9 100644 --- a/src/vcpkg-test/strings.cpp +++ b/src/vcpkg-test/strings.cpp @@ -236,6 +236,26 @@ TEST_CASE ("api_stable_format(sv,append_f)", "[strings]") REQUIRE(*res.get() == "123hello456"); } +TEST_CASE ("lex compare less", "[strings]") +{ + REQUIRE(Strings::case_insensitive_ascii_less("a", "b")); + REQUIRE(Strings::case_insensitive_ascii_less("a", "B")); + REQUIRE(Strings::case_insensitive_ascii_less("A", "b")); + REQUIRE(Strings::case_insensitive_ascii_less("A", "B")); + + REQUIRE_FALSE(Strings::case_insensitive_ascii_less("b", "a")); + REQUIRE_FALSE(Strings::case_insensitive_ascii_less("B", "a")); + REQUIRE_FALSE(Strings::case_insensitive_ascii_less("b", "A")); + REQUIRE_FALSE(Strings::case_insensitive_ascii_less("B", "A")); + + REQUIRE_FALSE(Strings::case_insensitive_ascii_less("b", "b")); + REQUIRE_FALSE(Strings::case_insensitive_ascii_less("b", "B")); + REQUIRE_FALSE(Strings::case_insensitive_ascii_less("B", "b")); + + REQUIRE(Strings::case_insensitive_ascii_less("a", "aa")); + REQUIRE_FALSE(Strings::case_insensitive_ascii_less("aa", "a")); +} + #if defined(_WIN32) TEST_CASE ("ascii to utf16", "[utf16]") { diff --git a/src/vcpkg/base/strings.cpp b/src/vcpkg/base/strings.cpp index c308819c9e..34633102c7 100644 --- a/src/vcpkg/base/strings.cpp +++ b/src/vcpkg/base/strings.cpp @@ -104,6 +104,11 @@ namespace bool operator()(char a, char b) const noexcept { return tolower_char(a) == tolower_char(b); } } icase_eq; + constexpr struct + { + bool operator()(char a, char b) const noexcept { return tolower_char(a) < tolower_char(b); } + } icase_less; + } #if defined(_WIN32) @@ -173,6 +178,11 @@ bool Strings::case_insensitive_ascii_equals(StringView left, StringView right) return std::equal(left.begin(), left.end(), right.begin(), right.end(), icase_eq); } +bool Strings::case_insensitive_ascii_less(StringView left, StringView right) +{ + return std::lexicographical_compare(left.begin(), left.end(), right.begin(), right.end(), icase_less); +} + void Strings::inplace_ascii_to_lowercase(char* first, char* last) { std::transform(first, last, first, tolower_char); } void Strings::inplace_ascii_to_lowercase(std::string& s) diff --git a/src/vcpkg/commands.install.cpp b/src/vcpkg/commands.install.cpp index 3c6270b030..2087e62835 100644 --- a/src/vcpkg/commands.install.cpp +++ b/src/vcpkg/commands.install.cpp @@ -231,14 +231,14 @@ namespace vcpkg struct intersection_compare { - // The VS2015 standard library requires comparison operators of T and U - // to also support comparison of T and T, and of U and U, due to debug checks. -#if _MSC_VER <= 1910 - bool operator()(const std::string& lhs, const std::string& rhs) const { return lhs < rhs; } - bool operator()(const file_pack& lhs, const file_pack& rhs) const { return lhs.first < rhs.first; } -#endif - bool operator()(const std::string& lhs, const file_pack& rhs) const { return lhs < rhs.first; } - bool operator()(const file_pack& lhs, const std::string& rhs) const { return lhs.first < rhs; } + bool operator()(const std::string& lhs, const file_pack& rhs) const + { + return Strings::case_insensitive_ascii_less(lhs, rhs.first); + } + bool operator()(const file_pack& lhs, const std::string& rhs) const + { + return Strings::case_insensitive_ascii_less(lhs.first, rhs); + } }; std::vector intersection;