[clang] 6d52c48 - Rewrite MSVC toolchain discovery with VFS
Arthur Eubanks via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 25 12:51:19 PST 2021
Author: Arthur Eubanks
Date: 2021-02-25T12:50:08-08:00
New Revision: 6d52c4819294dafb2c072011d72bb523092248a2
URL: https://github.com/llvm/llvm-project/commit/6d52c4819294dafb2c072011d72bb523092248a2
DIFF: https://github.com/llvm/llvm-project/commit/6d52c4819294dafb2c072011d72bb523092248a2.diff
LOG: Rewrite MSVC toolchain discovery with VFS
This fixes an issue where the toolchain discovery doesn't respect the
VFS's current working directory, specifically clangd not respecting a
relative /winsysroot.
Reviewed By: rnk
Differential Revision: https://reviews.llvm.org/D97437
Added:
Modified:
clang/lib/Driver/ToolChains/MSVC.cpp
Removed:
################################################################################
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index 0989620ef311..c95c5b61d6ae 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -27,6 +27,7 @@
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
+#include "llvm/Support/VirtualFileSystem.h"
#include <cstdio>
#ifdef _WIN32
@@ -61,19 +62,29 @@ using namespace clang::driver::tools;
using namespace clang;
using namespace llvm::opt;
+static bool canExecute(llvm::vfs::FileSystem &VFS, StringRef Path) {
+ auto Status = VFS.status(Path);
+ if (!Status)
+ return false;
+ return (Status->getPermissions() & llvm::sys::fs::perms::all_exe) != 0;
+}
+
// Defined below.
// Forward declare this so there aren't too many things above the constructor.
static bool getSystemRegistryString(const char *keyPath, const char *valueName,
std::string &value, std::string *phValue);
-static std::string getHighestNumericTupleInDirectory(StringRef Directory) {
+static std::string getHighestNumericTupleInDirectory(llvm::vfs::FileSystem &VFS,
+ StringRef Directory) {
std::string Highest;
llvm::VersionTuple HighestTuple;
std::error_code EC;
- for (llvm::sys::fs::directory_iterator DirIt(Directory, EC), DirEnd;
+ for (llvm::vfs::directory_iterator DirIt = VFS.dir_begin(Directory, EC),
+ DirEnd;
!EC && DirIt != DirEnd; DirIt.increment(EC)) {
- if (!llvm::sys::fs::is_directory(DirIt->path()))
+ auto Status = VFS.status(DirIt->path());
+ if (!Status || !Status->isDirectory())
continue;
StringRef CandidateName = llvm::sys::path::filename(DirIt->path());
llvm::VersionTuple Tuple;
@@ -90,7 +101,8 @@ static std::string getHighestNumericTupleInDirectory(StringRef Directory) {
// Check command line arguments to try and find a toolchain.
static bool
-findVCToolChainViaCommandLine(const ArgList &Args, std::string &Path,
+findVCToolChainViaCommandLine(llvm::vfs::FileSystem &VFS, const ArgList &Args,
+ std::string &Path,
MSVCToolChain::ToolsetLayout &VSLayout) {
// Don't validate the input; trust the value supplied by the user.
// The primary motivation is to prevent unnecessary file and registry access.
@@ -103,7 +115,7 @@ findVCToolChainViaCommandLine(const ArgList &Args, std::string &Path,
if (Arg *A = Args.getLastArg(options::OPT__SLASH_vctoolsversion))
VCToolsVersion = A->getValue();
else
- VCToolsVersion = getHighestNumericTupleInDirectory(ToolsPath);
+ VCToolsVersion = getHighestNumericTupleInDirectory(VFS, ToolsPath);
llvm::sys::path::append(ToolsPath, VCToolsVersion);
Path = std::string(ToolsPath.str());
} else {
@@ -117,7 +129,7 @@ findVCToolChainViaCommandLine(const ArgList &Args, std::string &Path,
// Check various environment variables to try and find a toolchain.
static bool
-findVCToolChainViaEnvironment(std::string &Path,
+findVCToolChainViaEnvironment(llvm::vfs::FileSystem &VFS, std::string &Path,
MSVCToolChain::ToolsetLayout &VSLayout) {
// These variables are typically set by vcvarsall.bat
// when launching a developer command prompt.
@@ -156,14 +168,14 @@ findVCToolChainViaEnvironment(std::string &Path,
// If cl.exe doesn't exist, then this definitely isn't a VC toolchain.
ExeTestPath = PathEntry;
llvm::sys::path::append(ExeTestPath, "cl.exe");
- if (!llvm::sys::fs::exists(ExeTestPath))
+ if (!VFS.exists(ExeTestPath))
continue;
// cl.exe existing isn't a conclusive test for a VC toolchain; clang also
// has a cl.exe. So let's check for link.exe too.
ExeTestPath = PathEntry;
llvm::sys::path::append(ExeTestPath, "link.exe");
- if (!llvm::sys::fs::exists(ExeTestPath))
+ if (!VFS.exists(ExeTestPath))
continue;
// whatever/VC/bin --> old toolchain, VC dir is toolchain dir.
@@ -229,8 +241,9 @@ findVCToolChainViaEnvironment(std::string &Path,
// and find its default VC toolchain.
// This is the preferred way to discover new Visual Studios, as they're no
// longer listed in the registry.
-static bool findVCToolChainViaSetupConfig(std::string &Path,
- MSVCToolChain::ToolsetLayout &VSLayout) {
+static bool
+findVCToolChainViaSetupConfig(llvm::vfs::FileSystem &VFS, std::string &Path,
+ MSVCToolChain::ToolsetLayout &VSLayout) {
#if !defined(USE_MSVC_SETUP_API)
return false;
#else
@@ -308,7 +321,8 @@ static bool findVCToolChainViaSetupConfig(std::string &Path,
llvm::SmallString<256> ToolchainPath(VCRootPath);
llvm::sys::path::append(ToolchainPath, "Tools", "MSVC",
ToolsVersionFile->get()->getBuffer().rtrim());
- if (!llvm::sys::fs::is_directory(ToolchainPath))
+ auto Status = VFS.status(ToolchainPath);
+ if (!Status || !Status->isDirectory())
return false;
Path = std::string(ToolchainPath.str());
@@ -350,8 +364,7 @@ static std::string FindVisualStudioExecutable(const ToolChain &TC,
SmallString<128> FilePath(MSVC.getSubDirectoryPath(
toolchains::MSVCToolChain::SubDirectoryType::Bin));
llvm::sys::path::append(FilePath, Exe);
- return std::string(llvm::sys::fs::can_execute(FilePath) ? FilePath.str()
- : Exe);
+ return std::string(canExecute(TC.getVFS(), FilePath) ? FilePath.str() : Exe);
}
void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
@@ -570,13 +583,13 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
// their own link.exe which may come first.
linkPath = FindVisualStudioExecutable(TC, "link.exe");
- if (!TC.FoundMSVCInstall() && !llvm::sys::fs::can_execute(linkPath)) {
+ if (!TC.FoundMSVCInstall() && !canExecute(TC.getVFS(), linkPath)) {
llvm::SmallString<128> ClPath;
ClPath = TC.GetProgramPath("cl.exe");
- if (llvm::sys::fs::can_execute(ClPath)) {
+ if (canExecute(TC.getVFS(), ClPath)) {
linkPath = llvm::sys::path::parent_path(ClPath);
llvm::sys::path::append(linkPath, "link.exe");
- if (!llvm::sys::fs::can_execute(linkPath))
+ if (!canExecute(TC.getVFS(), linkPath))
C.getDriver().Diag(clang::diag::warn_drv_msvc_not_found);
} else {
C.getDriver().Diag(clang::diag::warn_drv_msvc_not_found);
@@ -667,9 +680,9 @@ MSVCToolChain::MSVCToolChain(const Driver &D, const llvm::Triple &Triple,
// use. Check the environment next, in case we're being invoked from a VS
// command prompt. Failing that, just try to find the newest Visual Studio
// version we can and use its default VC toolchain.
- findVCToolChainViaCommandLine(Args, VCToolChainPath, VSLayout) ||
- findVCToolChainViaEnvironment(VCToolChainPath, VSLayout) ||
- findVCToolChainViaSetupConfig(VCToolChainPath, VSLayout) ||
+ findVCToolChainViaCommandLine(getVFS(), Args, VCToolChainPath, VSLayout) ||
+ findVCToolChainViaEnvironment(getVFS(), VCToolChainPath, VSLayout) ||
+ findVCToolChainViaSetupConfig(getVFS(), VCToolChainPath, VSLayout) ||
findVCToolChainViaRegistry(VCToolChainPath, VSLayout);
}
@@ -965,15 +978,17 @@ static bool getSystemRegistryString(const char *keyPath, const char *valueName,
// vcvarsqueryregistry.bat from Visual Studio 2015 sorts entries in the include
// directory by name and uses the last one of the list.
// So we compare entry names lexicographically to find the greatest one.
-static bool getWindows10SDKVersionFromPath(const std::string &SDKPath,
+static bool getWindows10SDKVersionFromPath(llvm::vfs::FileSystem &VFS,
+ const std::string &SDKPath,
std::string &SDKVersion) {
llvm::SmallString<128> IncludePath(SDKPath);
llvm::sys::path::append(IncludePath, "Include");
- SDKVersion = getHighestNumericTupleInDirectory(IncludePath);
+ SDKVersion = getHighestNumericTupleInDirectory(VFS, IncludePath);
return !SDKVersion.empty();
}
-static bool getWindowsSDKDirViaCommandLine(const ArgList &Args,
+static bool getWindowsSDKDirViaCommandLine(llvm::vfs::FileSystem &VFS,
+ const ArgList &Args,
std::string &Path, int &Major,
std::string &Version) {
if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkdir,
@@ -990,7 +1005,8 @@ static bool getWindowsSDKDirViaCommandLine(const ArgList &Args,
if (!SDKVersion.empty())
llvm::sys::path::append(SDKPath, Twine(SDKVersion.getMajor()));
else
- llvm::sys::path::append(SDKPath, getHighestNumericTupleInDirectory(SDKPath));
+ llvm::sys::path::append(
+ SDKPath, getHighestNumericTupleInDirectory(VFS, SDKPath));
Path = std::string(SDKPath.str());
} else {
Path = A->getValue();
@@ -999,7 +1015,7 @@ static bool getWindowsSDKDirViaCommandLine(const ArgList &Args,
if (!SDKVersion.empty()) {
Major = SDKVersion.getMajor();
Version = SDKVersion.getAsString();
- } else if (getWindows10SDKVersionFromPath(Path, Version)) {
+ } else if (getWindows10SDKVersionFromPath(VFS, Path, Version)) {
Major = 10;
}
return true;
@@ -1008,12 +1024,13 @@ static bool getWindowsSDKDirViaCommandLine(const ArgList &Args,
}
/// Get Windows SDK installation directory.
-static bool getWindowsSDKDir(const ArgList &Args, std::string &Path, int &Major,
+static bool getWindowsSDKDir(llvm::vfs::FileSystem &VFS, const ArgList &Args,
+ std::string &Path, int &Major,
std::string &WindowsSDKIncludeVersion,
std::string &WindowsSDKLibVersion) {
// Trust /winsdkdir and /winsdkversion if present.
- if (getWindowsSDKDirViaCommandLine(
- Args, Path, Major, WindowsSDKIncludeVersion)) {
+ if (getWindowsSDKDirViaCommandLine(VFS, Args, Path, Major,
+ WindowsSDKIncludeVersion)) {
WindowsSDKLibVersion = WindowsSDKIncludeVersion;
return true;
}
@@ -1043,7 +1060,7 @@ static bool getWindowsSDKDir(const ArgList &Args, std::string &Path, int &Major,
for (const char *Test : Tests) {
llvm::SmallString<128> TestPath(Path);
llvm::sys::path::append(TestPath, "Lib", Test);
- if (llvm::sys::fs::exists(TestPath.c_str())) {
+ if (VFS.exists(TestPath)) {
WindowsSDKLibVersion = Test;
break;
}
@@ -1051,7 +1068,7 @@ static bool getWindowsSDKDir(const ArgList &Args, std::string &Path, int &Major,
return !WindowsSDKLibVersion.empty();
}
if (Major == 10) {
- if (!getWindows10SDKVersionFromPath(Path, WindowsSDKIncludeVersion))
+ if (!getWindows10SDKVersionFromPath(VFS, Path, WindowsSDKIncludeVersion))
return false;
WindowsSDKLibVersion = WindowsSDKIncludeVersion;
return true;
@@ -1069,8 +1086,8 @@ bool MSVCToolChain::getWindowsSDKLibraryPath(
std::string windowsSDKLibVersion;
path.clear();
- if (!getWindowsSDKDir(Args, sdkPath, sdkMajor, windowsSDKIncludeVersion,
- windowsSDKLibVersion))
+ if (!getWindowsSDKDir(getVFS(), Args, sdkPath, sdkMajor,
+ windowsSDKIncludeVersion, windowsSDKLibVersion))
return false;
llvm::SmallString<128> libPath(sdkPath);
@@ -1104,15 +1121,16 @@ bool MSVCToolChain::useUniversalCRT() const {
llvm::SmallString<128> TestPath(
getSubDirectoryPath(SubDirectoryType::Include));
llvm::sys::path::append(TestPath, "stdlib.h");
- return !llvm::sys::fs::exists(TestPath);
+ return !getVFS().exists(TestPath);
}
-static bool getUniversalCRTSdkDir(const ArgList &Args, std::string &Path,
+static bool getUniversalCRTSdkDir(llvm::vfs::FileSystem &VFS,
+ const ArgList &Args, std::string &Path,
std::string &UCRTVersion) {
// If /winsdkdir is passed, use it as location for the UCRT too.
// FIXME: Should there be a dedicated /ucrtdir to override /winsdkdir?
int Major;
- if (getWindowsSDKDirViaCommandLine(Args, Path, Major, UCRTVersion))
+ if (getWindowsSDKDirViaCommandLine(VFS, Args, Path, Major, UCRTVersion))
return true;
// FIXME: Try env vars (%UniversalCRTSdkDir%, %UCRTVersion%) before going to
@@ -1125,7 +1143,7 @@ static bool getUniversalCRTSdkDir(const ArgList &Args, std::string &Path,
Path, nullptr))
return false;
- return getWindows10SDKVersionFromPath(Path, UCRTVersion);
+ return getWindows10SDKVersionFromPath(VFS, Path, UCRTVersion);
}
bool MSVCToolChain::getUniversalCRTLibraryPath(const ArgList &Args,
@@ -1134,7 +1152,7 @@ bool MSVCToolChain::getUniversalCRTLibraryPath(const ArgList &Args,
std::string UCRTVersion;
Path.clear();
- if (!getUniversalCRTSdkDir(Args, UniversalCRTSdkPath, UCRTVersion))
+ if (!getUniversalCRTSdkDir(getVFS(), Args, UniversalCRTSdkPath, UCRTVersion))
return false;
StringRef ArchName = llvmArchToWindowsSDKArch(getArch());
@@ -1245,7 +1263,8 @@ void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
if (useUniversalCRT()) {
std::string UniversalCRTSdkPath;
std::string UCRTVersion;
- if (getUniversalCRTSdkDir(DriverArgs, UniversalCRTSdkPath, UCRTVersion)) {
+ if (getUniversalCRTSdkDir(getVFS(), DriverArgs, UniversalCRTSdkPath,
+ UCRTVersion)) {
AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, UniversalCRTSdkPath,
"Include", UCRTVersion, "ucrt");
}
@@ -1255,7 +1274,7 @@ void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
int major;
std::string windowsSDKIncludeVersion;
std::string windowsSDKLibVersion;
- if (getWindowsSDKDir(DriverArgs, WindowsSDKDir, major,
+ if (getWindowsSDKDir(getVFS(), DriverArgs, WindowsSDKDir, major,
windowsSDKIncludeVersion, windowsSDKLibVersion)) {
if (major >= 8) {
// Note: windowsSDKIncludeVersion is empty for SDKs prior to v10.
More information about the cfe-commits
mailing list