forked from OSchip/llvm-project
clang-cl: Invent a /winsysroot concept
On non-Windows platforms, --sysroot can be used to make the compiler use a single, hermetic directory for all header and library files. This is useful, but difficult to do on Windows. After D95472 it's possible to achieve this with two flags: out/gn/bin/clang-cl win.c -fuse-ld=lld \ /vctoolsdir path/to/VC/Tools/MSVC/14.26.28801 \ /winsdkdir path/to/win_sdk But that's still cumbersome: It requires two flags instead of one, and it requires writing down the (changing) VC/Tools/MSVC version. This adds a new `/winsysroot <dir>` flag that's effectively an alias to these two flags. With this, building against a hermetic Windows toolchain only needs: out/gn/bin/clang-cl win.c -fuse-ld=lld /winsysroot path `/winsysroot <dir>` is the same as adding /vctoolsdir <dir>/VC/Tools/MSVC/<vctoolsver> /winsdkdir <dir>/Windows Kits/<winsdkmajorversion> `<vctoolsver>` is taken from `/vctoolsversion` if passed, or else it's the name of the directory in `<dir>/VC/Tools/MSVC` that's the highest numeric tuple. `<winsdkmajorversion>` is the major version in /winsdkversion if passed, else it's the name of the directory in `<dir>/Windows Kits` that's the highest number. So `/winsysroot <path>` requires this subfolder structure: path/ VC/ Tools/ MSVC/ 14.26.28801 (or another number) include/ ... Windows Kits/ 10/ Include/ 10.0.19041.0/ (or another number) um/ ... Lib/ 10.0.19041.0/ (or another number) um/ x64/ ... ... Differential Revision: https://reviews.llvm.org/D95534
This commit is contained in:
parent
d6b68d1344
commit
82847436e9
|
@ -5762,10 +5762,15 @@ 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_vctoolsversion : CLJoinedOrSeparate<"vctoolsversion">,
|
||||
HelpText<"For use with /winsysroot, defaults to newest found">;
|
||||
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">;
|
||||
HelpText<"Full version of the Windows SDK, defaults to newest found">;
|
||||
def _SLASH_winsysroot : CLJoinedOrSeparate<"winsysroot">,
|
||||
HelpText<"Same as /vctoolsdir <dir>/VC/Tools/MSVC/<vctoolsversion> /winsdkdir <dir>/Windows Kits/10">,
|
||||
MetaVarName<"<dir>">;
|
||||
def _SLASH_volatile_iso : Option<["/", "-"], "volatile:iso", KIND_FLAG>,
|
||||
Group<_SLASH_volatile_Group>, Flags<[CLOption, NoXarchOption]>,
|
||||
HelpText<"Volatile loads and stores have standard semantics">;
|
||||
|
|
|
@ -66,14 +66,49 @@ using namespace llvm::opt;
|
|||
static bool getSystemRegistryString(const char *keyPath, const char *valueName,
|
||||
std::string &value, std::string *phValue);
|
||||
|
||||
static std::string getHighestNumericTupleInDirectory(StringRef Directory) {
|
||||
std::string Highest;
|
||||
llvm::VersionTuple HighestTuple;
|
||||
|
||||
std::error_code EC;
|
||||
for (llvm::sys::fs::directory_iterator DirIt(Directory, EC), DirEnd;
|
||||
!EC && DirIt != DirEnd; DirIt.increment(EC)) {
|
||||
if (!llvm::sys::fs::is_directory(DirIt->path()))
|
||||
continue;
|
||||
StringRef CandidateName = llvm::sys::path::filename(DirIt->path());
|
||||
llvm::VersionTuple Tuple;
|
||||
if (Tuple.tryParse(CandidateName)) // tryParse() returns true on error.
|
||||
continue;
|
||||
if (Tuple > HighestTuple) {
|
||||
HighestTuple = Tuple;
|
||||
Highest = CandidateName.str();
|
||||
}
|
||||
}
|
||||
|
||||
return Highest;
|
||||
}
|
||||
|
||||
// Check command line arguments to try and find a toolchain.
|
||||
static bool
|
||||
findVCToolChainViaCommandLine(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.
|
||||
if (Arg *A = Args.getLastArg(options::OPT__SLASH_vctoolsdir)) {
|
||||
Path = A->getValue();
|
||||
if (Arg *A = Args.getLastArg(options::OPT__SLASH_vctoolsdir,
|
||||
options::OPT__SLASH_winsysroot)) {
|
||||
if (A->getOption().getID() == options::OPT__SLASH_winsysroot) {
|
||||
llvm::SmallString<128> ToolsPath(A->getValue());
|
||||
llvm::sys::path::append(ToolsPath, "VC", "Tools", "MSVC");
|
||||
std::string VCToolsVersion;
|
||||
if (Arg *A = Args.getLastArg(options::OPT__SLASH_vctoolsversion))
|
||||
VCToolsVersion = A->getValue();
|
||||
else
|
||||
VCToolsVersion = getHighestNumericTupleInDirectory(ToolsPath);
|
||||
llvm::sys::path::append(ToolsPath, VCToolsVersion);
|
||||
Path = std::string(ToolsPath.str());
|
||||
} else {
|
||||
Path = A->getValue();
|
||||
}
|
||||
VSLayout = MSVCToolChain::ToolsetLayout::VS2017OrNewer;
|
||||
return true;
|
||||
}
|
||||
|
@ -345,7 +380,8 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
|
|||
// they're doing. If the user passes /vctoolsdir or /winsdkdir, trust that
|
||||
// over env vars.
|
||||
if (!llvm::sys::Process::GetEnv("LIB") ||
|
||||
Args.getLastArg(options::OPT__SLASH_vctoolsdir)) {
|
||||
Args.getLastArg(options::OPT__SLASH_vctoolsdir,
|
||||
options::OPT__SLASH_winsysroot)) {
|
||||
CmdArgs.push_back(Args.MakeArgString(
|
||||
Twine("-libpath:") +
|
||||
TC.getSubDirectoryPath(
|
||||
|
@ -356,7 +392,8 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
|
|||
"atlmfc")));
|
||||
}
|
||||
if (!llvm::sys::Process::GetEnv("LIB") ||
|
||||
Args.getLastArg(options::OPT__SLASH_winsdkdir)) {
|
||||
Args.getLastArg(options::OPT__SLASH_winsdkdir,
|
||||
options::OPT__SLASH_winsysroot)) {
|
||||
if (TC.useUniversalCRT()) {
|
||||
std::string UniversalCRTLibPath;
|
||||
if (TC.getUniversalCRTLibraryPath(Args, UniversalCRTLibPath))
|
||||
|
@ -1069,42 +1106,38 @@ static bool getSystemRegistryString(const char *keyPath, const char *valueName,
|
|||
// So we compare entry names lexicographically to find the greatest one.
|
||||
static bool getWindows10SDKVersionFromPath(const std::string &SDKPath,
|
||||
std::string &SDKVersion) {
|
||||
SDKVersion.clear();
|
||||
|
||||
std::error_code EC;
|
||||
llvm::SmallString<128> IncludePath(SDKPath);
|
||||
llvm::sys::path::append(IncludePath, "Include");
|
||||
for (llvm::sys::fs::directory_iterator DirIt(IncludePath, EC), DirEnd;
|
||||
DirIt != DirEnd && !EC; DirIt.increment(EC)) {
|
||||
if (!llvm::sys::fs::is_directory(DirIt->path()))
|
||||
continue;
|
||||
StringRef CandidateName = llvm::sys::path::filename(DirIt->path());
|
||||
// If WDK is installed, there could be subfolders like "wdf" in the
|
||||
// "Include" directory.
|
||||
// Allow only directories which names start with "10.".
|
||||
if (!CandidateName.startswith("10."))
|
||||
continue;
|
||||
if (CandidateName > SDKVersion)
|
||||
SDKVersion = std::string(CandidateName);
|
||||
}
|
||||
|
||||
SDKVersion = getHighestNumericTupleInDirectory(IncludePath);
|
||||
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)) {
|
||||
if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkdir,
|
||||
options::OPT__SLASH_winsysroot)) {
|
||||
// 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;
|
||||
llvm::VersionTuple SDKVersion;
|
||||
if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkversion))
|
||||
SDKVersion.tryParse(A->getValue());
|
||||
|
||||
if (A->getOption().getID() == options::OPT__SLASH_winsysroot) {
|
||||
llvm::SmallString<128> SDKPath(A->getValue());
|
||||
llvm::sys::path::append(SDKPath, "Windows Kits");
|
||||
if (!SDKVersion.empty())
|
||||
llvm::sys::path::append(SDKPath, Twine(SDKVersion.getMajor()));
|
||||
else
|
||||
llvm::sys::path::append(SDKPath, getHighestNumericTupleInDirectory(SDKPath));
|
||||
Path = std::string(SDKPath.str());
|
||||
} else {
|
||||
Path = A->getValue();
|
||||
}
|
||||
|
||||
if (!SDKVersion.empty()) {
|
||||
Major = SDKVersion.getMajor();
|
||||
Version = SDKVersion.getAsString();
|
||||
} else if (getWindows10SDKVersionFromPath(Path, Version)) {
|
||||
Major = 10;
|
||||
}
|
||||
|
@ -1326,7 +1359,8 @@ void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
|
|||
|
||||
// Honor %INCLUDE%. It should know essential search paths with vcvarsall.bat.
|
||||
// Skip if the user expressly set a vctoolsdir
|
||||
if (!DriverArgs.getLastArg(options::OPT__SLASH_vctoolsdir)) {
|
||||
if (!DriverArgs.getLastArg(options::OPT__SLASH_vctoolsdir,
|
||||
options::OPT__SLASH_winsysroot)) {
|
||||
if (llvm::Optional<std::string> cl_include_dir =
|
||||
llvm::sys::Process::GetEnv("INCLUDE")) {
|
||||
SmallVector<StringRef, 8> Dirs;
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
// RUN: rm -rf %t
|
||||
// RUN: split-file %s %t
|
||||
|
||||
// RUN: %clang_cl /winsysroot %t -### -- %t/foo.cpp 2>&1 | FileCheck %s
|
||||
// RUN: %clang_cl /vctoolsdir %t/VC/Tools/MSVC/27.1828.18284 \
|
||||
// RUN: /winsdkdir "%t/Windows Kits/10" \
|
||||
// RUN: -### -- %t/foo.cpp 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: "-internal-isystem" "[[ROOT:[^"]*]]{{/|\\\\}}VC{{/|\\\\}}Tools{{/|\\\\}}MSVC{{/|\\\\}}27.1828.18284{{/|\\\\}}include"
|
||||
// CHECK: "-internal-isystem" "[[ROOT]]{{/|\\\\}}VC{{/|\\\\}}Tools{{/|\\\\}}MSVC{{/|\\\\}}27.1828.18284{{/|\\\\}}atlmfc{{/|\\\\}}include"
|
||||
// CHECK: "-internal-isystem" "[[ROOT]]{{/|\\\\}}Windows Kits{{/|\\\\}}10{{/|\\\\}}Include{{/|\\\\}}10.0.19041.0{{/|\\\\}}ucrt"
|
||||
// CHECK: "-internal-isystem" "[[ROOT]]{{/|\\\\}}Windows Kits{{/|\\\\}}10{{/|\\\\}}Include{{/|\\\\}}10.0.19041.0{{/|\\\\}}shared"
|
||||
// CHECK: "-internal-isystem" "[[ROOT]]{{/|\\\\}}Windows Kits{{/|\\\\}}10{{/|\\\\}}Include{{/|\\\\}}10.0.19041.0{{/|\\\\}}um"
|
||||
// CHECK: "-internal-isystem" "[[ROOT]]{{/|\\\\}}Windows Kits{{/|\\\\}}10{{/|\\\\}}Include{{/|\\\\}}10.0.19041.0{{/|\\\\}}winrt"
|
||||
|
||||
#--- VC/Tools/MSVC/27.1828.18284/include/string
|
||||
namespace std {
|
||||
class mystring {
|
||||
public:
|
||||
bool empty();
|
||||
};
|
||||
}
|
||||
|
||||
#--- Windows Kits/10/Include/10.0.19041.0/ucrt/assert.h
|
||||
#define myassert(X)
|
||||
|
||||
#--- foo.cpp
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
|
||||
void f() {
|
||||
std::mystring s;
|
||||
myassert(s.empty());
|
||||
}
|
Loading…
Reference in New Issue