Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang] Support --sysroot= for ${arch}-windows-msvc targets #96417

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

trcrsired
Copy link

I think it is possible to use the same rule for msvc targets with --target= and --sysroot=

See Repository:
https://github.com/trcrsired/windows-msvc-sysroot
Headers
Windows + CRT Headers Include Directory: include

C++ standard library headers:
With -stdlib=stl, headers should be located in include/c++/stl

With -stdlib=libc++, headers should be located in include/c++/v1

Libraries
Libraries should be placed in lib/$TRIPLET

For example. on x86_64-windows-msvc, it should find libs in lib/x86_64-windows-msvc

Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be
notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write
permissions for the repository. In which case you can instead tag reviewers by
name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review
by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate
is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' labels Jun 23, 2024
@llvmbot
Copy link
Member

llvmbot commented Jun 23, 2024

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-driver

Author: cqwrteur (trcrsired)

Changes

I think it is possible to use the same rule for msvc targets with --target= and --sysroot=

See Repository:
https://github.com/trcrsired/windows-msvc-sysroot
Headers
Windows + CRT Headers Include Directory: include

C++ standard library headers:
With -stdlib=stl, headers should be located in include/c++/stl

With -stdlib=libc++, headers should be located in include/c++/v1

Libraries
Libraries should be placed in lib/$TRIPLET

For example. on x86_64-windows-msvc, it should find libs in lib/x86_64-windows-msvc


Full diff: https://github.com/llvm/llvm-project/pull/96417.diff

5 Files Affected:

  • (modified) clang/include/clang/Driver/ToolChain.h (+6-3)
  • (modified) clang/lib/Driver/ToolChain.cpp (+9)
  • (modified) clang/lib/Driver/ToolChains/MSVC.cpp (+187-61)
  • (modified) clang/lib/Driver/ToolChains/MSVC.h (+19-5)
  • (added) clang/test/Driver/msvc-sysroot.cpp (+11)
diff --git a/clang/include/clang/Driver/ToolChain.h b/clang/include/clang/Driver/ToolChain.h
index 1f93bd612e9b0..04535a98dd69c 100644
--- a/clang/include/clang/Driver/ToolChain.h
+++ b/clang/include/clang/Driver/ToolChain.h
@@ -95,18 +95,21 @@ class ToolChain {
 
   enum CXXStdlibType {
     CST_Libcxx,
-    CST_Libstdcxx
+    CST_Libstdcxx,
+    CST_Stl,
   };
 
   enum RuntimeLibType {
     RLT_CompilerRT,
-    RLT_Libgcc
+    RLT_Libgcc,
+    RLT_Vcruntime
   };
 
   enum UnwindLibType {
     UNW_None,
     UNW_CompilerRT,
-    UNW_Libgcc
+    UNW_Libgcc,
+    UNW_Vcruntime
   };
 
   enum class UnwindTableLevel {
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 40ab2e91125d1..b3ed8fc6de36d 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -1091,6 +1091,8 @@ ToolChain::RuntimeLibType ToolChain::GetRuntimeLibType(
     runtimeLibType = ToolChain::RLT_CompilerRT;
   else if (LibName == "libgcc")
     runtimeLibType = ToolChain::RLT_Libgcc;
+  else if (LibName == "vcruntime")
+    runtimeLibType = ToolChain::RLT_Vcruntime;
   else if (LibName == "platform")
     runtimeLibType = GetDefaultRuntimeLibType();
   else {
@@ -1129,6 +1131,8 @@ ToolChain::UnwindLibType ToolChain::GetUnwindLibType(
     unwindLibType = ToolChain::UNW_CompilerRT;
   } else if (LibName == "libgcc")
     unwindLibType = ToolChain::UNW_Libgcc;
+  else if (LibName == "vcruntime")
+    unwindLibType = ToolChain::UNW_Vcruntime;
   else {
     if (A)
       getDriver().Diag(diag::err_drv_invalid_unwindlib_name)
@@ -1152,6 +1156,8 @@ ToolChain::CXXStdlibType ToolChain::GetCXXStdlibType(const ArgList &Args) const{
     cxxStdlibType = ToolChain::CST_Libcxx;
   else if (LibName == "libstdc++")
     cxxStdlibType = ToolChain::CST_Libstdcxx;
+  else if (LibName == "stl")
+    cxxStdlibType = ToolChain::CST_Stl;
   else if (LibName == "platform")
     cxxStdlibType = GetDefaultCXXStdlibType();
   else {
@@ -1290,6 +1296,9 @@ void ToolChain::AddCXXStdlibLibArgs(const ArgList &Args,
   case ToolChain::CST_Libstdcxx:
     CmdArgs.push_back("-lstdc++");
     break;
+
+  default:
+    break;
   }
 }
 
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index ca266e3e1d1d3..bf1b6d3b9bc84 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -31,12 +31,12 @@
 #include <cstdio>
 
 #ifdef _WIN32
-  #define WIN32_LEAN_AND_MEAN
-  #define NOGDI
-  #ifndef NOMINMAX
-    #define NOMINMAX
-  #endif
-  #include <windows.h>
+#define WIN32_LEAN_AND_MEAN
+#define NOGDI
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#include <windows.h>
 #endif
 
 using namespace clang::driver;
@@ -95,43 +95,52 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
   // the environment variable is set however, assume the user knows what
   // they're doing. If the user passes /vctoolsdir or /winsdkdir, trust that
   // over env vars.
-  if (const Arg *A = Args.getLastArg(options::OPT__SLASH_diasdkdir,
-                                     options::OPT__SLASH_winsysroot)) {
-    // cl.exe doesn't find the DIA SDK automatically, so this too requires
-    // explicit flags and doesn't automatically look in "DIA SDK" relative
-    // to the path we found for VCToolChainPath.
-    llvm::SmallString<128> DIAPath(A->getValue());
-    if (A->getOption().getID() == options::OPT__SLASH_winsysroot)
-      llvm::sys::path::append(DIAPath, "DIA SDK");
-
-    // The DIA SDK always uses the legacy vc arch, even in new MSVC versions.
-    llvm::sys::path::append(DIAPath, "lib",
-                            llvm::archToLegacyVCArch(TC.getArch()));
-    CmdArgs.push_back(Args.MakeArgString(Twine("-libpath:") + DIAPath));
-  }
-  if (!llvm::sys::Process::GetEnv("LIB") ||
-      Args.getLastArg(options::OPT__SLASH_vctoolsdir,
-                      options::OPT__SLASH_winsysroot)) {
-    CmdArgs.push_back(Args.MakeArgString(
-        Twine("-libpath:") +
-        TC.getSubDirectoryPath(llvm::SubDirectoryType::Lib)));
-    CmdArgs.push_back(Args.MakeArgString(
-        Twine("-libpath:") +
-        TC.getSubDirectoryPath(llvm::SubDirectoryType::Lib, "atlmfc")));
-  }
-  if (!llvm::sys::Process::GetEnv("LIB") ||
-      Args.getLastArg(options::OPT__SLASH_winsdkdir,
-                      options::OPT__SLASH_winsysroot)) {
-    if (TC.useUniversalCRT()) {
-      std::string UniversalCRTLibPath;
-      if (TC.getUniversalCRTLibraryPath(Args, UniversalCRTLibPath))
+  auto SysRoot = TC.getDriver().SysRoot;
+  if (SysRoot.empty()) {
+    if (const Arg *A = Args.getLastArg(options::OPT__SLASH_diasdkdir,
+                                       options::OPT__SLASH_winsysroot)) {
+      // cl.exe doesn't find the DIA SDK automatically, so this too requires
+      // explicit flags and doesn't automatically look in "DIA SDK" relative
+      // to the path we found for VCToolChainPath.
+      llvm::SmallString<128> DIAPath(A->getValue());
+      if (A->getOption().getID() == options::OPT__SLASH_winsysroot)
+        llvm::sys::path::append(DIAPath, "DIA SDK");
+
+      // The DIA SDK always uses the legacy vc arch, even in new MSVC versions.
+      llvm::sys::path::append(DIAPath, "lib",
+                              llvm::archToLegacyVCArch(TC.getArch()));
+      CmdArgs.push_back(Args.MakeArgString(Twine("-libpath:") + DIAPath));
+    }
+    if (!llvm::sys::Process::GetEnv("LIB") ||
+        Args.getLastArg(options::OPT__SLASH_vctoolsdir,
+                        options::OPT__SLASH_winsysroot)) {
+      CmdArgs.push_back(Args.MakeArgString(
+          Twine("-libpath:") +
+          TC.getSubDirectoryPath(llvm::SubDirectoryType::Lib)));
+      CmdArgs.push_back(Args.MakeArgString(
+          Twine("-libpath:") +
+          TC.getSubDirectoryPath(llvm::SubDirectoryType::Lib, "atlmfc")));
+    }
+    if (!llvm::sys::Process::GetEnv("LIB") ||
+        Args.getLastArg(options::OPT__SLASH_winsdkdir,
+                        options::OPT__SLASH_winsysroot)) {
+      if (TC.useUniversalCRT()) {
+        std::string UniversalCRTLibPath;
+        if (TC.getUniversalCRTLibraryPath(Args, UniversalCRTLibPath))
+          CmdArgs.push_back(
+              Args.MakeArgString(Twine("-libpath:") + UniversalCRTLibPath));
+      }
+      std::string WindowsSdkLibPath;
+      if (TC.getWindowsSDKLibraryPath(Args, WindowsSdkLibPath))
         CmdArgs.push_back(
-            Args.MakeArgString(Twine("-libpath:") + UniversalCRTLibPath));
+            Args.MakeArgString(std::string("-libpath:") + WindowsSdkLibPath));
     }
-    std::string WindowsSdkLibPath;
-    if (TC.getWindowsSDKLibraryPath(Args, WindowsSdkLibPath))
-      CmdArgs.push_back(
-          Args.MakeArgString(std::string("-libpath:") + WindowsSdkLibPath));
+  } else {
+    const std::string MultiarchTriple =
+        TC.getMultiarchTriple(TC.getDriver(), TC.getTriple(), SysRoot);
+    std::string SysRootLib = "-libpath:" + SysRoot + "/lib";
+    CmdArgs.push_back(Args.MakeArgString(SysRootLib + '/' + MultiarchTriple));
+    CmdArgs.push_back(Args.MakeArgString(SysRootLib));
   }
 
   if (!C.getDriver().IsCLMode() && Args.hasArg(options::OPT_L))
@@ -207,13 +216,14 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
         CmdArgs.push_back(TC.getCompilerRTArgString(Args, Lib));
       // Make sure the dynamic runtime thunk is not optimized out at link time
       // to ensure proper SEH handling.
-      CmdArgs.push_back(Args.MakeArgString(
-          TC.getArch() == llvm::Triple::x86
-              ? "-include:___asan_seh_interceptor"
-              : "-include:__asan_seh_interceptor"));
+      CmdArgs.push_back(
+          Args.MakeArgString(TC.getArch() == llvm::Triple::x86
+                                 ? "-include:___asan_seh_interceptor"
+                                 : "-include:__asan_seh_interceptor"));
       // Make sure the linker consider all object files from the dynamic runtime
       // thunk.
-      CmdArgs.push_back(Args.MakeArgString(std::string("-wholearchive:") +
+      CmdArgs.push_back(Args.MakeArgString(
+          std::string("-wholearchive:") +
           TC.getCompilerRT(Args, "asan_dynamic_runtime_thunk")));
     } else if (DLL) {
       CmdArgs.push_back(TC.getCompilerRTArgString(Args, "asan_dll_thunk"));
@@ -224,7 +234,7 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
         // This is necessary because instrumented dlls need access to all the
         // interface exported by the static lib in the main executable.
         CmdArgs.push_back(Args.MakeArgString(std::string("-wholearchive:") +
-            TC.getCompilerRT(Args, Lib)));
+                                             TC.getCompilerRT(Args, Lib)));
       }
     }
   }
@@ -430,6 +440,11 @@ MSVCToolChain::MSVCToolChain(const Driver &D, const llvm::Triple &Triple,
       RocmInstallation(D, Triple, Args) {
   getProgramPaths().push_back(getDriver().Dir);
 
+  auto SysRoot = getDriver().SysRoot;
+  if (!SysRoot.empty()) {
+    return;
+  }
+
   std::optional<llvm::StringRef> VCToolsDir, VCToolsVersion;
   if (Arg *A = Args.getLastArg(options::OPT__SLASH_vctoolsdir))
     VCToolsDir = A->getValue();
@@ -602,8 +617,8 @@ static VersionTuple getMSVCVersionFromExe(const std::string &BinDir) {
   if (!llvm::ConvertUTF8toWide(ClExe.c_str(), ClExeWide))
     return Version;
 
-  const DWORD VersionSize = ::GetFileVersionInfoSizeW(ClExeWide.c_str(),
-                                                      nullptr);
+  const DWORD VersionSize =
+      ::GetFileVersionInfoSizeW(ClExeWide.c_str(), nullptr);
   if (VersionSize == 0)
     return Version;
 
@@ -620,7 +635,7 @@ static VersionTuple getMSVCVersionFromExe(const std::string &BinDir) {
     return Version;
 
   const unsigned Major = (FileInfo->dwFileVersionMS >> 16) & 0xFFFF;
-  const unsigned Minor = (FileInfo->dwFileVersionMS      ) & 0xFFFF;
+  const unsigned Minor = (FileInfo->dwFileVersionMS) & 0xFFFF;
   const unsigned Micro = (FileInfo->dwFileVersionLS >> 16) & 0xFFFF;
 
   Version = VersionTuple(Major, Minor, Micro);
@@ -647,6 +662,17 @@ void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
                                   "include");
   }
 
+  auto SysRoot = getDriver().SysRoot;
+  if (!SysRoot.empty()) {
+    const Driver &D = getDriver();
+    const std::string MultiarchTriple =
+        getMultiarchTriple(D, getTriple(), SysRoot);
+    addSystemInclude(DriverArgs, CC1Args,
+                     SysRoot + "/include/" + MultiarchTriple);
+    addSystemInclude(DriverArgs, CC1Args, SysRoot + "/include");
+    return;
+  }
+
   // Add %INCLUDE%-like directories from the -imsvc flag.
   for (const auto &Path : DriverArgs.getAllArgValues(options::OPT__SLASH_imsvc))
     addSystemInclude(DriverArgs, CC1Args, Path);
@@ -763,12 +789,11 @@ void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
   // As a fallback, select default install paths.
   // FIXME: Don't guess drives and paths like this on Windows.
   const StringRef Paths[] = {
-    "C:/Program Files/Microsoft Visual Studio 10.0/VC/include",
-    "C:/Program Files/Microsoft Visual Studio 9.0/VC/include",
-    "C:/Program Files/Microsoft Visual Studio 9.0/VC/PlatformSDK/Include",
-    "C:/Program Files/Microsoft Visual Studio 8/VC/include",
-    "C:/Program Files/Microsoft Visual Studio 8/VC/PlatformSDK/Include"
-  };
+      "C:/Program Files/Microsoft Visual Studio 10.0/VC/include",
+      "C:/Program Files/Microsoft Visual Studio 9.0/VC/include",
+      "C:/Program Files/Microsoft Visual Studio 9.0/VC/PlatformSDK/Include",
+      "C:/Program Files/Microsoft Visual Studio 8/VC/include",
+      "C:/Program Files/Microsoft Visual Studio 8/VC/PlatformSDK/Include"};
   addSystemIncludes(DriverArgs, CC1Args, Paths);
 #endif
 }
@@ -776,6 +801,24 @@ void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
 void MSVCToolChain::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
                                                  ArgStringList &CC1Args) const {
   // FIXME: There should probably be logic here to find libc++ on Windows.
+  if (DriverArgs.hasArg(options::OPT_nostdinc, options::OPT_nostdlibinc,
+                        options::OPT_nostdincxx))
+    return;
+  if (getDriver().SysRoot.empty())
+    return;
+  switch (GetCXXStdlibType(DriverArgs)) {
+  case ToolChain::CST_Stl:
+    addStlIncludePaths(DriverArgs, CC1Args);
+    break;
+  case ToolChain::CST_Libstdcxx:
+    addLibStdCXXIncludePaths(DriverArgs, CC1Args);
+    break;
+  case ToolChain::CST_Libcxx:
+    addLibCxxIncludePaths(DriverArgs, CC1Args);
+    break;
+  default:
+    break;
+  }
 }
 
 VersionTuple MSVCToolChain::computeMSVCVersion(const Driver *D,
@@ -877,7 +920,8 @@ static void TranslateOptArg(Arg *A, llvm::opt::DerivedArgList &DAL,
           DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_inline));
           break;
         case '1':
-          DAL.AddFlagArg(A, Opts.getOption(options::OPT_finline_hint_functions));
+          DAL.AddFlagArg(A,
+                         Opts.getOption(options::OPT_finline_hint_functions));
           break;
         case '2':
         case '3':
@@ -912,11 +956,10 @@ static void TranslateOptArg(Arg *A, llvm::opt::DerivedArgList &DAL,
       }
       if (SupportsForcingFramePointer) {
         if (OmitFramePointer)
-          DAL.AddFlagArg(A,
-                         Opts.getOption(options::OPT_fomit_frame_pointer));
+          DAL.AddFlagArg(A, Opts.getOption(options::OPT_fomit_frame_pointer));
         else
-          DAL.AddFlagArg(
-              A, Opts.getOption(options::OPT_fno_omit_frame_pointer));
+          DAL.AddFlagArg(A,
+                         Opts.getOption(options::OPT_fno_omit_frame_pointer));
       } else {
         // Don't warn about /Oy- in x86-64 builds (where
         // SupportsForcingFramePointer is false).  The flag having no effect
@@ -1027,3 +1070,86 @@ void MSVCToolChain::addClangTargetOptions(
   if (Arg *A = DriverArgs.getLastArgNoClaim(options::OPT_marm64x))
     A->ignoreTargetSpecific();
 }
+
+void MSVCToolChain::addStlIncludePaths(
+    const llvm::opt::ArgList &DriverArgs,
+    llvm::opt::ArgStringList &CC1Args) const {
+  const Driver &D = getDriver();
+  std::string SysRoot = computeSysRoot();
+  std::string LibPath = SysRoot + "/include";
+  const std::string MultiarchTriple =
+      getMultiarchTriple(D, getTriple(), SysRoot);
+
+  std::string TargetDir = LibPath + "/" + MultiarchTriple + "/c++/stl";
+  addSystemInclude(DriverArgs, CC1Args, TargetDir);
+
+  // Second add the generic one.
+  addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/stl");
+}
+
+void MSVCToolChain::addLibCxxIncludePaths(
+    const llvm::opt::ArgList &DriverArgs,
+    llvm::opt::ArgStringList &CC1Args) const {
+  const Driver &D = getDriver();
+  std::string SysRoot = computeSysRoot();
+  std::string LibPath = SysRoot + "/include";
+  const std::string MultiarchTriple =
+      getMultiarchTriple(D, getTriple(), SysRoot);
+
+  std::string Version = detectLibcxxVersion(LibPath);
+  if (Version.empty())
+    return;
+
+  std::string TargetDir = LibPath + "/" + MultiarchTriple + "/c++/" + Version;
+  addSystemInclude(DriverArgs, CC1Args, TargetDir);
+
+  // Second add the generic one.
+  addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version);
+}
+
+void MSVCToolChain::addLibStdCXXIncludePaths(
+    const llvm::opt::ArgList &DriverArgs,
+    llvm::opt::ArgStringList &CC1Args) const {
+  // We cannot use GCCInstallationDetector here as the sysroot usually does
+  // not contain a full GCC installation.
+  // Instead, we search the given sysroot for /usr/include/xx, similar
+  // to how we do it for libc++.
+  const Driver &D = getDriver();
+  std::string SysRoot = computeSysRoot();
+  std::string LibPath = SysRoot + "/include";
+  const std::string MultiarchTriple =
+      getMultiarchTriple(D, getTriple(), SysRoot);
+
+  // This is similar to detectLibcxxVersion()
+  std::string Version;
+  {
+    std::error_code EC;
+    Generic_GCC::GCCVersion MaxVersion =
+        Generic_GCC::GCCVersion::Parse("0.0.0");
+    SmallString<128> Path(LibPath);
+    llvm::sys::path::append(Path, "c++");
+    for (llvm::vfs::directory_iterator LI = getVFS().dir_begin(Path, EC), LE;
+         !EC && LI != LE; LI = LI.increment(EC)) {
+      StringRef VersionText = llvm::sys::path::filename(LI->path());
+      if (VersionText[0] != 'v') {
+        auto Version = Generic_GCC::GCCVersion::Parse(VersionText);
+        if (Version > MaxVersion)
+          MaxVersion = Version;
+      }
+    }
+    if (MaxVersion.Major > 0)
+      Version = MaxVersion.Text;
+  }
+
+  if (Version.empty())
+    return;
+
+  std::string TargetDir = LibPath + "/c++/" + Version + "/" + MultiarchTriple;
+  addSystemInclude(DriverArgs, CC1Args, TargetDir);
+
+  // Second add the generic one.
+  addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version);
+  // Third the backward one.
+  addSystemInclude(DriverArgs, CC1Args,
+                   LibPath + "/c++/" + Version + "/backward");
+}
diff --git a/clang/lib/Driver/ToolChains/MSVC.h b/clang/lib/Driver/ToolChains/MSVC.h
index 3950a8ed38e8b..609ce1c738751 100644
--- a/clang/lib/Driver/ToolChains/MSVC.h
+++ b/clang/lib/Driver/ToolChains/MSVC.h
@@ -71,9 +71,7 @@ class LLVM_LIBRARY_VISIBILITY MSVCToolChain : public ToolChain {
     return llvm::DebuggerKind::Default;
   }
 
-  unsigned GetDefaultDwarfVersion() const override {
-    return 4;
-  }
+  unsigned GetDefaultDwarfVersion() const override { return 4; }
 
   std::string getSubDirectoryPath(llvm::SubDirectoryType Type,
                                   llvm::StringRef SubdirParent = "") const;
@@ -100,8 +98,8 @@ class LLVM_LIBRARY_VISIBILITY MSVCToolChain : public ToolChain {
   void AddHIPRuntimeLibArgs(const llvm::opt::ArgList &Args,
                             llvm::opt::ArgStringList &CmdArgs) const override;
 
-  bool getWindowsSDKLibraryPath(
-      const llvm::opt::ArgList &Args, 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;
@@ -132,7 +130,23 @@ class LLVM_LIBRARY_VISIBILITY MSVCToolChain : public ToolChain {
 
   Tool *buildLinker() const override;
   Tool *buildAssembler() const override;
+
 private:
+  CXXStdlibType GetDefaultCXXStdlibType() const override {
+    return ToolChain::CST_Stl;
+  }
+  RuntimeLibType GetDefaultRuntimeLibType() const override {
+    return ToolChain::RLT_Vcruntime;
+  }
+  UnwindLibType GetDefaultUnwindLibType() const override {
+    return ToolChain::UNW_Vcruntime;
+  }
+  void addStlIncludePaths(const llvm::opt::ArgList &DriverArgs,
+                          llvm::opt::ArgStringList &CC1Args) const;
+  void addLibCxxIncludePaths(const llvm::opt::ArgList &DriverArgs,
+                             llvm::opt::ArgStringList &CC1Args) const;
+  void addLibStdCXXIncludePaths(const llvm::opt::ArgList &DriverArgs,
+                                llvm::opt::ArgStringList &CC1Args) const;
   std::optional<llvm::StringRef> WinSdkDir, WinSdkVersion, WinSysRoot;
   std::string VCToolChainPath;
   llvm::ToolsetLayout VSLayout = llvm::ToolsetLayout::OlderVS;
diff --git a/clang/test/Driver/msvc-sysroot.cpp b/clang/test/Driver/msvc-sysroot.cpp
new file mode 100644
index 0000000000000..9e58729ec57e9
--- /dev/null
+++ b/clang/test/Driver/msvc-sysroot.cpp
@@ -0,0 +1,11 @@
+// RUN: %clangxx --target=x86_64-unknown-windows-msvc -### --sysroot=%S -fuse-ld=lld %s 2>&1 | FileCheck --check-prefix=COMPILE %s
+// COMPILE: clang{{.*}}" "-cc1"
+// COMPILE: "-isysroot" "[[SYSROOT:[^"]+]]"
+// COMPILE: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/x86_64-unknown-windows-msvc/c++/stl"
+// COMPILE: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/c++/stl"
+
+// RUN: %clangxx --target=aarch64-unknown-windows-msvc -### --sysroot=%S -fuse-ld=lld %s 2>&1 | FileCheck --check-prefix=COMPILE %s
+// COMPILE: clang{{.*}}" "-cc1"
+// COMPILE: "-isysroot" "[[SYSROOT:[^"]+]]"
+// COMPILE: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/aarch64-unknown-windows-msvc/c++/stl"
+// COMPILE: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/c++/stl"

@trcrsired trcrsired changed the title Support --sysroot= for ${arch}-windows-msvc targets [clang] Support --sysroot= for ${arch}-windows-msvc targets Jun 26, 2024
@Sirraide Sirraide requested review from MaskRay and AaronBallman June 26, 2024 04:55
@trcrsired
Copy link
Author

@MaskRay

Copy link
Collaborator

@zmodem zmodem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've read half the patch so far, but wanted to ask before continuing: would it be possible (and simpler) to extend the /winsysroot support somehow to also handle --sysroot?

(It would also be easier to review if the unrelated formatting changes were removed or split to a separate PR.)

clang/include/clang/Driver/ToolChain.h Outdated Show resolved Hide resolved
clang/lib/Driver/ToolChain.cpp Outdated Show resolved Hide resolved
clang/lib/Driver/ToolChains/MSVC.cpp Outdated Show resolved Hide resolved
clang/lib/Driver/ToolChains/MSVC.cpp Outdated Show resolved Hide resolved
@trcrsired
Copy link
Author

trcrsired commented Jul 1, 2024

I've read half the patch so far, but wanted to ask before continuing: would it be possible (and simpler) to extend the /winsysroot support somehow to also handle --sysroot?

(It would also be easier to review if the unrelated formatting changes were removed or split to a separate PR.)

no. i do not use clang-cl. /winsysroot is not sysroot. /winsysroot is for clang-cl. not clang itself

sysroot is what the usually the clang and GCC means. That is not a semantics for msvc.

I have checked the code first clang does not support /winsysroot, second they have very different meanings which make them incompatible. Just use --sysroot because --sysroot matches the behavior of other platforms, including GNU variants of windows targets like x86_64-windows-gnu.

BTW using --sysroot gives the same semantics for build systems like build tools cmake or xmake.

@trcrsired
Copy link
Author

I've read half the patch so far, but wanted to ask before continuing: would it be possible (and simpler) to extend the /winsysroot support somehow to also handle --sysroot?

(It would also be easier to review if the unrelated formatting changes were removed or split to a separate PR.)

I have avoided formatting as much as possible. However, the llvm CI does not pass if I do not format it.

@zmodem
Copy link
Collaborator

zmodem commented Jul 1, 2024

I've read half the patch so far, but wanted to ask before continuing: would it be possible (and simpler) to extend the /winsysroot support somehow to also handle --sysroot?
(It would also be easier to review if the unrelated formatting changes were removed or split to a separate PR.)

no. i do not use clang-cl. /winsysroot is not sysroot. /winsysroot is for clang-cl. not clang itself

sysroot is what the usually the clang and GCC means. That is not a semantics for msvc.

I have checked the code first clang does not support /winsysroot, second they have very different meanings which make them incompatible. Just use --sysroot because --sysroot matches the behavior of other platforms, including GNU variants of windows targets like x86_64-windows-gnu.

BTW using --sysroot gives the same semantics for build systems like build tools cmake or xmake.

I didn't mean that you should use /winsysroot, I was asking whether --sysroot and /winsysroot could share code for implementation.

@trcrsired
Copy link
Author

trcrsired commented Jul 1, 2024

I've read half the patch so far, but wanted to ask before continuing: would it be possible (and simpler) to extend the /winsysroot support somehow to also handle --sysroot?
(It would also be easier to review if the unrelated formatting changes were removed or split to a separate PR.)

no. i do not use clang-cl. /winsysroot is not sysroot. /winsysroot is for clang-cl. not clang itself
sysroot is what the usually the clang and GCC means. That is not a semantics for msvc.
I have checked the code first clang does not support /winsysroot, second they have very different meanings which make them incompatible. Just use --sysroot because --sysroot matches the behavior of other platforms, including GNU variants of windows targets like x86_64-windows-gnu.
BTW using --sysroot gives the same semantics for build systems like build tools cmake or xmake.

I didn't mean that you should use /winsysroot, I was asking whether --sysroot and /winsysroot could share code for implementation.

They couldn't. They mean different things.

  1. Settings --sysroot will disable all existing environment settings. The behavior is different.
  2. The file structures are completely different. winsysroot has a very complicated file structures that need to reference while --sysroot is not. /winsysroot reuses microsoft's file structures while --sysroot is GNU and LLVM's file structures.
  3. --sysroot supports -stdlib=libc++ while other settings do not.

const std::string MultiarchTriple =

Other toggles only work when --sysroot is null. This is a completely different setting than winsysroot and many other settings such as environmental variables. winsysroot still references environmental variable, which are completely different behavior. I suggest deprecating winsysroot completely.

@trcrsired
Copy link
Author

@zmodem Looks like clang-formatting makes you harder to review the code, I will try to use the upstream code and avoiding formatting.

@trcrsired trcrsired force-pushed the msvcsysroot branch 7 times, most recently from d716838 to e3f96da Compare July 1, 2024 21:25
@trcrsired
Copy link
Author

trcrsired commented Jul 1, 2024

@zmodem Can you review it again? I have removed the formatting part here. Although I disagree with -stdlib=msstl thing. Can you check any other part that is problematic first? Ty

@zmodem
Copy link
Collaborator

zmodem commented Jul 5, 2024

I extract all files from msvc installations and windows SDK and put them in the format of what a normal sysroot would do on other platforms. Basically, unify the behavior with another platform.

Isn't it better to point Clang at MSVC and the Windows SDK as they're normally installed?

This patch seems specific to your custom setup, not something that would be useful for others.

@trcrsired
Copy link
Author

trcrsired commented Jul 5, 2024

I extract all files from msvc installations and windows SDK and put them in the format of what a normal sysroot would do on other platforms. Basically, unify the behavior with another platform.

Isn't it better to point Clang at MSVC and the Windows SDK as they're normally installed?

This patch seems specific to your custom setup, not something that would be useful for others.

Windows SDK is not portable since it does not follow the Unix naming convention. I need to cross-compile Windows Binaries from Linux. Using Windows SDK is completely useless. Plus, the headers and libs are uppercase in Windows SDK, which is not the same header name as mingw-w64 and wine's headers. This creates huge headaches since the Linux filesystem is case-sensitive while Windows is not. Windows SDK is pointless.

No. I just create the same file structure as clang usually uses. Plus clang itself does not support libc++ because there is no place to put LLVM libc++

/include/c++/stl: MSVC STL headers
/include/c++/v1: LLVM libc++
/include/arch-unknown-windows-msvc: target specific headers
/include: headers
/lib/arch-unknown-windows-msvc: target specific libraries
/lib: generic libraries

Windows SDK file structure is much more complicated. Not just you need to put windows-sdk, you also have to put MSVC installation path which means I have to emulate entire program files (x32) which is impossible. I am not running wine either. Without the patch, I have to manually do -I -L and mostly importantly cmake does not support sysroot correctly.

This is the same rule as other triplets. I don't see how that is not useful because every other triplet, for example, x86_64-windows-gnu, x86_64-linux-gnu, wasm(32/64)-wasip1, aarch64-linux-android{version} are doing exactly the same.

For example wasm:

void WebAssembly::AddCXXStdlibLibArgs(const llvm::opt::ArgList &Args,

If you look at this rust cross-compiling thing. They are doing precisely the same; they need to repackage the libs since they are useless due to case insensitive on Windows. I am just putting the final results so that people could go and clone the sysroot repository to use it.
https://jake-shadle.github.io/xwin/#1-setup-toolchain-s

cqwrteur@otsiningo:~/Libraries/fast_io/examples/0007.legacy$ clang++ -o construct_fstream_from_syscall_msvc.exe construct_fstream_from_syscall.cc -Ofast -std=c++26 -flto=thin -I../../include --target=x86_64-windows-msvc --sysroot=/home/cqwrteur/toolchains/windows-msvc-sysroot -fuse-ld=lld
cqwrteur@otsiningo:~/Libraries/fast_io/examples/0007.legacy$ clang++ -o construct_fstream_from_syscall_gnu.exe construct_fstream_from_syscall.cc -Ofast -std=c++26 -flto=thin -I../../include --target=x86_64-windows-gnu --sysroot=/home/cqwrteur/toolchains/x86_64-pc-linux-gnu/x86_64-w64-mingw32 -s -fuse-ld=lld -lntdll
cqwrteur@otsiningo:~/Libraries/fast_io/examples/0007.legacy$ wine ./construct_fstream_from_syscall_msvc.exe 
Unix Timestamp:1720219272.6386773
Universe Timestamp:434602343149454472.6386773
UTC:2024-07-05T22:41:12.6386773Z
Local:2024-07-05T18:41:12.6386773-04:00 Timezone:Eastern Daylight Time
LLVM clang 19.0.0git ([email protected]:trcrsired/llvm-project.git 2a34fe2019959857e64027673
a62929cb357a951)
Universal CRT
Microsoft Visual C++ STL 202407
fstream.rdbuf():0x00007ffffe1ffcb8
FILE*:0x00007ffffe22c5c0
fd:3
win32 HANDLE:0x0000000000000038
zw HANDLE:0x0000000000000038
nt HANDLE:0x0000000000000038
cqwrteur@otsiningo:~/Libraries/fast_io/examples/0007.legacy$ wine ./construct_fstream_from_syscall_gnu.exe 
Unix Timestamp:1720219276.6978169
Universe Timestamp:434602343149454476.6978169
UTC:2024-07-05T22:41:16.6978169Z
Local:2024-07-05T18:41:16.6978169-04:00 Timezone:Eastern Daylight Time
LLVM clang 19.0.0git ([email protected]:trcrsired/llvm-project.git 2a34fe2019959857e64027673
a62929cb357a951)
Universal CRT
GNU C++ Library 15 20240629
fstream.rdbuf():0x00007ffffe1ffa80
FILE*:0x00006ffffeb59328
fd:3
win32 HANDLE:0x0000000000000038
zw HANDLE:0x0000000000000038
nt HANDLE:0x0000000000000038

@trcrsired trcrsired force-pushed the msvcsysroot branch 3 times, most recently from 1458330 to 4dc119b Compare July 11, 2024 23:11
@trcrsired
Copy link
Author

trcrsired commented Jul 11, 2024

@zmodem @MaskRay

@godvino
Copy link

godvino commented Aug 9, 2024

This could close #54868 that I had opened a while back to support cross compiling using clang with xwin.

@trcrsired
Copy link
Author

trcrsired commented Aug 11, 2024

This could close #54868 that I had opened a while back to support cross compiling using clang with xwin.

no. I do not want that complicated file structure. I just want normal GNU style's sysroot.

x86_64-windows-msvc sysroot should have exact the same file struct as x86_64-windows-gnu.

winsysroot is a terrible idea.

https://github.com/trcrsired/windows-msvc-sysroot/tree/main

@trcrsired
Copy link
Author

trcrsired commented Aug 11, 2024

x86_64-windows-gnu
image
x86_64-linux-gnu
image
aarch64-linux-gnu
image
See wasm. xxx-windows-gnu. aarch64-linux-android. x86_64-linux-gnu etc. They all have the same file structures. I do not see why should xxx-windows-msvc should be different from xxx-windows-gnu. /winsysroot and clang-cl thing just complicates everything.

https://github.com/trcrsired/llvm-releases/releases
https://github.com/trcrsired/gcc-releases/releases

Try yourself. All of them follow the same rule
C++ headers;
${SYSROOT}/include/c++/xxx
Headers:
${SYSROOT}/include
Libraries:
${SYSROOT}/lib/${TRIPLET}
${SYSROOT}/lib
BIN/DLL:
${SYSROOT}/bin/${TRIPLET}
${SYSROOT}/bin
or
${SYSROOT}/lib/${TRIPLET}
${SYSROOT}/lib
There is no reason why xxx-windows-msvc does not follow the same rule.

@zmodem
Copy link
Collaborator

zmodem commented Aug 12, 2024

no. I do not want that complicated file structure. I just want normal GNU style's sysroot.

x86_64-windows-msvc sysroot should have exact the same file struct as x86_64-windows-gnu.

The -msvc triples are designed to work with libraries from MSVC and the Windows SDK. We can't change how those are organized.

@trcrsired
Copy link
Author

trcrsired commented Aug 19, 2024

no. I do not want that complicated file structure. I just want normal GNU style's sysroot.
x86_64-windows-msvc sysroot should have exact the same file struct as x86_64-windows-gnu.

The -msvc triples are designed to work with libraries from MSVC and the Windows SDK. We can't change how those are organized.

There is no "--sysroot" for msvc and windows SDK. Also, there is no support for libc++ in that directory and that is a huge issue. My definition is correct to make clang --sysroot work the same way as other platforms.

If you don't like it, you can continue using winsysroot. winsysroot isn't --sysroot

Also, without a GNU style --sysroot=, users cannot even compile and use their libraries. Microsoft compiler was supposed to ask you to add dependencies to their IDE manually. That is not an option for cross compiling.

@trcrsired
Copy link
Author

@zmodem ok. let me explain to me why using the same file structure of msvc does not work.

MSVC's built-in headers conflict with Clang's built-in headers so clang cannot use its headers any more.
The headers and libraries are not case-sensitive, which causes issues on operating systems with case-sensitive filesystems.
libcxx is incompatible with this "Windows SDK," as it cannot locate the headers and libraries due to the commented-out paths in the MSVCToolchain.cpp.
The libraries and build systems assume the files are located in sysroot/lib and sysroot/include. Failing to adhere to this structure will disrupt other build systems.

Every of these issue need tweaking. You cannot just download windows sdk and use them on none windows platforms. They don't work.

@trcrsired
Copy link
Author

trcrsired commented Oct 3, 2024

Similarly it'd be excellent if clang supported equivalents to clang-cl's /winsdkdir and /vctoolsdir, though these probably couldn't be smooshed into existing options like /winsysroot can with --sysroot.

@zmodem @godvino

I strongly oppose making /winsysroot working the same way as --sysroot, and I would argue /winsysroot needs to be removed completely.

These setups are overly complex and lack support for libc++, making them practically useless. A GNU-style sysroot would solve everything, but Rust enthusiasts have complicated things by bringing in Cargo and micro-dependencies like xwin. I doubt xwin works well, especially since Microsoft's headers need fixes for case-sensitive filesystems—something that requires effort Microsoft isn't directly providing.

The last time I checked, even the Windows SDK has C headers mixed with standard C++ headers, making it impossible to use with LLVM libc++ and GNU libstdc++, and Microsoft's intrinsic headers don't work with Clang. Clang provides its own headers, like intrin.h, which aren't compatible with Microsoft's implementation. In fact, clang's own code in MSVC.cpp proves it does not work and it needs extra logic for dealing with libc++.
https://github.com/llvm/llvm-project/blob/ae635d6f997a28c81a01bfffe70fd849d0eafcca/clang/lib/Driver/ToolChains/MSVC.cpp#L773C1-L776C2

void MSVCToolChain::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
                                                 ArgStringList &CC1Args) const {
  // FIXME: There should probably be logic here to find libc++ on Windows.
}

I guess xwin is maintained by rust people who have very little knowledge on how things really work for C and C++, so you end up with even larger mess. I strongly oppose to introduce any rust engineering work outside their own bubble since you end up with mess like wasip2. https://kerkour.com/webassembly-wasi-preview2

This issue is exactly an example why just using microsoft's one does not work since basic all language compilers using the GNU's style sysroot rule and microsoft headers need manually fix up when things don't compile.
Jake-Shadle/xwin#138

https://github.com/trcrsired/windows-msvc-sysroot

@YexuanXiao
Copy link

This patch is useful. This is a simple way to get rid of the huge Visual Studio installation. I use the following command line to configure my project with cmake and use libc++ as the standard library on Windows:

cmake -G Ninja -D "CMAKE_C_COMPILER=D:\Projects\clang\llvm\bin\clang.exe" -D "CMAKE_CXX_COMPILER=D:\Projects\clang\llvm\bin\clang++.exe" -D "CMAKE_C_FLAGS=--target=x86_64-windows-msvc --sysroot=D:\Projects\windows-msvc-sysroot -fuse-ld=lld" -D "CMAKE_CXX_FLAGS=--target=x86_64-windows-msvc --sysroot=D:\Projects\windows-msvc-sysroot -fuse-ld=lld -stdlib=libc++" ..

By changing -stdlib=libc++ to -stdlib=stl, I can use stl as the C++ standard library.

Due to cmake incorrectly using msvc-style lld-link instead of lld when using gnu-sysroot, manual compilation is currently the only way to use gnu-sysroot:

clang++ --target=x86_64-windows-gnu --sysroot=D:\Projects\clang\x86_64-windows-gnu -rtlib=compiler-rt --unwindlib=libunwind -fuse-ld=lld -stdlib=libc++ -lc++abi -lunwind -std=c++23 examples.cpp

Overall, I believe this patch is effective, although windows-msvc-sysroot is a private repository, it doesn't have any conceptual differences from other sysroots. Perhaps the author should create a tutorial on how to build this system root.

I think it is possible to use the same rule for msvc targets with
--target= and --sysroot=

See Repository:
https://github.com/trcrsired/windows-msvc-sysroot

Add sysroot support for msvc

MSVC.cpp needs getDriver before using D

add stl in parser for -stdlib=

Add Vcruntime to runtime list and unwind list

MSVC add default runtime lib type and default unwind lib type

add a msvc sysroot test

use %S instead of /foo

Fix test for msvc-sysroot

Also add a pesudo implementation for WebAssembly and
maybe Microsoft STL could be ported to more targets in the future

Fix the toggle of wasm that prevents -stdlib=stl passed into

Avoid clang-formatting in MSVC.cpp

Add some comments to ToolChain

avoid indent the if block

Add back space before winsysroot line

use  instead of arch in the comment

remove FIXME for libc++ since we have added the logic here

Remove MSVC.h formatting

Remove default cases in WebAssembly

add back the empty line before the Sysroot line

fix msvc-sysroot typo for libc++ loongarch

Fix : missing in CST_Stl case
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants