diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -5753,6 +5753,10 @@ def _SLASH_TP : CLCompileFlag<"TP">, HelpText<"Treat all source files as C++">; def _SLASH_vctoolsdir : CLJoinedOrSeparate<"vctoolsdir">, HelpText<"Path to the VCToolChain">, MetaVarName<"">; +def _SLASH_winsdkdir : CLJoinedOrSeparate<"winsdkdir">, + HelpText<"Path to the Windows SDK">, MetaVarName<"">; +def _SLASH_winsdkversion : CLJoinedOrSeparate<"winsdkversion">, + HelpText<"Full version of the Windows SDK">; def _SLASH_volatile_iso : Option<["/", "-"], "volatile:iso", KIND_FLAG>, Group<_SLASH_volatile_Group>, Flags<[CLOption, NoXarchOption]>, HelpText<"Volatile loads and stores have standard semantics">; diff --git a/clang/lib/Driver/ToolChains/MSVC.h b/clang/lib/Driver/ToolChains/MSVC.h --- a/clang/lib/Driver/ToolChains/MSVC.h +++ b/clang/lib/Driver/ToolChains/MSVC.h @@ -126,9 +126,10 @@ void AddHIPIncludeArgs(const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args) const override; - bool getWindowsSDKLibraryPath(std::string &path) const; - /// Check if Universal CRT should be used if available - bool getUniversalCRTLibraryPath(std::string &path) const; + bool getWindowsSDKLibraryPath( + const llvm::opt::ArgList &Args, std::string &path) const; + bool getUniversalCRTLibraryPath(const llvm::opt::ArgList &Args, + std::string &path) const; bool useUniversalCRT() const; VersionTuple computeMSVCVersion(const Driver *D, diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp --- a/clang/lib/Driver/ToolChains/MSVC.cpp +++ b/clang/lib/Driver/ToolChains/MSVC.cpp @@ -81,8 +81,9 @@ } // Check various environment variables to try and find a toolchain. -static bool findVCToolChainViaEnvironment(std::string &Path, - MSVCToolChain::ToolsetLayout &VSLayout) { +static bool +findVCToolChainViaEnvironment(std::string &Path, + MSVCToolChain::ToolsetLayout &VSLayout) { // These variables are typically set by vcvarsall.bat // when launching a developer command prompt. if (llvm::Optional VCToolsInstallDir = @@ -355,13 +356,13 @@ if (TC.useUniversalCRT()) { std::string UniversalCRTLibPath; - if (TC.getUniversalCRTLibraryPath(UniversalCRTLibPath)) + if (TC.getUniversalCRTLibraryPath(Args, UniversalCRTLibPath)) CmdArgs.push_back( Args.MakeArgString(Twine("-libpath:") + UniversalCRTLibPath)); } std::string WindowsSdkLibPath; - if (TC.getWindowsSDKLibraryPath(WindowsSdkLibPath)) + if (TC.getWindowsSDKLibraryPath(Args, WindowsSdkLibPath)) CmdArgs.push_back( Args.MakeArgString(std::string("-libpath:") + WindowsSdkLibPath)); } @@ -1088,12 +1089,43 @@ return !SDKVersion.empty(); } +static bool getWindowsSDKDirViaCommandLine(const ArgList &Args, + std::string &Path, int &Major, + std::string &Version) { + if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkdir)) { + // Don't validate the input; trust the value supplied by the user. + // The motivation is to prevent unnecessary file and registry access. + Path = A->getValue(); + if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkversion)) { + StringRef WinSdkVersion = A->getValue(); + Version = WinSdkVersion.str(); + if (WinSdkVersion.consumeInteger(10, Major)) + return false; + if (!(WinSdkVersion.empty() || WinSdkVersion.startswith("."))) + return false; + } else if (getWindows10SDKVersionFromPath(Path, Version)) { + Major = 10; + } + return true; + } + return false; +} + /// Get Windows SDK installation directory. -static bool getWindowsSDKDir(std::string &Path, int &Major, +static bool getWindowsSDKDir(const ArgList &Args, std::string &Path, int &Major, std::string &WindowsSDKIncludeVersion, std::string &WindowsSDKLibVersion) { - std::string RegistrySDKVersion; + // Trust /winsdkdir: and /winsdkversion: if present. + if (getWindowsSDKDirViaCommandLine( + Args, Path, Major, WindowsSDKIncludeVersion)) { + WindowsSDKLibVersion = WindowsSDKIncludeVersion; + return true; + } + + // FIXME: Try env vars (%WindowsSdkDir%, %UCRTVersion%) before going to registry. + // Try the Windows registry. + std::string RegistrySDKVersion; if (!getSystemRegistryString( "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\$VERSION", "InstallationFolder", Path, &RegistrySDKVersion)) @@ -1133,14 +1165,15 @@ } // Gets the library path required to link against the Windows SDK. -bool MSVCToolChain::getWindowsSDKLibraryPath(std::string &path) const { +bool MSVCToolChain::getWindowsSDKLibraryPath( + const ArgList &Args, std::string &path) const { std::string sdkPath; int sdkMajor = 0; std::string windowsSDKIncludeVersion; std::string windowsSDKLibVersion; path.clear(); - if (!getWindowsSDKDir(sdkPath, sdkMajor, windowsSDKIncludeVersion, + if (!getWindowsSDKDir(Args, sdkPath, sdkMajor, windowsSDKIncludeVersion, windowsSDKLibVersion)) return false; @@ -1178,7 +1211,17 @@ return !llvm::sys::fs::exists(TestPath); } -static bool getUniversalCRTSdkDir(std::string &Path, std::string &UCRTVersion) { +static bool getUniversalCRTSdkDir(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)) + return true; + + // FIXME: Try env vars (%UniversalCRTSdkDir%, %UCRTVersion%) before going to + // registry. + // vcvarsqueryregistry.bat for Visual Studio 2015 queries the registry // for the specific key "KitsRoot10". So do we. if (!getSystemRegistryString( @@ -1189,12 +1232,13 @@ return getWindows10SDKVersionFromPath(Path, UCRTVersion); } -bool MSVCToolChain::getUniversalCRTLibraryPath(std::string &Path) const { +bool MSVCToolChain::getUniversalCRTLibraryPath(const ArgList &Args, + std::string &Path) const { std::string UniversalCRTSdkPath; std::string UCRTVersion; Path.clear(); - if (!getUniversalCRTSdkDir(UniversalCRTSdkPath, UCRTVersion)) + if (!getUniversalCRTSdkDir(Args, UniversalCRTSdkPath, UCRTVersion)) return false; StringRef ArchName = llvmArchToWindowsSDKArch(getArch()); @@ -1304,7 +1348,7 @@ if (useUniversalCRT()) { std::string UniversalCRTSdkPath; std::string UCRTVersion; - if (getUniversalCRTSdkDir(UniversalCRTSdkPath, UCRTVersion)) { + if (getUniversalCRTSdkDir(DriverArgs, UniversalCRTSdkPath, UCRTVersion)) { AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, UniversalCRTSdkPath, "Include", UCRTVersion, "ucrt"); } @@ -1314,23 +1358,23 @@ int major; std::string windowsSDKIncludeVersion; std::string windowsSDKLibVersion; - if (getWindowsSDKDir(WindowsSDKDir, major, windowsSDKIncludeVersion, - windowsSDKLibVersion)) { + if (getWindowsSDKDir(DriverArgs, WindowsSDKDir, major, + windowsSDKIncludeVersion, windowsSDKLibVersion)) { if (major >= 8) { // Note: windowsSDKIncludeVersion is empty for SDKs prior to v10. // Anyway, llvm::sys::path::append is able to manage it. AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir, - "include", windowsSDKIncludeVersion, + "Include", windowsSDKIncludeVersion, "shared"); AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir, - "include", windowsSDKIncludeVersion, + "Include", windowsSDKIncludeVersion, "um"); AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir, - "include", windowsSDKIncludeVersion, + "Include", windowsSDKIncludeVersion, "winrt"); } else { AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir, - "include"); + "Include"); } } diff --git a/clang/test/Driver/cl-options.c b/clang/test/Driver/cl-options.c --- a/clang/test/Driver/cl-options.c +++ b/clang/test/Driver/cl-options.c @@ -696,8 +696,16 @@ // VCTOOLSDIR: "-triple" "{{[a-zA-Z0-9_-]*}}-pc-windows-msvc19.11.0" // Validate that built-in include paths are based on the supplied path -// RUN: %clang_cl -vctoolsdir "/fake" -### -- %s 2>&1 | FileCheck %s --check-prefix FAKEDIR +// RUN: %clang_cl -vctoolsdir "/fake" -winsdkdir "/foo" -winsdkversion 10.0.12345.0 -### -- %s 2>&1 | FileCheck %s --check-prefix FAKEDIR // FAKEDIR: "-internal-isystem" "/fake{{/|\\\\}}include" // FAKEDIR: "-internal-isystem" "/fake{{/|\\\\}}atlmfc{{/|\\\\}}include" +// FAKEDIR: "-internal-isystem" "/foo{{/|\\\\}}Include{{/|\\\\}}10.0.12345.0{{/|\\\\}}ucrt" +// FAKEDIR: "-internal-isystem" "/foo{{/|\\\\}}Include{{/|\\\\}}10.0.12345.0{{/|\\\\}}shared" +// FAKEDIR: "-internal-isystem" "/foo{{/|\\\\}}Include{{/|\\\\}}10.0.12345.0{{/|\\\\}}um" +// FAKEDIR: "-internal-isystem" "/foo{{/|\\\\}}Include{{/|\\\\}}10.0.12345.0{{/|\\\\}}winrt" +// FAKEDIR: "-libpath:/fake{{/|\\\\}}lib{{/|\\\\}} +// FAKEDIR: "-libpath:/fake{{/|\\\\}}atlmfc{{/|\\\\}}lib{{/|\\\\}} +// FAKEDIR: "-libpath:/foo{{/|\\\\}}Lib{{/|\\\\}}10.0.12345.0{{/|\\\\}}ucrt +// FAKEDIR: "-libpath:/foo{{/|\\\\}}Lib{{/|\\\\}}10.0.12345.0{{/|\\\\}}um void f() { }