clang-cl: Add /winsdkdir and /winsdkversion flags

These do for the Windows SDK path what D85998 did for
%VCToolsInstallDir% with /vctoolsdir: Offer a way to set them with an
explicit commandline switch.

With this (and /vctoolsdir), it's possible to compile and link
against hermetic vctools and winsdk directories with:

    out/gn/bin/clang-cl win.c -fuse-ld=lld \
        /vctoolsdir path/to/VC/Tools/MSVC/14.26.28801 \
        /winsdkdir path/to/win_sdk

compared to a long list of -imsvc and /link /libpath: flags.

While here:
- Change the case of the "Include" folder inside the windows sdk
  from "include" to "Include" to match on-disk case. Since the
  Windows file system is case-insensitive this isn't a behavior
  change, it's just a bit cleaner.
- Add libpath tests to the /vctoolsdir
- Add a FIXME about reading env vars for win sdk and ucrt sdk
  if these flags aren't present, to match the VCToolsInstallDir
  logic

We should also cache all these computed paths in the driver instead
of computing them every time they're queried, but that's for a future
patch.

It'd also be nice to invent a /winsysroot: flag that sets both
/vctoolsdir: and /winsdkdir: to some well-known subdirectory.
That's for a future patch as well.

Differential Revision: https://reviews.llvm.org/D95472
This commit is contained in:
Nico Weber
2021-01-26 15:21:12 -05:00
parent 00fcc03687
commit a5d85cbec5
4 changed files with 79 additions and 22 deletions

View File

@@ -5753,6 +5753,10 @@ def _SLASH_Tp : CLCompileJoinedOrSeparate<"Tp">,
def _SLASH_TP : CLCompileFlag<"TP">, HelpText<"Treat all source files as C++">;
def _SLASH_vctoolsdir : CLJoinedOrSeparate<"vctoolsdir">,
HelpText<"Path to the VCToolChain">, MetaVarName<"<dir>">;
def _SLASH_winsdkdir : CLJoinedOrSeparate<"winsdkdir">,
HelpText<"Path to the Windows SDK">, MetaVarName<"<dir>">;
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">;

View File

@@ -81,8 +81,9 @@ findVCToolChainViaCommandLine(const ArgList &Args, std::string &Path,
}
// 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<std::string> VCToolsInstallDir =
@@ -355,13 +356,13 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
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 @@ static bool getWindows10SDKVersionFromPath(const std::string &SDKPath,
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 @@ static bool getWindowsSDKDir(std::string &Path, int &Major,
}
// 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 @@ bool MSVCToolChain::useUniversalCRT() const {
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 @@ static bool getUniversalCRTSdkDir(std::string &Path, std::string &UCRTVersion) {
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 @@ void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
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 @@ void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
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");
}
}

View File

@@ -126,9 +126,10 @@ public:
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,

View File

@@ -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() { }