mirror of https://github.com/microsoft/vcpkg.git
[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 9d954e49bc.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>
This commit is contained in:
parent
1095c177a9
commit
5d33bd3d79
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]")
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue