aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBilly O'Neal <bion@microsoft.com>2020-09-22 20:16:24 -0700
committerGitHub <noreply@github.com>2020-09-22 20:16:24 -0700
commit5d33bd3d7910c6090911f71f9dcf1a4eecd303da (patch)
treecbd4b6e0562f4f0a18b2489cb7e43ab110b1432c
parent1095c177a9e1616921c03ffcd3a7799c627b5cd0 (diff)
downloadvcpkg-5d33bd3d7910c6090911f71f9dcf1a4eecd303da.tar.gz
vcpkg-5d33bd3d7910c6090911f71f9dcf1a4eecd303da.zip
[vcpkg] Fix the case of current_path() before use on Windows. (#13537)
* Fix the case of current_path() before use on Windows. This is a better solution than that I tried in https://github.com/microsoft/vcpkg/pull/13144 -- rather than trying to cannoicalize the path completely, which would destroy symlinks etc; this form calls FindFirstFile on each path element to get the real case from the filesystem. Fixes #13105. (Note the wrong case in the CWD in the prompt, but the correct case in all the output:) PS C:\DeV\vcPKG> .\vcpkg.exe install curl Computing installation plan... The following packages will be built and installed: curl[core,non-http,schannel,ssl,sspi,winssl]:x86-windows * zlib[core]:x86-windows Additional packages (*) will be modified to complete this operation. Detecting compiler hash for triplet x86-windows... Starting package 1/2: zlib:x86-windows Building package zlib[core]:x86-windows... Using cached binary package: C:\Users\billy\AppData\Local\vcpkg/archives\c6\c61dd1bcc23348934c55f4ced77f1e4b0f353394.zipBuilding package zlib[core]:x86-windows... done Installing package zlib[core]:x86-windows... Installing package zlib[core]:x86-windows... done Elapsed time for package zlib:x86-windows: 62.26 ms Starting package 2/2: curl:x86-windows Building package curl[core,non-http,schannel,ssl,sspi,winssl]:x86-windows... Could not locate cached archive: C:\Users\billy\AppData\Local\vcpkg/archives\9f\9fa16d473c9539e9ea7ab3922eff46618f3a4c4b.zip -- Downloading https://github.com/curl/curl/archive/9d954e49bce3706a9a2efb119ecd05767f0f2a9e.tar.gz... -- Extracting source C:/Dev/vcpkg/downloads/curl-curl-9d954e49bce3706a9a2efb119ecd05767f0f2a9e.tar.gz -- Applying patch 0002_fix_uwp.patch -- Applying patch 0004_nghttp2_staticlib.patch -- Applying patch 0005_remove_imp_suffix.patch -- Applying patch 0006_fix_tool_depends.patch -- Applying patch 0007_disable_tool_export_curl_target.patch -- Applying patch 0009_fix_openssl_config.patch -- Applying patch 0010_fix_othertests_cmake.patch -- Applying patch 0011_fix_static_build.patch -- Using source at C:/Dev/vcpkg/buildtrees/curl/src/767f0f2a9e-91d24adee1.clean -- Configuring x86-windows -- Building x86-windows-dbg -- Building x86-windows-rel -- Installing: C:/Dev/vcpkg/packages/curl_x86-windows/share/curl/curl-config -- Installing: C:/Dev/vcpkg/packages/curl_x86-windows/share/curl/vcpkg-cmake-wrapper.cmake -- Installing: C:/Dev/vcpkg/packages/curl_x86-windows/share/curl/copyright -- Performing post-build validation -- Performing post-build validation done Stored binary cache: C:\Users\billy\AppData\Local\vcpkg/archives\9f\9fa16d473c9539e9ea7ab3922eff46618f3a4c4b.zip Building package curl[core,non-http,schannel,ssl,sspi,winssl]:x86-windows... done Installing package curl[core,non-http,schannel,ssl,sspi,winssl]:x86-windows... Installing package curl[core,non-http,schannel,ssl,sspi,winssl]:x86-windows... done Elapsed time for package curl:x86-windows: 1.299 min Total elapsed time: 1.33 min The package curl:x86-windows provides CMake targets: find_package(CURL CONFIG REQUIRED) target_link_libraries(main PRIVATE CURL::libcurl) PS C:\DeV\vcPKG> * Fix *nix builds. * PR feedback. * Update toolsrc/src/vcpkg/base/files.cpp Co-authored-by: ras0219 <533828+ras0219@users.noreply.github.com>
-rw-r--r--toolsrc/include/vcpkg/base/files.h4
-rw-r--r--toolsrc/src/vcpkg-test/files.cpp56
-rw-r--r--toolsrc/src/vcpkg/base/files.cpp180
-rw-r--r--toolsrc/src/vcpkg/vcpkgpaths.cpp24
4 files changed, 246 insertions, 18 deletions
diff --git a/toolsrc/include/vcpkg/base/files.h b/toolsrc/include/vcpkg/base/files.h
index 08bbe7906..71e386a5c 100644
--- a/toolsrc/include/vcpkg/base/files.h
+++ b/toolsrc/include/vcpkg/base/files.h
@@ -237,4 +237,8 @@ namespace vcpkg::Files
/// Performs "lhs / rhs" according to the C++17 Filesystem Library Specification.
/// This function exists as a workaround for TS implementations.
fs::path combine(const fs::path& lhs, const fs::path& rhs);
+
+#if defined(_WIN32)
+ fs::path win32_fix_path_case(const fs::path& source);
+#endif // _WIN32
}
diff --git a/toolsrc/src/vcpkg-test/files.cpp b/toolsrc/src/vcpkg-test/files.cpp
index a8c7c2ba2..429a4ba15 100644
--- a/toolsrc/src/vcpkg-test/files.cpp
+++ b/toolsrc/src/vcpkg-test/files.cpp
@@ -194,6 +194,62 @@ TEST_CASE ("remove all", "[files]")
CHECK_EC_ON_FILE(temp_dir, ec);
}
+#if defined(_WIN32)
+TEST_CASE ("win32_fix_path_case", "[files]")
+{
+ using vcpkg::Files::win32_fix_path_case;
+
+ // This test assumes that the Windows directory is C:\Windows
+
+ CHECK(win32_fix_path_case(L"") == L"");
+
+ CHECK(win32_fix_path_case(L"C:") == L"C:");
+ CHECK(win32_fix_path_case(L"c:") == L"C:");
+ CHECK(win32_fix_path_case(L"C:/") == L"C:\\");
+ CHECK(win32_fix_path_case(L"C:\\") == L"C:\\");
+ CHECK(win32_fix_path_case(L"c:\\") == L"C:\\");
+ CHECK(win32_fix_path_case(L"C:\\WiNdOws") == L"C:\\Windows");
+ CHECK(win32_fix_path_case(L"c:\\WiNdOws\\") == L"C:\\Windows\\");
+ CHECK(win32_fix_path_case(L"C://///////WiNdOws") == L"C:\\Windows");
+ CHECK(win32_fix_path_case(L"c:\\/\\/WiNdOws\\/") == L"C:\\Windows\\");
+
+ auto& fs = vcpkg::Files::get_real_filesystem();
+ auto original_cwd = fs.current_path(VCPKG_LINE_INFO);
+ fs.current_path(L"C:\\", VCPKG_LINE_INFO);
+ CHECK(win32_fix_path_case(L"\\") == L"\\");
+ CHECK(win32_fix_path_case(L"\\/\\WiNdOws") == L"\\Windows");
+ CHECK(win32_fix_path_case(L"\\WiNdOws") == L"\\Windows");
+ CHECK(win32_fix_path_case(L"\\WiNdOws") == L"\\Windows");
+ CHECK(win32_fix_path_case(L"c:WiNdOws") == L"C:Windows");
+ CHECK(win32_fix_path_case(L"c:WiNdOws/system32") == L"C:Windows\\System32");
+ fs.current_path(original_cwd, VCPKG_LINE_INFO);
+
+ fs.create_directories("SuB/Dir/Ectory", VCPKG_LINE_INFO);
+ CHECK(win32_fix_path_case(L"sub") == L"SuB");
+ CHECK(win32_fix_path_case(L"SUB") == L"SuB");
+ CHECK(win32_fix_path_case(L"sub/") == L"SuB\\");
+ CHECK(win32_fix_path_case(L"sub/dir") == L"SuB\\Dir");
+ CHECK(win32_fix_path_case(L"sub/dir/") == L"SuB\\Dir\\");
+ CHECK(win32_fix_path_case(L"sub/dir/ectory") == L"SuB\\Dir\\Ectory");
+ CHECK(win32_fix_path_case(L"sub/dir/ectory/") == L"SuB\\Dir\\Ectory\\");
+ fs.remove_all("SuB", VCPKG_LINE_INFO);
+
+ CHECK(win32_fix_path_case(L"//nonexistent_server\\nonexistent_share\\") ==
+ L"\\\\nonexistent_server\\nonexistent_share\\");
+ CHECK(win32_fix_path_case(L"\\\\nonexistent_server\\nonexistent_share\\") ==
+ L"\\\\nonexistent_server\\nonexistent_share\\");
+ CHECK(win32_fix_path_case(L"\\\\nonexistent_server\\nonexistent_share") ==
+ L"\\\\nonexistent_server\\nonexistent_share");
+
+ CHECK(win32_fix_path_case(L"///three_slashes_not_a_server\\subdir\\") == L"\\three_slashes_not_a_server\\subdir\\");
+
+ CHECK(win32_fix_path_case(L"\\??\\c:\\WiNdOws") == L"\\??\\c:\\WiNdOws");
+ CHECK(win32_fix_path_case(L"\\\\?\\c:\\WiNdOws") == L"\\\\?\\c:\\WiNdOws");
+ CHECK(win32_fix_path_case(L"\\\\.\\c:\\WiNdOws") == L"\\\\.\\c:\\WiNdOws");
+ CHECK(win32_fix_path_case(L"c:\\/\\/Nonexistent\\/path/here") == L"C:\\Nonexistent\\path\\here");
+}
+#endif // _WIN32
+
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
TEST_CASE ("remove all -- benchmarks", "[files][!benchmark]")
{
diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp
index 51c6c9c76..916c26bda 100644
--- a/toolsrc/src/vcpkg/base/files.cpp
+++ b/toolsrc/src/vcpkg/base/files.cpp
@@ -5,12 +5,14 @@
#include <vcpkg/base/system.process.h>
#include <vcpkg/base/util.h>
-#if !defined(_WIN32)
+#if defined(_WIN32)
+#include <vcpkg/base/system_headers.h>
+#else // ^^^ _WIN32 // !_WIN32 vvv
#include <fcntl.h>
#include <sys/file.h>
#include <sys/stat.h>
-#endif
+#endif // _WIN32
#if defined(__linux__)
#include <sys/sendfile.h>
@@ -18,6 +20,78 @@
#include <copyfile.h>
#endif // ^^^ defined(__APPLE__)
+#include <algorithm>
+#include <string>
+
+#if defined(_WIN32)
+namespace
+{
+ struct IsSlash
+ {
+ bool operator()(const wchar_t c) const noexcept { return c == L'/' || c == L'\\'; }
+ };
+
+ constexpr IsSlash is_slash;
+
+ template<size_t N>
+ bool wide_starts_with(const std::wstring& haystack, const wchar_t (&needle)[N]) noexcept
+ {
+ const size_t without_null = N - 1;
+ return haystack.size() >= without_null && std::equal(needle, needle + without_null, haystack.begin());
+ }
+
+ bool starts_with_drive_letter(std::wstring::const_iterator first, const std::wstring::const_iterator last) noexcept
+ {
+ if (last - first < 2)
+ {
+ return false;
+ }
+
+ if (!(first[0] >= L'a' && first[0] <= L'z') && !(first[0] >= L'A' && first[0] <= L'Z'))
+ {
+ return false;
+ }
+
+ if (first[1] != L':')
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ struct FindFirstOp
+ {
+ HANDLE h_find = INVALID_HANDLE_VALUE;
+ WIN32_FIND_DATAW find_data;
+
+ unsigned long find_first(const wchar_t* const path) noexcept
+ {
+ assert(h_find == INVALID_HANDLE_VALUE);
+ h_find = FindFirstFileW(path, &find_data);
+ if (h_find == INVALID_HANDLE_VALUE)
+ {
+ return GetLastError();
+ }
+
+ return ERROR_SUCCESS;
+ }
+
+ FindFirstOp() = default;
+ FindFirstOp(const FindFirstOp&) = delete;
+ FindFirstOp& operator=(const FindFirstOp&) = delete;
+
+ ~FindFirstOp()
+ {
+ if (h_find != INVALID_HANDLE_VALUE)
+ {
+ (void)FindClose(h_find);
+ }
+ }
+ };
+} // unnamed namespace
+#endif // _WIN32
+
fs::path fs::u8path(vcpkg::StringView s)
{
#if defined(_WIN32)
@@ -1197,4 +1271,106 @@ namespace vcpkg::Files
#endif // ^^^ windows
#endif // ^^^ std::experimental::filesystem
}
+
+#ifdef _WIN32
+ fs::path win32_fix_path_case(const fs::path& source)
+ {
+ const std::wstring& native = source.native();
+ if (native.empty())
+ {
+ return fs::path{};
+ }
+
+ if (wide_starts_with(native, L"\\\\?\\") || wide_starts_with(native, L"\\??\\") ||
+ wide_starts_with(native, L"\\\\.\\"))
+ {
+ // no support to attempt to fix paths in the NT, \\GLOBAL??, or device namespaces at this time
+ return source;
+ }
+
+ const auto last = native.end();
+ auto first = native.begin();
+ auto is_wildcard = [](wchar_t c) { return c == L'?' || c == L'*'; };
+ if (std::any_of(first, last, is_wildcard))
+ {
+ Checks::exit_with_message(
+ VCPKG_LINE_INFO, "Attempt to fix case of a path containing wildcards: %s", fs::u8string(source));
+ }
+
+ std::wstring in_progress;
+ in_progress.reserve(native.size());
+ if (last - first >= 3 && is_slash(first[0]) && is_slash(first[1]) && !is_slash(first[2]))
+ {
+ // path with UNC prefix \\server\share; this will be rejected by FindFirstFile so we skip over that
+ in_progress.push_back(L'\\');
+ in_progress.push_back(L'\\');
+ first += 2;
+ auto next_slash = std::find_if(first, last, is_slash);
+ in_progress.append(first, next_slash);
+ in_progress.push_back(L'\\');
+ first = std::find_if_not(next_slash, last, is_slash);
+ next_slash = std::find_if(first, last, is_slash);
+ in_progress.append(first, next_slash);
+ first = std::find_if_not(next_slash, last, is_slash);
+ if (first != next_slash)
+ {
+ in_progress.push_back(L'\\');
+ }
+ }
+ else if (last - first >= 1 && is_slash(first[0]))
+ {
+ // root relative path
+ in_progress.push_back(L'\\');
+ first = std::find_if_not(first, last, is_slash);
+ }
+ else if (starts_with_drive_letter(first, last))
+ {
+ // path with drive letter root
+ auto letter = first[0];
+ if (letter >= L'a' && letter <= L'z')
+ {
+ letter = letter - L'a' + L'A';
+ }
+
+ in_progress.push_back(letter);
+ in_progress.push_back(L':');
+ first += 2;
+ if (first != last && is_slash(*first))
+ {
+ // absolute path
+ in_progress.push_back(L'\\');
+ first = std::find_if_not(first, last, is_slash);
+ }
+ }
+
+ assert(!fs::path(first, last).has_root_path());
+
+ while (first != last)
+ {
+ auto next_slash = std::find_if(first, last, is_slash);
+ auto original_size = in_progress.size();
+ in_progress.append(first, next_slash);
+ FindFirstOp this_find;
+ unsigned long last_error = this_find.find_first(in_progress.c_str());
+ if (last_error == ERROR_SUCCESS)
+ {
+ in_progress.resize(original_size);
+ in_progress.append(this_find.find_data.cFileName);
+ }
+ else
+ {
+ // we might not have access to this intermediate part of the path;
+ // just guess that the case of that element is correct and move on
+ }
+
+ first = std::find_if_not(next_slash, last, is_slash);
+ if (first != next_slash)
+ {
+ in_progress.push_back(L'\\');
+ }
+ }
+
+ return fs::path(std::move(in_progress));
+ }
+#endif // _WIN32
}
diff --git a/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp
index 9f4ffce6b..60403edbd 100644
--- a/toolsrc/src/vcpkg/vcpkgpaths.cpp
+++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp
@@ -62,24 +62,13 @@ namespace
Files::Filesystem& filesystem, const fs::path& root, std::string* option, StringLiteral name, LineInfo li)
{
auto result = process_output_directory_impl(filesystem, root, option, name, li);
+#if defined(_WIN32)
+ result = vcpkg::Files::win32_fix_path_case(result);
+#endif // _WIN32
Debug::print("Using ", name, "-root: ", fs::u8string(result), '\n');
return result;
}
- void uppercase_win32_drive_letter(fs::path& path)
- {
-#if defined(_WIN32)
- const auto& nativePath = path.native();
- if (nativePath.size() > 2 && (nativePath[0] >= L'a' && nativePath[0] <= L'z') && nativePath[1] == L':')
- {
- auto uppercaseFirstLetter = std::move(path).native();
- uppercaseFirstLetter[0] = nativePath[0] - L'a' + L'A';
- path = uppercaseFirstLetter;
- }
-#endif
- (void)path;
- }
-
} // unnamed namespace
namespace vcpkg
@@ -225,6 +214,10 @@ namespace vcpkg
: m_pimpl(std::make_unique<details::VcpkgPathsImpl>(filesystem, args.compiler_tracking_enabled()))
{
original_cwd = filesystem.current_path(VCPKG_LINE_INFO);
+#if defined(_WIN32)
+ original_cwd = vcpkg::Files::win32_fix_path_case(original_cwd);
+#endif // _WIN32
+
if (args.vcpkg_root_dir)
{
root = filesystem.canonical(VCPKG_LINE_INFO, fs::u8path(*args.vcpkg_root_dir));
@@ -238,7 +231,7 @@ namespace vcpkg
filesystem.canonical(VCPKG_LINE_INFO, System::get_exe_path_of_current_process()), ".vcpkg-root");
}
}
- uppercase_win32_drive_letter(root);
+
Checks::check_exit(VCPKG_LINE_INFO, !root.empty(), "Error: Could not detect vcpkg-root.");
Debug::print("Using vcpkg-root: ", fs::u8string(root), '\n');
@@ -252,7 +245,6 @@ namespace vcpkg
{
manifest_root_dir = filesystem.find_file_recursively_up(original_cwd, fs::u8path("vcpkg.json"));
}
- uppercase_win32_drive_letter(manifest_root_dir);
if (!manifest_root_dir.empty() && manifest_mode_on)
{