Index: include/clang/Basic/DiagnosticDriverKinds.td =================================================================== --- include/clang/Basic/DiagnosticDriverKinds.td +++ include/clang/Basic/DiagnosticDriverKinds.td @@ -277,4 +277,8 @@ def err_drv_unsupported_linker : Error<"unsupported value '%0' for -linker option">; def err_drv_defsym_invalid_format : Error<"defsym must be of the form: sym=value: %0">; def err_drv_defsym_invalid_symval : Error<"Value is not an integer: %0">; + +def err_drv_msvc_not_found : Error< + "unable to find a Visual Studio installation; " + "try running Clang from a developer command prompt">; } Index: lib/Driver/MSVCToolChain.cpp =================================================================== --- lib/Driver/MSVCToolChain.cpp +++ lib/Driver/MSVCToolChain.cpp @@ -23,16 +23,20 @@ #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include -// Include the necessary headers to interface with the Windows registry and -// environment. #if defined(LLVM_ON_WIN32) -#define USE_WIN32 + #define USE_WIN32 + #if 0 + #define USE_VS_SETUP_CONFIG + #endif #endif +// Include the necessary headers to interface with the Windows registry and +// environment. #ifdef USE_WIN32 #define WIN32_LEAN_AND_MEAN #define NOGDI @@ -42,20 +46,265 @@ #include #endif +// Include the headers needed for the setup config COM stuff and define +// smart pointers for the interfaces we need. +#ifdef USE_VS_SETUP_CONFIG + #include "clang/Basic/VirtualFileSystem.h" + #include "llvm/Support/COM.h" + #include + #include + _COM_SMARTPTR_TYPEDEF(ISetupConfiguration, __uuidof(ISetupConfiguration)); + _COM_SMARTPTR_TYPEDEF(ISetupConfiguration2, __uuidof(ISetupConfiguration2)); + _COM_SMARTPTR_TYPEDEF(ISetupHelper, __uuidof(ISetupHelper)); + _COM_SMARTPTR_TYPEDEF(IEnumSetupInstances, __uuidof(IEnumSetupInstances)); + _COM_SMARTPTR_TYPEDEF(ISetupInstance, __uuidof(ISetupInstance)); + _COM_SMARTPTR_TYPEDEF(ISetupInstance2, __uuidof(ISetupInstance2)); +#endif + using namespace clang::driver; using namespace clang::driver::toolchains; using namespace clang; using namespace llvm::opt; -MSVCToolChain::MSVCToolChain(const Driver &D, const llvm::Triple &Triple, +// 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); + +// Check various environment variables to try and find a toolchain. +static bool findVCToolChainViaEnvironment(std::string &Path, + bool &IsVS2017OrNewer) { + // These variables are typically set by vcvarsall.bat + // when launching a developer command prompt. + if (llvm::Optional VCToolsInstallDir = + llvm::sys::Process::GetEnv("VCToolsInstallDir")) { + // This is only set by newer Visual Studios, and it leads straight to + // the toolchain directory. + Path = std::move(*VCToolsInstallDir); + IsVS2017OrNewer = true; + return true; + } + if (llvm::Optional VCInstallDir = + llvm::sys::Process::GetEnv("VCINSTALLDIR")) { + // If the previous variable isn't set but this one is, then we've found + // an older Visual Studio. This variable is set by newer Visual Studios too, + // so this check has to appear second. + // In older Visual Studios, the VC directory is the toolchain. + Path = std::move(*VCInstallDir); + IsVS2017OrNewer = false; + return true; + } + + // We couldn't find any VC environment variables. Let's walk through PATH and + // see if it leads us to a VC toolchain bin directory. If it does, pick the + // first one that we find. + if (llvm::Optional PathEnv = + llvm::sys::Process::GetEnv("PATH")) { + llvm::SmallVector PathEntries; + llvm::StringRef(*PathEnv).split(PathEntries, llvm::sys::EnvPathSeparator); + for (llvm::StringRef PathEntry : PathEntries) { + if (PathEntry.empty()) + continue; + + llvm::SmallString<256> ExeTestPath; + + // 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)) + 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)) + continue; + + // whatever/VC/bin --> old toolchain, VC dir is toolchain dir. + if (llvm::sys::path::filename(PathEntry) == "bin") { + llvm::StringRef ParentPath = llvm::sys::path::parent_path(PathEntry); + if (llvm::sys::path::filename(ParentPath) == "VC") { + Path = ParentPath; + IsVS2017OrNewer = false; + return true; + } + } else { + // This could be a new (>=VS2017) toolchain. If it is, we should find + // path components with these prefixes when walking backwards through + // the path. + // Note: empty strings match anything. + llvm::StringRef ExpectedPrefixes[] = + { "", "Host", "bin", "", "MSVC", "Tools", "VC" }; + + llvm::sys::path::reverse_iterator + It = llvm::sys::path::rbegin(PathEntry), + End = llvm::sys::path::rend(PathEntry); + for (llvm::StringRef Prefix : ExpectedPrefixes) { + if (It == End) goto NotAToolChain; + if (!It->startswith(Prefix)) goto NotAToolChain; + ++It; + } + + // We've found a new toolchain! + // Back up 3 times (/bin/Host/arch) to get the root path. + llvm::StringRef ToolChainPath(PathEntry); + for (int i = 0; i < 3; ++i) + ToolChainPath = llvm::sys::path::parent_path(ToolChainPath); + + Path = ToolChainPath; + IsVS2017OrNewer = true; + return true; + } + + NotAToolChain: + continue; + } + } + return false; +} + +// Query the Setup Config server for installs, then pick the newest version +// 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, + bool &IsVS2017OrNewer) { +#ifndef USE_VS_SETUP_CONFIG + return false; +#else + llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::SingleThreaded); + HRESULT HR; + + // _com_ptr_t will throw a _com_error if a COM calls fail. + // The LLVM coding standards forbid exception handling, so we'll have to + // stop them from being thrown in the first place. + // The destructor will put the regular error handler back when we leave + // this scope. + struct SuppressCOMErrorsRAII { + SuppressCOMErrorsRAII() { + _set_com_error_handler([](HRESULT, IErrorInfo *) { }); + } + ~SuppressCOMErrorsRAII() { + _set_com_error_handler(_com_raise_error); + } + } COMErrorSuppressor; + + ISetupConfigurationPtr Query; + HR = Query.CreateInstance(__uuidof(SetupConfiguration)); + if (FAILED(HR)) return false; + + IEnumSetupInstancesPtr EnumInstances; + HR = ISetupConfiguration2Ptr(Query)->EnumAllInstances(&EnumInstances); + if (FAILED(HR)) return false; + + ISetupInstancePtr Instance; + HR = EnumInstances->Next(1, &Instance, nullptr); + if (HR != S_OK) return false; + + ISetupInstancePtr NewestInstance(Instance); + uint64_t NewestVersionNum; + { + bstr_t VersionString; + HR = NewestInstance->GetInstallationVersion(VersionString.GetAddress()); + if (FAILED(HR)) return false; + HR = ISetupHelperPtr(Query)->ParseVersion(VersionString, + &NewestVersionNum); + if (FAILED(HR)) return false; + } + + while ((HR = EnumInstances->Next(1, &Instance, nullptr)) == S_OK) { + bstr_t VersionString; + uint64_t VersionNum; + HR = Instance->GetInstallationVersion(VersionString.GetAddress()); + if (FAILED(HR)) continue; + HR = ISetupHelperPtr(Query)->ParseVersion(VersionString, + &VersionNum); + if (FAILED(HR)) continue; + if (VersionNum > NewestVersionNum) { + NewestInstance = Instance; + NewestVersionNum = VersionNum; + } + } + + bstr_t VCPathWide; + HR = NewestInstance->ResolvePath(L"VC", + VCPathWide.GetAddress()); + if (FAILED(HR)) return false; + + std::string VCRootPath; + llvm::convertWideToUTF8(std::wstring(VCPathWide), VCRootPath); + + llvm::SmallString<256> ToolsVersionFilePath(VCRootPath); + llvm::sys::path::append(ToolsVersionFilePath, + "Auxiliary", + "Build", + "Microsoft.VCToolsVersion.default.txt"); + + auto ToolsVersionFile = + clang::vfs::getRealFileSystem()->getBufferForFile(ToolsVersionFilePath); + if (!ToolsVersionFile) + return false; + + llvm::SmallString<256> ToolchainPath(VCRootPath); + llvm::sys::path::append(ToolchainPath, + "Tools", + "MSVC", + ToolsVersionFile->get()->getBuffer().rtrim()); + if (!llvm::sys::fs::is_directory(ToolchainPath)) + return false; + + Path = ToolchainPath.str(); + IsVS2017OrNewer = true; + return true; +#endif /*USE_VS_SETUP_CONFIG*/ +} + +// Look in the registry for Visual Studio installs, and use that to get +// a toolchain path. VS2017 and newer don't get added to the registry. +// So if we find something here, we know that it's an older version. +static bool findVCToolChainViaRegistry(std::string &Path, + bool &IsVS2017OrNewer) { + std::string VSInstallPath; + if (getSystemRegistryString(R"(SOFTWARE\Microsoft\VisualStudio\$VERSION)", + "InstallDir", VSInstallPath, nullptr) || + getSystemRegistryString(R"(SOFTWARE\Microsoft\VCExpress\$VERSION)", + "InstallDir", VSInstallPath, nullptr)) { + if (!VSInstallPath.empty()) { + llvm::SmallString<256> + VCPath(llvm::StringRef(VSInstallPath.c_str(), + VSInstallPath.find(R"(\Common7\IDE)"))); + llvm::sys::path::append(VCPath, "VC"); + + Path = VCPath.str(); + IsVS2017OrNewer = false; + return true; + } + } + return false; +} + +MSVCToolChain::MSVCToolChain(const Driver &D, const llvm::Triple& Triple, const ArgList &Args) : ToolChain(D, Triple, Args), CudaInstallation(D, Triple, Args) { getProgramPaths().push_back(getDriver().getInstalledDir()); if (getDriver().getInstalledDir() != getDriver().Dir) getProgramPaths().push_back(getDriver().Dir); + + // Check the environment first, since that's probably the user telling us + // what they want to use. + // Failing that, just try to find the newest Visual Studio version we can + // and use its default VC toolchain. + findVCToolChainViaEnvironment(VCToolChainPath, IsVS2017OrNewer) + || findVCToolChainViaSetupConfig(VCToolChainPath, IsVS2017OrNewer) + || findVCToolChainViaRegistry(VCToolChainPath, IsVS2017OrNewer); } Tool *MSVCToolChain::buildLinker() const { + if (VCToolChainPath.empty()) { + getDriver().Diag(clang::diag::err_drv_msvc_not_found); + return nullptr; + } return new tools::visualstudio::Linker(*this); } @@ -103,6 +352,77 @@ CudaInstallation.print(OS); } +// Windows SDKs and VC Toolchains group their contents into subdirectories based +// on the target architecture. This function converts an llvm::Triple::ArchType +// to the corresponding subdirectory name. +static const char *llvmArchToWindowsSDKArch(llvm::Triple::ArchType Arch) { + using ArchType = llvm::Triple::ArchType; + switch (Arch) { + case ArchType::x86: + return "x86"; + case ArchType::x86_64: + return "x64"; + case ArchType::arm: + return "arm"; + default: + return ""; + } +} + +// Similar to the above function, but for Visual Studios before VS2017. +static const char *llvmArchToLegacyVCArch(llvm::Triple::ArchType Arch) { + using ArchType = llvm::Triple::ArchType; + switch (Arch) { + case ArchType::x86: + // x86 is default in legacy VC toolchains. + // e.g. x86 libs are directly in /lib as opposed to /lib/x86. + return ""; + case ArchType::x86_64: + return "amd64"; + case ArchType::arm: + return "arm"; + default: + return ""; + } +} + +// Get the path to a specific subdirectory in the current toolchain for +// a given target architecture. +// VS2017 changed the VC toolchain layout, so this should be used instead +// of hardcoding paths. +std::string + MSVCToolChain::getSubDirectoryPath(SubDirectoryType Type, + llvm::Triple::ArchType TargetArch) const { + llvm::SmallString<256> Path(VCToolChainPath); + switch (Type) { + case SubDirectoryType::Bin: + if (IsVS2017OrNewer) { + bool HostIsX64 = llvm::Triple(llvm::sys::getProcessTriple()).isArch64Bit(); + llvm::sys::path::append(Path, + "bin", + (HostIsX64 ? "HostX64" : "HostX86"), + llvmArchToWindowsSDKArch(TargetArch)); + } + else { + llvm::sys::path::append(Path, + "bin", + llvmArchToLegacyVCArch(TargetArch)); + } + break; + case SubDirectoryType::Include: + llvm::sys::path::append(Path, "include"); + break; + case SubDirectoryType::Lib: + llvm::sys::path::append(Path, + "lib", + IsVS2017OrNewer + ? llvmArchToWindowsSDKArch(TargetArch) + : llvmArchToLegacyVCArch(TargetArch)); + break; + } + return Path.str(); +} + #ifdef USE_WIN32 static bool readFullStringValue(HKEY hkey, const char *valueName, std::string &value) { @@ -232,27 +552,12 @@ #endif // USE_WIN32 } -// Convert LLVM's ArchType -// to the corresponding name of Windows SDK libraries subfolder -static StringRef getWindowsSDKArch(llvm::Triple::ArchType Arch) { - switch (Arch) { - case llvm::Triple::x86: - return "x86"; - case llvm::Triple::x86_64: - return "x64"; - case llvm::Triple::arm: - return "arm"; - default: - return ""; - } -} - // Find the most recent version of Universal CRT or Windows 10 SDK. // 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 getWindows10SDKVersion(const std::string &SDKPath, - std::string &SDKVersion) { +static bool getWindows10SDKVersionFromPath(const std::string &SDKPath, + std::string &SDKVersion) { SDKVersion.clear(); std::error_code EC; @@ -276,9 +581,9 @@ } /// \brief Get Windows SDK installation directory. -bool MSVCToolChain::getWindowsSDKDir(std::string &Path, int &Major, - std::string &WindowsSDKIncludeVersion, - std::string &WindowsSDKLibVersion) const { +static bool getWindowsSDKDir(std::string &Path, int &Major, + std::string &WindowsSDKIncludeVersion, + std::string &WindowsSDKLibVersion) { std::string RegistrySDKVersion; // Try the Windows registry. if (!getSystemRegistryString( @@ -310,7 +615,7 @@ return !WindowsSDKLibVersion.empty(); } if (Major == 10) { - if (!getWindows10SDKVersion(Path, WindowsSDKIncludeVersion)) + if (!getWindows10SDKVersionFromPath(Path, WindowsSDKIncludeVersion)) return false; WindowsSDKLibVersion = WindowsSDKIncludeVersion; return true; @@ -333,9 +638,14 @@ llvm::SmallString<128> libPath(sdkPath); llvm::sys::path::append(libPath, "Lib"); - if (sdkMajor <= 7) { + if (sdkMajor >= 8) { + llvm::sys::path::append(libPath, + windowsSDKLibVersion, + "um", + llvmArchToWindowsSDKArch(getArch())); + } else { switch (getArch()) { - // In Windows SDK 7.x, x86 libraries are directly in the Lib folder. + // In Windows SDK 7.x, x86 libraries are directly in the Lib folder. case llvm::Triple::x86: break; case llvm::Triple::x86_64: @@ -347,11 +657,6 @@ default: return false; } - } else { - const StringRef archName = getWindowsSDKArch(getArch()); - if (archName.empty()) - return false; - llvm::sys::path::append(libPath, windowsSDKLibVersion, "um", archName); } path = libPath.str(); @@ -360,24 +665,22 @@ // Check if the Include path of a specified version of Visual Studio contains // specific header files. If not, they are probably shipped with Universal CRT. -bool clang::driver::toolchains::MSVCToolChain::useUniversalCRT( - std::string &VisualStudioDir) const { - llvm::SmallString<128> TestPath(VisualStudioDir); - llvm::sys::path::append(TestPath, "VC\\include\\stdlib.h"); - +bool MSVCToolChain::useUniversalCRT() const { + llvm::SmallString<128> TestPath(getSubDirectoryPath(SubDirectoryType::Include)); + llvm::sys::path::append(TestPath, "stdlib.h"); return !llvm::sys::fs::exists(TestPath); } -bool MSVCToolChain::getUniversalCRTSdkDir(std::string &Path, - std::string &UCRTVersion) const { +static bool getUniversalCRTSdkDir(std::string &Path, + std::string &UCRTVersion) { // vcvarsqueryregistry.bat for Visual Studio 2015 queries the registry // for the specific key "KitsRoot10". So do we. if (!getSystemRegistryString( - "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10", - Path, nullptr)) + "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", + "KitsRoot10", Path, nullptr)) return false; - return getWindows10SDKVersion(Path, UCRTVersion); + return getWindows10SDKVersionFromPath(Path, UCRTVersion); } bool MSVCToolChain::getUniversalCRTLibraryPath(std::string &Path) const { @@ -388,7 +691,7 @@ if (!getUniversalCRTSdkDir(UniversalCRTSdkPath, UCRTVersion)) return false; - StringRef ArchName = getWindowsSDKArch(getArch()); + StringRef ArchName = llvmArchToWindowsSDKArch(getArch()); if (ArchName.empty()) return false; @@ -399,104 +702,18 @@ return true; } -// Get the location to use for Visual Studio binaries. The location priority -// is: %VCINSTALLDIR% > %PATH% > newest copy of Visual Studio installed on -// system (as reported by the registry). -bool MSVCToolChain::getVisualStudioBinariesFolder(const char *clangProgramPath, - std::string &path) const { - path.clear(); - - SmallString<128> BinDir; - - // First check the environment variables that vsvars32.bat sets. - llvm::Optional VcInstallDir = - llvm::sys::Process::GetEnv("VCINSTALLDIR"); - if (VcInstallDir.hasValue()) { - BinDir = VcInstallDir.getValue(); - llvm::sys::path::append(BinDir, "bin"); - } else { - // Next walk the PATH, trying to find a cl.exe in the path. If we find one, - // use that. However, make sure it's not clang's cl.exe. - llvm::Optional OptPath = llvm::sys::Process::GetEnv("PATH"); - if (OptPath.hasValue()) { - const char EnvPathSeparatorStr[] = {llvm::sys::EnvPathSeparator, '\0'}; - SmallVector PathSegments; - llvm::SplitString(OptPath.getValue(), PathSegments, EnvPathSeparatorStr); - - for (StringRef PathSegment : PathSegments) { - if (PathSegment.empty()) - continue; - - SmallString<128> FilePath(PathSegment); - llvm::sys::path::append(FilePath, "cl.exe"); - // Checking if cl.exe exists is a small optimization over calling - // can_execute, which really only checks for existence but will also do - // extra checks for cl.exe.exe. These add up when walking a long path. - if (llvm::sys::fs::exists(FilePath.c_str()) && - !llvm::sys::fs::equivalent(FilePath.c_str(), clangProgramPath)) { - // If we found it on the PATH, use it exactly as is with no - // modifications. - path = PathSegment; - return true; - } - } - } - - std::string installDir; - // With no VCINSTALLDIR and nothing on the PATH, if we can't find it in the - // registry then we have no choice but to fail. - if (!getVisualStudioInstallDir(installDir)) - return false; - - // Regardless of what binary we're ultimately trying to find, we make sure - // that this is a Visual Studio directory by checking for cl.exe. We use - // cl.exe instead of other binaries like link.exe because programs such as - // GnuWin32 also have a utility called link.exe, so cl.exe is the least - // ambiguous. - BinDir = installDir; - llvm::sys::path::append(BinDir, "VC", "bin"); - SmallString<128> ClPath(BinDir); - llvm::sys::path::append(ClPath, "cl.exe"); - - if (!llvm::sys::fs::can_execute(ClPath.c_str())) - return false; - } - - if (BinDir.empty()) - return false; - - switch (getArch()) { - case llvm::Triple::x86: - break; - case llvm::Triple::x86_64: - llvm::sys::path::append(BinDir, "amd64"); - break; - case llvm::Triple::arm: - llvm::sys::path::append(BinDir, "arm"); - break; - default: - // Whatever this is, Visual Studio doesn't have a toolchain for it. - return false; - } - path = BinDir.str(); - return true; -} - -VersionTuple MSVCToolChain::getMSVCVersionFromTriple() const { +static VersionTuple getMSVCVersionFromTriple(const llvm::Triple &Triple) { unsigned Major, Minor, Micro; - getTriple().getEnvironmentVersion(Major, Minor, Micro); + Triple.getEnvironmentVersion(Major, Minor, Micro); if (Major || Minor || Micro) return VersionTuple(Major, Minor, Micro); return VersionTuple(); } -VersionTuple MSVCToolChain::getMSVCVersionFromExe() const { +static VersionTuple getMSVCVersionFromExe(const std::string &BinDir) { VersionTuple Version; #ifdef USE_WIN32 - std::string BinPath; - if (!getVisualStudioBinariesFolder("", BinPath)) - return Version; - SmallString<128> ClExe(BinPath); + SmallString<128> ClExe(BinDir); llvm::sys::path::append(ClExe, "cl.exe"); std::wstring ClExeWide; @@ -529,62 +746,6 @@ return Version; } -// Get Visual Studio installation directory. -bool MSVCToolChain::getVisualStudioInstallDir(std::string &path) const { - // First check the environment variables that vsvars32.bat sets. - if (llvm::Optional VcInstallDir = - llvm::sys::Process::GetEnv("VCINSTALLDIR")) { - path = std::move(*VcInstallDir); - path = path.substr(0, path.find("\\VC")); - return true; - } - - std::string vsIDEInstallDir; - std::string vsExpressIDEInstallDir; - // Then try the windows registry. - bool hasVCDir = - getSystemRegistryString("SOFTWARE\\Microsoft\\VisualStudio\\$VERSION", - "InstallDir", vsIDEInstallDir, nullptr); - if (hasVCDir && !vsIDEInstallDir.empty()) { - path = vsIDEInstallDir.substr(0, vsIDEInstallDir.find("\\Common7\\IDE")); - return true; - } - - bool hasVCExpressDir = - getSystemRegistryString("SOFTWARE\\Microsoft\\VCExpress\\$VERSION", - "InstallDir", vsExpressIDEInstallDir, nullptr); - if (hasVCExpressDir && !vsExpressIDEInstallDir.empty()) { - path = vsExpressIDEInstallDir.substr( - 0, vsIDEInstallDir.find("\\Common7\\IDE")); - return true; - } - - // Try the environment. - std::string vcomntools; - if (llvm::Optional vs120comntools = - llvm::sys::Process::GetEnv("VS120COMNTOOLS")) - vcomntools = std::move(*vs120comntools); - else if (llvm::Optional vs100comntools = - llvm::sys::Process::GetEnv("VS100COMNTOOLS")) - vcomntools = std::move(*vs100comntools); - else if (llvm::Optional vs90comntools = - llvm::sys::Process::GetEnv("VS90COMNTOOLS")) - vcomntools = std::move(*vs90comntools); - else if (llvm::Optional vs80comntools = - llvm::sys::Process::GetEnv("VS80COMNTOOLS")) - vcomntools = std::move(*vs80comntools); - - // Find any version we can. - if (!vcomntools.empty()) { - size_t p = vcomntools.find("\\Common7\\Tools"); - if (p != std::string::npos) - vcomntools.resize(p); - path = std::move(vcomntools); - return true; - } - return false; -} - void MSVCToolChain::AddSystemIncludeWithSubfolder( const ArgList &DriverArgs, ArgStringList &CC1Args, const std::string &folder, const Twine &subfolder1, const Twine &subfolder2, @@ -623,14 +784,14 @@ return; } - std::string VSDir; - // When built with access to the proper Windows APIs, try to actually find // the correct include paths first. - if (getVisualStudioInstallDir(VSDir)) { - AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, VSDir, "VC\\include"); + if (!VCToolChainPath.empty()) { + addSystemInclude(DriverArgs, + CC1Args, + getSubDirectoryPath(SubDirectoryType::Include)); - if (useUniversalCRT(VSDir)) { + if (useUniversalCRT()) { std::string UniversalCRTSdkPath; std::string UCRTVersion; if (getUniversalCRTSdkDir(UniversalCRTSdkPath, UCRTVersion)) { @@ -661,9 +822,8 @@ AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir, "include"); } - } else { - addSystemInclude(DriverArgs, CC1Args, VSDir); } + return; } @@ -690,8 +850,10 @@ const ArgList &Args) const { bool IsWindowsMSVC = getTriple().isWindowsMSVCEnvironment(); VersionTuple MSVT = ToolChain::computeMSVCVersion(D, Args); - if (MSVT.empty()) MSVT = getMSVCVersionFromTriple(); - if (MSVT.empty() && IsWindowsMSVC) MSVT = getMSVCVersionFromExe(); + if (MSVT.empty()) + MSVT = getMSVCVersionFromTriple(getTriple()); + if (MSVT.empty() && IsWindowsMSVC) + MSVT = getMSVCVersionFromExe(getSubDirectoryPath(SubDirectoryType::Bin)); if (MSVT.empty() && Args.hasFlag(options::OPT_fms_extensions, options::OPT_fno_ms_extensions, IsWindowsMSVC)) { Index: lib/Driver/ToolChains.h =================================================================== --- lib/Driver/ToolChains.h +++ lib/Driver/ToolChains.h @@ -1141,6 +1141,11 @@ }; class LLVM_LIBRARY_VISIBILITY MSVCToolChain : public ToolChain { + std::string VCToolChainPath; + bool IsVS2017OrNewer; + + CudaInstallationDetector CudaInstallation; + public: MSVCToolChain(const Driver &D, const llvm::Triple &Triple, const llvm::opt::ArgList &Args); @@ -1155,6 +1160,22 @@ bool isPIEDefault() const override; bool isPICDefaultForced() const override; + enum class SubDirectoryType { + Bin, + Include, + Lib, + }; + std::string getSubDirectoryPath(SubDirectoryType Type, + llvm::Triple::ArchType TargetArch) const; + + // Convenience overload. + // Uses the current target arch. + std::string getSubDirectoryPath(SubDirectoryType Type) const { + return getSubDirectoryPath(Type, getArch()); + } + + bool getIsVS2017OrNewer() const { return IsVS2017OrNewer; } + void AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args) const override; @@ -1165,17 +1186,10 @@ void AddCudaIncludeArgs(const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args) const override; - bool getWindowsSDKDir(std::string &path, int &major, - std::string &windowsSDKIncludeVersion, - std::string &windowsSDKLibVersion) const; bool getWindowsSDKLibraryPath(std::string &path) const; /// \brief Check if Universal CRT should be used if available - bool useUniversalCRT(std::string &visualStudioDir) const; - bool getUniversalCRTSdkDir(std::string &path, std::string &ucrtVersion) const; + bool useUniversalCRT() const; bool getUniversalCRTLibraryPath(std::string &path) const; - bool getVisualStudioInstallDir(std::string &path) const; - bool getVisualStudioBinariesFolder(const char *clangProgramPath, - std::string &path) const; VersionTuple computeMSVCVersion(const Driver *D, const llvm::opt::ArgList &Args) const override; @@ -1196,11 +1210,6 @@ Tool *buildLinker() const override; Tool *buildAssembler() const override; -private: - VersionTuple getMSVCVersionFromTriple() const; - VersionTuple getMSVCVersionFromExe() const; - - CudaInstallationDetector CudaInstallation; }; class LLVM_LIBRARY_VISIBILITY CrossWindowsToolChain : public Generic_GCC { Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -10809,19 +10809,12 @@ // making sure that whatever executable that's found is not a same-named exe // from clang itself to prevent clang from falling back to itself. static std::string FindVisualStudioExecutable(const ToolChain &TC, - const char *Exe, - const char *ClangProgramPath) { + const char *Exe) { const auto &MSVC = static_cast(TC); - std::string visualStudioBinDir; - if (MSVC.getVisualStudioBinariesFolder(ClangProgramPath, - visualStudioBinDir)) { - SmallString<128> FilePath(visualStudioBinDir); - llvm::sys::path::append(FilePath, Exe); - if (llvm::sys::fs::can_execute(FilePath.c_str())) - return FilePath.str(); - } - - return Exe; + SmallString<128> FilePath(MSVC.getSubDirectoryPath(toolchains::MSVCToolChain + ::SubDirectoryType::Bin)); + llvm::sys::path::append(FilePath, Exe); + return (llvm::sys::fs::can_execute(FilePath) ? FilePath.str() : Exe); } void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, @@ -10830,7 +10823,7 @@ const ArgList &Args, const char *LinkingOutput) const { ArgStringList CmdArgs; - const ToolChain &TC = getToolChain(); + auto &TC = static_cast(getToolChain()); assert((Output.isFilename() || Output.isNothing()) && "invalid output"); if (Output.isFilename()) @@ -10846,37 +10839,20 @@ // did not run vcvarsall), try to build a consistent link environment. If // the environment variable is set however, assume the user knows what // they're doing. - std::string VisualStudioDir; - const auto &MSVC = static_cast(TC); - if (MSVC.getVisualStudioInstallDir(VisualStudioDir)) { - SmallString<128> LibDir(VisualStudioDir); - llvm::sys::path::append(LibDir, "VC", "lib"); - switch (MSVC.getArch()) { - case llvm::Triple::x86: - // x86 just puts the libraries directly in lib - break; - case llvm::Triple::x86_64: - llvm::sys::path::append(LibDir, "amd64"); - break; - case llvm::Triple::arm: - llvm::sys::path::append(LibDir, "arm"); - break; - default: - break; - } - CmdArgs.push_back( - Args.MakeArgString(std::string("-libpath:") + LibDir.c_str())); + CmdArgs.push_back(Args.MakeArgString( + std::string("-libpath:") + + TC.getSubDirectoryPath(toolchains::MSVCToolChain + ::SubDirectoryType::Lib))); - if (MSVC.useUniversalCRT(VisualStudioDir)) { - std::string UniversalCRTLibPath; - if (MSVC.getUniversalCRTLibraryPath(UniversalCRTLibPath)) - CmdArgs.push_back(Args.MakeArgString(std::string("-libpath:") + - UniversalCRTLibPath)); - } + if (TC.useUniversalCRT()) { + std::string UniversalCRTLibPath; + if (TC.getUniversalCRTLibraryPath(UniversalCRTLibPath)) + CmdArgs.push_back(Args.MakeArgString(std::string("-libpath:") + + UniversalCRTLibPath)); } std::string WindowsSdkLibPath; - if (MSVC.getWindowsSDKLibraryPath(WindowsSdkLibPath)) + if (TC.getWindowsSDKLibraryPath(WindowsSdkLibPath)) CmdArgs.push_back( Args.MakeArgString(std::string("-libpath:") + WindowsSdkLibPath)); } @@ -10991,8 +10967,7 @@ // If we're using the MSVC linker, it's not sufficient to just use link // from the program PATH, because other environments like GnuWin32 install // their own link.exe which may come first. - linkPath = FindVisualStudioExecutable(TC, "link.exe", - C.getDriver().getClangProgramPath()); + linkPath = FindVisualStudioExecutable(TC, "link.exe"); } else { linkPath = Linker; llvm::sys::path::replace_extension(linkPath, "exe"); @@ -11125,9 +11100,7 @@ Args.MakeArgString(std::string("/Fo") + Output.getFilename()); CmdArgs.push_back(Fo); - const Driver &D = getToolChain().getDriver(); - std::string Exec = FindVisualStudioExecutable(getToolChain(), "cl.exe", - D.getClangProgramPath()); + std::string Exec = FindVisualStudioExecutable(getToolChain(), "cl.exe"); return llvm::make_unique(JA, *this, Args.MakeArgString(Exec), CmdArgs, Inputs); }