forked from OSchip/llvm-project
More long path name support on Windows, this time in program execution.
Allows long paths for the executable and redirected stdin/stdout/stderr. Addresses PR21563. llvm-svn: 222671
This commit is contained in:
parent
397a25e7cd
commit
c38deee807
|
@ -44,6 +44,7 @@ using namespace llvm;
|
||||||
|
|
||||||
using llvm::sys::windows::UTF8ToUTF16;
|
using llvm::sys::windows::UTF8ToUTF16;
|
||||||
using llvm::sys::windows::UTF16ToUTF8;
|
using llvm::sys::windows::UTF16ToUTF8;
|
||||||
|
using llvm::sys::path::widenPath;
|
||||||
|
|
||||||
static std::error_code windows_error(DWORD E) {
|
static std::error_code windows_error(DWORD E) {
|
||||||
return mapWindowsError(E);
|
return mapWindowsError(E);
|
||||||
|
@ -59,11 +60,15 @@ static bool is_separator(const wchar_t value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace llvm {
|
||||||
|
namespace sys {
|
||||||
|
namespace path {
|
||||||
|
|
||||||
// Convert a UTF-8 path to UTF-16. Also, if the absolute equivalent of the
|
// Convert a UTF-8 path to UTF-16. Also, if the absolute equivalent of the
|
||||||
// path is longer than CreateDirectory can tolerate, make it absolute and
|
// path is longer than CreateDirectory can tolerate, make it absolute and
|
||||||
// prefixed by '\\?\'.
|
// prefixed by '\\?\'.
|
||||||
static std::error_code widenPath(const Twine &Path8,
|
std::error_code widenPath(const Twine &Path8,
|
||||||
SmallVectorImpl<wchar_t> &Path16) {
|
SmallVectorImpl<wchar_t> &Path16) {
|
||||||
const size_t MaxDirLen = MAX_PATH - 12; // Must leave room for 8.3 filename.
|
const size_t MaxDirLen = MAX_PATH - 12; // Must leave room for 8.3 filename.
|
||||||
|
|
||||||
// Several operations would convert Path8 to SmallString; more efficient to
|
// Several operations would convert Path8 to SmallString; more efficient to
|
||||||
|
@ -111,9 +116,8 @@ static std::error_code widenPath(const Twine &Path8,
|
||||||
// Just use the caller's original path.
|
// Just use the caller's original path.
|
||||||
return UTF8ToUTF16(Path8Str, Path16);
|
return UTF8ToUTF16(Path8Str, Path16);
|
||||||
}
|
}
|
||||||
|
} // end namespace path
|
||||||
|
|
||||||
namespace llvm {
|
|
||||||
namespace sys {
|
|
||||||
namespace fs {
|
namespace fs {
|
||||||
|
|
||||||
std::string getMainExecutable(const char *argv0, void *MainExecAddr) {
|
std::string getMainExecutable(const char *argv0, void *MainExecAddr) {
|
||||||
|
|
|
@ -117,14 +117,19 @@ static HANDLE RedirectIO(const StringRef *path, int fd, std::string* ErrMsg) {
|
||||||
sa.bInheritHandle = TRUE;
|
sa.bInheritHandle = TRUE;
|
||||||
|
|
||||||
SmallVector<wchar_t, 128> fnameUnicode;
|
SmallVector<wchar_t, 128> fnameUnicode;
|
||||||
if (windows::UTF8ToUTF16(fname, fnameUnicode))
|
if (path->empty()) {
|
||||||
return INVALID_HANDLE_VALUE;
|
// Don't play long-path tricks on "NUL".
|
||||||
|
if (windows::UTF8ToUTF16(fname, fnameUnicode))
|
||||||
|
return INVALID_HANDLE_VALUE;
|
||||||
|
} else {
|
||||||
|
if (path::widenPath(fname, fnameUnicode))
|
||||||
|
return INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
h = CreateFileW(fnameUnicode.data(), fd ? GENERIC_WRITE : GENERIC_READ,
|
h = CreateFileW(fnameUnicode.data(), fd ? GENERIC_WRITE : GENERIC_READ,
|
||||||
FILE_SHARE_READ, &sa, fd == 0 ? OPEN_EXISTING : CREATE_ALWAYS,
|
FILE_SHARE_READ, &sa, fd == 0 ? OPEN_EXISTING : CREATE_ALWAYS,
|
||||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
if (h == INVALID_HANDLE_VALUE) {
|
if (h == INVALID_HANDLE_VALUE) {
|
||||||
MakeErrMsg(ErrMsg, std::string(fname) + ": Can't open file for " +
|
MakeErrMsg(ErrMsg, fname + ": Can't open file for " +
|
||||||
(fd ? "input: " : "output: "));
|
(fd ? "input: " : "output: "));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,7 +327,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **args,
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
|
|
||||||
SmallVector<wchar_t, MAX_PATH> ProgramUtf16;
|
SmallVector<wchar_t, MAX_PATH> ProgramUtf16;
|
||||||
if (std::error_code ec = windows::UTF8ToUTF16(Program, ProgramUtf16)) {
|
if (std::error_code ec = path::widenPath(Program, ProgramUtf16)) {
|
||||||
SetLastError(ec.value());
|
SetLastError(ec.value());
|
||||||
MakeErrMsg(ErrMsg,
|
MakeErrMsg(ErrMsg,
|
||||||
std::string("Unable to convert application name to UTF-16"));
|
std::string("Unable to convert application name to UTF-16"));
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#include "llvm/ADT/SmallVector.h"
|
#include "llvm/ADT/SmallVector.h"
|
||||||
#include "llvm/ADT/StringRef.h"
|
#include "llvm/ADT/StringRef.h"
|
||||||
|
#include "llvm/ADT/Twine.h"
|
||||||
#include "llvm/Config/config.h" // Get build system configuration settings
|
#include "llvm/Config/config.h" // Get build system configuration settings
|
||||||
#include "llvm/Support/Compiler.h"
|
#include "llvm/Support/Compiler.h"
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
|
@ -162,6 +163,11 @@ c_str(SmallVectorImpl<T> &str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace sys {
|
namespace sys {
|
||||||
|
namespace path {
|
||||||
|
std::error_code widenPath(const Twine &Path8,
|
||||||
|
SmallVectorImpl<wchar_t> &Path16);
|
||||||
|
} // end namespace path
|
||||||
|
|
||||||
namespace windows {
|
namespace windows {
|
||||||
std::error_code UTF8ToUTF16(StringRef utf8, SmallVectorImpl<wchar_t> &utf16);
|
std::error_code UTF8ToUTF16(StringRef utf8, SmallVectorImpl<wchar_t> &utf16);
|
||||||
std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len,
|
std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len,
|
||||||
|
|
|
@ -70,6 +70,56 @@ static void CopyEnvironment(std::vector<const char *> &out) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef LLVM_ON_WIN32
|
||||||
|
TEST(ProgramTest, CreateProcessLongPath) {
|
||||||
|
if (getenv("LLVM_PROGRAM_TEST_LONG_PATH"))
|
||||||
|
exit(0);
|
||||||
|
|
||||||
|
// getMainExecutable returns an absolute path; prepend the long-path prefix.
|
||||||
|
std::string MyAbsExe =
|
||||||
|
sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);
|
||||||
|
std::string MyExe;
|
||||||
|
if (!StringRef(MyAbsExe).startswith("\\\\?\\"))
|
||||||
|
MyExe.append("\\\\?\\");
|
||||||
|
MyExe.append(MyAbsExe);
|
||||||
|
|
||||||
|
const char *ArgV[] = {
|
||||||
|
MyExe.c_str(),
|
||||||
|
"--gtest_filter=ProgramTest.CreateProcessLongPath",
|
||||||
|
nullptr
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add LLVM_PROGRAM_TEST_LONG_PATH to the environment of the child.
|
||||||
|
std::vector<const char *> EnvP;
|
||||||
|
CopyEnvironment(EnvP);
|
||||||
|
EnvP.push_back("LLVM_PROGRAM_TEST_LONG_PATH=1");
|
||||||
|
EnvP.push_back(nullptr);
|
||||||
|
|
||||||
|
// Redirect stdout to a long path.
|
||||||
|
SmallString<128> TestDirectory;
|
||||||
|
ASSERT_NO_ERROR(
|
||||||
|
fs::createUniqueDirectory("program-redirect-test", TestDirectory));
|
||||||
|
SmallString<256> LongPath(TestDirectory);
|
||||||
|
LongPath.push_back('\\');
|
||||||
|
// MAX_PATH = 260
|
||||||
|
LongPath.append(260 - TestDirectory.size(), 'a');
|
||||||
|
StringRef LongPathRef(LongPath);
|
||||||
|
|
||||||
|
std::string Error;
|
||||||
|
bool ExecutionFailed;
|
||||||
|
const StringRef *Redirects[] = { nullptr, &LongPathRef, nullptr };
|
||||||
|
int RC = ExecuteAndWait(MyExe, ArgV, &EnvP[0], Redirects,
|
||||||
|
/*secondsToWait=*/ 10, /*memoryLimit=*/ 0, &Error,
|
||||||
|
&ExecutionFailed);
|
||||||
|
EXPECT_FALSE(ExecutionFailed) << Error;
|
||||||
|
EXPECT_EQ(0, RC);
|
||||||
|
|
||||||
|
// Remove the long stdout.
|
||||||
|
ASSERT_NO_ERROR(fs::remove(Twine(LongPath)));
|
||||||
|
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory)));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
TEST(ProgramTest, CreateProcessTrailingSlash) {
|
TEST(ProgramTest, CreateProcessTrailingSlash) {
|
||||||
if (getenv("LLVM_PROGRAM_TEST_CHILD")) {
|
if (getenv("LLVM_PROGRAM_TEST_CHILD")) {
|
||||||
if (ProgramTestStringArg1 == "has\\\\ trailing\\" &&
|
if (ProgramTestStringArg1 == "has\\\\ trailing\\" &&
|
||||||
|
|
Loading…
Reference in New Issue