From c1fd2bc314a04ca6509a9eab487410ea375edb83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A4r=20Bohrarper?= Date: Fri, 21 Apr 2023 09:28:58 +0200 Subject: [PATCH] Use *W versions of win32 file functions So that the long paths work on windows with registry LongPathsEnabled=1 --- src/disk_interface.cc | 51 +++++++++++++++++++++++++++++++++++++------ src/util.cc | 10 ++++++++- src/util.h | 3 +++ 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/disk_interface.cc b/src/disk_interface.cc index 1157463432..ba1e9a689c 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -25,7 +25,7 @@ #ifdef _WIN32 #include #include -#include // _mkdir +#include #else #include #endif @@ -56,7 +56,8 @@ string DirName(const string& path) { int MakeDir(const string& path) { #ifdef _WIN32 - return _mkdir(path.c_str()); + std::wstring wpath = ::toWide(path); + return CreateDirectoryW((LPCWSTR)wpath.c_str(), NULL) ? 0 : -1; #else return mkdir(path.c_str(), 0777); #endif @@ -75,7 +76,8 @@ TimeStamp TimeStampFromFileTime(const FILETIME& filetime) { TimeStamp StatSingleFile(const string& path, string* err) { WIN32_FILE_ATTRIBUTE_DATA attrs; - if (!GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &attrs)) { + std::wstring wpath = ::toWide(path); + if (!GetFileAttributesExW(wpath.c_str(), GetFileExInfoStandard, &attrs)) { DWORD win_err = GetLastError(); if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) return 0; @@ -227,6 +229,33 @@ TimeStamp RealDiskInterface::Stat(const string& path, string* err) const { } bool RealDiskInterface::WriteFile(const string& path, const string& contents) { +#ifdef _WIN32 + std::wstring wpath = ::toWide(path); + HANDLE h = CreateFileW(wpath.c_str(), (GENERIC_READ | GENERIC_WRITE), 0, NULL, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) { + Error("WriteFile(%s): Unable to create file. %s", path.c_str(), + GetLastErrorString().c_str()); + return false; + } + DWORD written = 0; + bool result = ::WriteFile(h, (LPCVOID)contents.data(), contents.length(), + &written, NULL); + if (!result || written != contents.length()) { + Error("WriteFile(%s): Unable to write to the file. %s", path.c_str(), + GetLastErrorString().c_str()); + CloseHandle(h); + return false; + } + + if (!CloseHandle(h)) { + Error("WriteFile(%s): Unable to close the file. %s", path.c_str(), + GetLastErrorString().c_str()); + return false; + } + + return true; +#else FILE* fp = fopen(path.c_str(), "w"); if (fp == NULL) { Error("WriteFile(%s): Unable to create file. %s", @@ -248,13 +277,20 @@ bool RealDiskInterface::WriteFile(const string& path, const string& contents) { } return true; +#endif } bool RealDiskInterface::MakeDir(const string& path) { if (::MakeDir(path) < 0) { +#if _WIN32 + if (GetLastError() == ERROR_ALREADY_EXISTS) { + return true; + } +#else if (errno == EEXIST) { return true; } +#endif Error("mkdir(%s): %s", path.c_str(), strerror(errno)); return false; } @@ -273,7 +309,8 @@ FileReader::Status RealDiskInterface::ReadFile(const string& path, int RealDiskInterface::RemoveFile(const string& path) { #ifdef _WIN32 - DWORD attributes = GetFileAttributesA(path.c_str()); + std::wstring wpath = ::toWide(path); + DWORD attributes = GetFileAttributesW(wpath.c_str()); if (attributes == INVALID_FILE_ATTRIBUTES) { DWORD win_err = GetLastError(); if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) { @@ -284,7 +321,7 @@ int RealDiskInterface::RemoveFile(const string& path) { // On Windows Ninja should behave the same: // https://github.com/ninja-build/ninja/issues/1886 // Skip error checking. If this fails, accept whatever happens below. - SetFileAttributesA(path.c_str(), attributes & ~FILE_ATTRIBUTE_READONLY); + SetFileAttributesW(wpath.c_str(), attributes & ~FILE_ATTRIBUTE_READONLY); } if (attributes & FILE_ATTRIBUTE_DIRECTORY) { // remove() deletes both files and directories. On Windows we have to @@ -292,7 +329,7 @@ int RealDiskInterface::RemoveFile(const string& path) { // used on a directory) // This fixes the behavior of ninja -t clean in some cases // https://github.com/ninja-build/ninja/issues/828 - if (!RemoveDirectoryA(path.c_str())) { + if (!RemoveDirectoryW(wpath.c_str())) { DWORD win_err = GetLastError(); if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) { return 1; @@ -302,7 +339,7 @@ int RealDiskInterface::RemoveFile(const string& path) { return -1; } } else { - if (!DeleteFileA(path.c_str())) { + if (!DeleteFileW(wpath.c_str())) { DWORD win_err = GetLastError(); if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) { return 1; diff --git a/src/util.cc b/src/util.cc index eefa3f50cd..501ae4abec 100644 --- a/src/util.cc +++ b/src/util.cc @@ -340,7 +340,8 @@ int ReadFile(const string& path, string* contents, string* err) { // This makes a ninja run on a set of 1500 manifest files about 4% faster // than using the generic fopen code below. err->clear(); - HANDLE f = ::CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, + std::wstring wpath = ::toWide(path); + HANDLE f = ::CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (f == INVALID_HANDLE_VALUE) { err->assign(GetLastErrorString()); @@ -477,6 +478,13 @@ void Win32Fatal(const char* function, const char* hint) { Fatal("%s: %s", function, GetLastErrorString().c_str()); } } + +std::wstring toWide(const string& s) { + int wchars_num = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0); + std::wstring w(wchars_num, ' '); + MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, (LPWSTR)w.data(), w.size()); + return w; +} #endif bool islatinalpha(int c) { diff --git a/src/util.h b/src/util.h index 4a7fea2258..a8b593ba28 100644 --- a/src/util.h +++ b/src/util.h @@ -126,6 +126,9 @@ std::string GetLastErrorString(); /// Calls Fatal() with a function name and GetLastErrorString. NORETURN void Win32Fatal(const char* function, const char* hint = NULL); + +/// Converts utf-8 std::string to wide char wstring +std::wstring toWide(const std::string& s); #endif #endif // NINJA_UTIL_H_