Author: Nico Weber Date: 2021-01-29T09:47:00-05:00 New Revision: 82847436e9258a12503dcfadb5dc373cb42fea43
URL: https://github.com/llvm/llvm-project/commit/82847436e9258a12503dcfadb5dc373cb42fea43 DIFF: https://github.com/llvm/llvm-project/commit/82847436e9258a12503dcfadb5dc373cb42fea43.diff LOG: 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 Added: clang/test/Driver/cl-sysroot.cpp Modified: clang/include/clang/Driver/Options.td clang/lib/Driver/ToolChains/MSVC.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 77f13e21bb18..845f5be55ae7 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -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">; diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp index 9253ca6c2cb4..1fc517cebd1c 100644 --- a/clang/lib/Driver/ToolChains/MSVC.cpp +++ b/clang/lib/Driver/ToolChains/MSVC.cpp @@ -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; diff --git a/clang/test/Driver/cl-sysroot.cpp b/clang/test/Driver/cl-sysroot.cpp new file mode 100644 index 000000000000..ae82d4e15964 --- /dev/null +++ b/clang/test/Driver/cl-sysroot.cpp @@ -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()); +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits