Skip to content

Commit 324120b

Browse files
committed
[clang] Support --sysroot= for ${arch}-windows-msvc targets
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 Support --sysroot= for ${arch}-windows-msvc targets 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 [clang] update msvc-sysroot.cpp tep to test arm64ec and arm64x [clang] Add arm64ec -windows-msvc support for msvc sysroot by finding libs in the aarch64-unknown-windows-msvc subdir MSVC target needs to include Gnu.h to support libstdc++ [clang] fix formatting issues for MSVC sysroot PR [clang] Support --sysroot= for ${arch}-windows-msvc targets 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 Support --sysroot= for ${arch}-windows-msvc targets 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 [clang] update msvc-sysroot.cpp tep to test arm64ec and arm64x [clang] Add arm64ec -windows-msvc support for msvc sysroot by finding libs in the aarch64-unknown-windows-msvc subdir MSVC target needs to include Gnu.h to support libstdc++ [clang] fix formatting issues for MSVC sysroot PR
1 parent 720007e commit 324120b

File tree

18 files changed

+494
-103
lines changed

18 files changed

+494
-103
lines changed

clang/docs/UsersManual.rst

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5457,3 +5457,76 @@ Restrictions and Limitations compared to Clang
54575457
Strict aliasing (TBAA) is always off by default in clang-cl whereas in clang,
54585458
strict aliasing is turned on by default for all optimization levels. For more
54595459
details, see :ref:`Strict aliasing <strict_aliasing>`.
5460+
5461+
Using clang/clang++ with MSVC Targets
5462+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5463+
5464+
While ``clang-cl`` emulates the MSVC compiler interface, users may prefer to invoke ``clang`` or ``clang++`` directly for greater control and platform consistency. When targeting MSVC environments, Clang supports the use of ``--target=`` and ``--sysroot=`` following the same conventions as Unix-style cross-compilation.
5465+
5466+
This approach avoids reliance on a Windows environment, Wine, or environmental variables, instead using a predictable and portable sysroot layout:
5467+
5468+
Headers
5469+
-------
5470+
5471+
- Windows + CRT headers: ``include/``
5472+
5473+
- C++ standard library headers (selected via ``-stdlib=``):
5474+
5475+
- ``-stdlib=msstl`` → ``include/c++/msstl``
5476+
Microsoft STL (MSVC's standard library implementation)
5477+
5478+
- ``-stdlib=libc++`` → ``include/c++/v1``
5479+
LLVM libc++ (Clang's standard library implementation)
5480+
5481+
- ``-stdlib=libstdc++`` → ``include/c++/<version>`` (e.g. ``16.0.0``)
5482+
GNU libstdc++ (GCC's standard library implementation)
5483+
5484+
Library Naming Conventions
5485+
--------------------------
5486+
5487+
When targeting ``${cpu}-unknown-windows-msvc``, the naming of runtime libraries differs from GNU-style targets:
5488+
5489+
- **LLVM libc++**:
5490+
- MSVC target: ``c++.dll``, ``c++.lib``
5491+
- GNU target: ``libc++.dll``, ``libc++.a``
5492+
5493+
- **GNU libstdc++**:
5494+
- MSVC target: ``stdc++-6.dll``, ``stdc++.lib``
5495+
- GNU target: ``libstdc++-6.dll``, ``libstdc++.a``
5496+
5497+
These naming conventions reflect platform-specific linker and runtime expectations. The MSVC target omits the ``lib`` prefix and uses `.lib` import libraries, while GNU targets retain the traditional Unix-style naming.
5498+
5499+
Libraries
5500+
---------
5501+
5502+
The sysroot must contain libraries in the following fallback order:
5503+
5504+
1. ``lib/${cpu}-unknown-windows-msvc``
5505+
2. ``lib/``
5506+
5507+
Example for ``x86_64-unknown-windows-msvc``:
5508+
``lib/x86_64-unknown-windows-msvc`` → ``lib/``
5509+
5510+
This structure allows toolchains to support both target-specific and shared libraries, enabling clean fallback behavior and simplifying multi-target packaging.
5511+
5512+
Binaries
5513+
--------
5514+
5515+
The sysroot must also contain binaries in the following fallback order:
5516+
5517+
1. ``bin/${cpu}-unknown-windows-msvc``
5518+
2. ``bin/``
5519+
5520+
Example for ``x86_64-unknown-windows-msvc``:
5521+
``bin/x86_64-unknown-windows-msvc`` → ``bin/``
5522+
5523+
This layout supports future scenarios such as universal binaries and ensures consistent tool resolution across architectures.
5524+
5525+
Case Sensitivity
5526+
----------------
5527+
5528+
All header and library paths must use lowercase file names. This ensures compatibility across case-sensitive filesystems such as Linux and macOS, and matches the behavior of ``mingw-w64-crt``. Windows itself is case-insensitive, but relying on mixed-case paths can lead to portability issues.
5529+
5530+
This layout is fully compatible with Clang’s standard sysroot resolution logic and requires no MSVC-specific flags. It enables clean cross-compilation workflows and portable toolchain packaging.
5531+
5532+
For a reference implementation, see `windows-msvc-sysroot <https://github.com/trcrsired/windows-msvc-sysroot>`_.

clang/include/clang/Driver/ToolChain.h

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -94,20 +94,14 @@ class ToolChain {
9494
using path_list = SmallVector<std::string, 16>;
9595

9696
enum CXXStdlibType {
97-
CST_Libcxx,
98-
CST_Libstdcxx
97+
CST_Libcxx, // LLVM libc++
98+
CST_Libstdcxx, // GNU libstdc++
99+
CST_Msstl, // MSVC STL
99100
};
100101

101-
enum RuntimeLibType {
102-
RLT_CompilerRT,
103-
RLT_Libgcc
104-
};
102+
enum RuntimeLibType { RLT_CompilerRT, RLT_Libgcc, RLT_Vcruntime };
105103

106-
enum UnwindLibType {
107-
UNW_None,
108-
UNW_CompilerRT,
109-
UNW_Libgcc
110-
};
104+
enum UnwindLibType { UNW_None, UNW_CompilerRT, UNW_Libgcc, UNW_Vcruntime };
111105

112106
enum class UnwindTableLevel {
113107
None,

clang/lib/Driver/Driver.cpp

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2588,6 +2588,8 @@ bool Driver::HandleImmediateArgs(Compilation &C) {
25882588
case ToolChain::RLT_Libgcc:
25892589
llvm::outs() << GetFilePath("libgcc.a", TC) << "\n";
25902590
break;
2591+
default:
2592+
break;
25912593
}
25922594
return false;
25932595
}
@@ -6609,63 +6611,76 @@ std::string Driver::GetProgramPath(StringRef Name, const ToolChain &TC) const {
66096611
return std::string(Name);
66106612
}
66116613

6614+
static std::optional<std::string> CxxModulePathEvaluate(const ToolChain &TC,
6615+
CXXStdlibType cxxstdlib,
6616+
const char *library) {
6617+
const char *modulejsonfilename = "modules.json";
6618+
switch (cxxstdlib) {
6619+
case ToolChain::CST_Libcxx: {
6620+
modulejsonfilename = "libc++.modules.json";
6621+
break;
6622+
}
6623+
case ToolChain::CST_Libstdcxx: {
6624+
modulejsonfilename = "libstdc++.modules.json";
6625+
break;
6626+
}
6627+
default: {
6628+
break;
6629+
}
6630+
}
6631+
6632+
if (library == nullptr) {
6633+
library = modulejsonfilename;
6634+
}
6635+
std::string lib = GetFilePath(library, TC);
6636+
6637+
// Note when there are multiple flavours of libc++ the module json needs
6638+
// to look at the command-line arguments for the proper json. These
6639+
// flavours do not exist at the moment, but there are plans to provide a
6640+
// variant that is built with sanitizer instrumentation enabled.
6641+
6642+
// For example
6643+
// StringRef modules = [&] {
6644+
// const SanitizerArgs &Sanitize = TC.getSanitizerArgs(C.getArgs());
6645+
// if (Sanitize.needsAsanRt())
6646+
// return "libc++.modules-asan.json";
6647+
// return "libc++.modules.json";
6648+
// }();
6649+
6650+
SmallString<128> path(lib.begin(), lib.end());
6651+
llvm::sys::path::remove_filename(path);
6652+
llvm::sys::path::append(path, modulejsonfilename);
6653+
if (TC.getVFS().exists(path))
6654+
return static_cast<std::string>(path);
6655+
6656+
return {};
6657+
}
6658+
66126659
std::string Driver::GetStdModuleManifestPath(const Compilation &C,
66136660
const ToolChain &TC) const {
66146661
std::string error = "<NOT PRESENT>";
66156662

66166663
if (C.getArgs().hasArg(options::OPT_nostdlib))
66176664
return error;
66186665

6619-
switch (TC.GetCXXStdlibType(C.getArgs())) {
6666+
auto cxxstdlib = TC.GetCXXStdlibType(C.getArgs());
6667+
switch (cxxstdlib) {
66206668
case ToolChain::CST_Libcxx: {
6621-
auto evaluate = [&](const char *library) -> std::optional<std::string> {
6622-
std::string lib = GetFilePath(library, TC);
6623-
6624-
// Note when there are multiple flavours of libc++ the module json needs
6625-
// to look at the command-line arguments for the proper json. These
6626-
// flavours do not exist at the moment, but there are plans to provide a
6627-
// variant that is built with sanitizer instrumentation enabled.
6628-
6629-
// For example
6630-
// StringRef modules = [&] {
6631-
// const SanitizerArgs &Sanitize = TC.getSanitizerArgs(C.getArgs());
6632-
// if (Sanitize.needsAsanRt())
6633-
// return "libc++.modules-asan.json";
6634-
// return "libc++.modules.json";
6635-
// }();
6636-
6637-
SmallString<128> path(lib.begin(), lib.end());
6638-
llvm::sys::path::remove_filename(path);
6639-
llvm::sys::path::append(path, "libc++.modules.json");
6640-
if (TC.getVFS().exists(path))
6641-
return static_cast<std::string>(path);
6642-
6643-
return {};
6644-
};
6645-
6646-
if (std::optional<std::string> result = evaluate("libc++.so"); result)
6669+
if (std::optional<std::string> result = CxxModulePathEvaluate(TC, cxxstdlib, "libc++.so"); result)
66476670
return *result;
66486671

6649-
return evaluate("libc++.a").value_or(error);
6672+
return CxxModulePathEvaluate(TC, cxxstdlib, "libc++.a").value_or(error);
66506673
}
66516674

66526675
case ToolChain::CST_Libstdcxx: {
6653-
auto evaluate = [&](const char *library) -> std::optional<std::string> {
6654-
std::string lib = GetFilePath(library, TC);
6655-
6656-
SmallString<128> path(lib.begin(), lib.end());
6657-
llvm::sys::path::remove_filename(path);
6658-
llvm::sys::path::append(path, "libstdc++.modules.json");
6659-
if (TC.getVFS().exists(path))
6660-
return static_cast<std::string>(path);
6661-
6662-
return {};
6663-
};
6664-
6665-
if (std::optional<std::string> result = evaluate("libstdc++.so"); result)
6676+
if (std::optional<std::string> result = CxxModulePathEvaluate(TC, cxxstdlib, "libstdc++.so"); result)
66666677
return *result;
66676678

6668-
return evaluate("libstdc++.a").value_or(error);
6679+
return CxxModulePathEvaluate(TC, cxxstdlib, "libstdc++.a").value_or(error);
6680+
}
6681+
6682+
default: {
6683+
return CxxModulePathEvaluate(TC, cxxstdlib, nullptr).value_or(error);
66696684
}
66706685
}
66716686

clang/lib/Driver/ToolChain.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,6 +1330,8 @@ ToolChain::RuntimeLibType ToolChain::GetRuntimeLibType(
13301330
runtimeLibType = ToolChain::RLT_CompilerRT;
13311331
else if (LibName == "libgcc")
13321332
runtimeLibType = ToolChain::RLT_Libgcc;
1333+
else if (LibName == "vcruntime")
1334+
runtimeLibType = ToolChain::RLT_Vcruntime;
13331335
else if (LibName == "platform")
13341336
runtimeLibType = GetDefaultRuntimeLibType();
13351337
else {
@@ -1368,6 +1370,8 @@ ToolChain::UnwindLibType ToolChain::GetUnwindLibType(
13681370
unwindLibType = ToolChain::UNW_CompilerRT;
13691371
} else if (LibName == "libgcc")
13701372
unwindLibType = ToolChain::UNW_Libgcc;
1373+
else if (LibName == "vcruntime")
1374+
unwindLibType = ToolChain::UNW_Vcruntime;
13711375
else {
13721376
if (A)
13731377
getDriver().Diag(diag::err_drv_invalid_unwindlib_name)
@@ -1391,6 +1395,8 @@ ToolChain::CXXStdlibType ToolChain::GetCXXStdlibType(const ArgList &Args) const{
13911395
cxxStdlibType = ToolChain::CST_Libcxx;
13921396
else if (LibName == "libstdc++")
13931397
cxxStdlibType = ToolChain::CST_Libstdcxx;
1398+
else if (LibName == "msstl")
1399+
cxxStdlibType = ToolChain::CST_Msstl;
13941400
else if (LibName == "platform")
13951401
cxxStdlibType = GetDefaultCXXStdlibType();
13961402
else {
@@ -1546,6 +1552,10 @@ void ToolChain::AddCXXStdlibLibArgs(const ArgList &Args,
15461552
case ToolChain::CST_Libstdcxx:
15471553
CmdArgs.push_back("-lstdc++");
15481554
break;
1555+
1556+
case ToolChain::CST_Msstl:
1557+
// MSVC STL does not need to add -l
1558+
break;
15491559
}
15501560
}
15511561

clang/lib/Driver/ToolChains/AIX.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,9 +473,11 @@ void AIX::AddCXXStdlibLibArgs(const llvm::opt::ArgList &Args,
473473
CmdArgs.push_back("-lc++experimental");
474474
CmdArgs.push_back("-lc++abi");
475475
return;
476+
default:
477+
break;
476478
}
477479

478-
llvm_unreachable("Unexpected C++ library type; only libc++ is supported.");
480+
llvm_unreachable("Unexpected C++ library type");
479481
}
480482

481483
// This function processes all the mtocdata options to build the final

clang/lib/Driver/ToolChains/BareMetal.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,10 +477,14 @@ void BareMetal::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
477477
AddCXXIncludePath(P);
478478
break;
479479
}
480-
case ToolChain::CST_Libstdcxx:
480+
case ToolChain::CST_Libstdcxx: {
481481
addLibStdCxxIncludePaths(DriverArgs, CC1Args);
482482
break;
483483
}
484+
default: {
485+
break;
486+
}
487+
}
484488

485489
std::string SysRootDir(computeSysRoot());
486490
if (SysRootDir.empty())

clang/lib/Driver/ToolChains/CommonArgs.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2398,6 +2398,8 @@ static void AddUnwindLibrary(const ToolChain &TC, const Driver &D,
23982398
CmdArgs.push_back("-lunwind");
23992399
}
24002400
break;
2401+
default:
2402+
break;
24012403
}
24022404

24032405
if (AsNeeded)
@@ -2446,6 +2448,8 @@ void tools::AddRunTimeLibs(const ToolChain &TC, const Driver &D,
24462448
} else
24472449
AddLibgcc(TC, D, CmdArgs, Args);
24482450
break;
2451+
default:
2452+
break;
24492453
}
24502454

24512455
// On Android, the unwinder uses dl_iterate_phdr (or one of

clang/lib/Driver/ToolChains/Darwin.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2885,6 +2885,9 @@ void AppleMachO::AddCXXStdlibLibArgs(const ArgList &Args,
28852885
// Otherwise, let the linker search.
28862886
CmdArgs.push_back("-lstdc++");
28872887
break;
2888+
2889+
default:
2890+
break;
28882891
}
28892892
}
28902893

clang/lib/Driver/ToolChains/Fuchsia.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ void Fuchsia::AddCXXStdlibLibArgs(const ArgList &Args,
467467
CmdArgs.push_back("-lc++experimental");
468468
break;
469469

470-
case ToolChain::CST_Libstdcxx:
470+
default:
471471
llvm_unreachable("invalid stdlib name");
472472
}
473473
}

clang/lib/Driver/ToolChains/Hexagon.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,9 @@ void HexagonToolChain::AddCXXStdlibLibArgs(const ArgList &Args,
644644
case ToolChain::CST_Libstdcxx:
645645
CmdArgs.push_back("-lstdc++");
646646
break;
647+
648+
default:
649+
break;
647650
}
648651
}
649652

0 commit comments

Comments
 (0)