Skip to content

Commit 3536350

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 [clang] Driver.cpp's formatting issue [clang] Driver itself needs to be passed into CxxModulePathEvaluate [clang] Fix Driver discards const qualifiers [clang] Fix remaining unhandled msstl and also add msstl to mingw We also always -lntdll since gcc with mcf alrady does that. Even on windows 9x the linker would still discard -lntdll and ntdll.dll does exist on windows 95 [clang] Darwin still did not handle msstl
1 parent 07eeb5f commit 3536350

File tree

22 files changed

+551
-120
lines changed

22 files changed

+551
-120
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: 60 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,79 @@ std::string Driver::GetProgramPath(StringRef Name, const ToolChain &TC) const {
66096611
return std::string(Name);
66106612
}
66116613

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

66166660
if (C.getArgs().hasArg(options::OPT_nostdlib))
66176661
return error;
66186662

6619-
switch (TC.GetCXXStdlibType(C.getArgs())) {
6663+
auto cxxstdlib = TC.GetCXXStdlibType(C.getArgs());
6664+
switch (cxxstdlib) {
66206665
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)
6666+
if (std::optional<std::string> result =
6667+
CxxModulePathEvaluate(*this, TC, cxxstdlib, "libc++.so");
6668+
result)
66476669
return *result;
66486670

6649-
return evaluate("libc++.a").value_or(error);
6671+
return CxxModulePathEvaluate(*this, TC, cxxstdlib, "libc++.a")
6672+
.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 =
6677+
CxxModulePathEvaluate(*this, TC, cxxstdlib, "libstdc++.so");
6678+
result)
66666679
return *result;
66676680

6668-
return evaluate("libstdc++.a").value_or(error);
6681+
return CxxModulePathEvaluate(*this, TC, cxxstdlib, "libstdc++.a")
6682+
.value_or(error);
6683+
}
6684+
6685+
default: {
6686+
return CxxModulePathEvaluate(*this, TC, cxxstdlib, nullptr).value_or(error);
66696687
}
66706688
}
66716689

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: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,8 @@ void AIX::AddClangCXXStdlibIncludeArgs(
457457
CC1Args.push_back("-D__LIBC_NO_CPP_MATH_OVERLOADS__");
458458
return;
459459
}
460+
default:
461+
break;
460462
}
461463

462464
llvm_unreachable("Unexpected C++ library type; only libc++ is supported.");
@@ -473,9 +475,11 @@ void AIX::AddCXXStdlibLibArgs(const llvm::opt::ArgList &Args,
473475
CmdArgs.push_back("-lc++experimental");
474476
CmdArgs.push_back("-lc++abi");
475477
return;
478+
default:
479+
break;
476480
}
477481

478-
llvm_unreachable("Unexpected C++ library type; only libc++ is supported.");
482+
llvm_unreachable("Unexpected C++ library type");
479483
}
480484

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

clang/lib/Driver/ToolChains/BareMetal.cpp

Lines changed: 9 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())
@@ -526,6 +530,8 @@ void BareMetal::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
526530
}
527531
break;
528532
}
533+
default:
534+
break;
529535
}
530536
}
531537
}
@@ -648,6 +654,8 @@ void baremetal::Linker::ConstructJob(Compilation &C, const JobAction &JA,
648654
TC.getCompilerRTArgString(Args, "crtend", ToolChain::FT_Object);
649655
break;
650656
}
657+
default:
658+
return;
651659
}
652660
CmdArgs.push_back(Args.MakeArgString(TC.GetFilePath(CRTBegin)));
653661
}

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

0 commit comments

Comments
 (0)