[clang] [Driver] Print C++ standard library info (PR #207798)
Yaxun Liu via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 20 07:48:30 PDT 2026
https://github.com/yxsamliu updated https://github.com/llvm/llvm-project/pull/207798
>From 414e15bbe999530b854894349077cea36e23b14d Mon Sep 17 00:00:00 2001
From: "Yaxun (Sam) Liu" <yaxun.liu at amd.com>
Date: Mon, 6 Jul 2026 11:48:02 -0400
Subject: [PATCH] [Driver] Print C++ standard library info
Users and tools sometimes need to know which C++ standard library Clang selected, and which include directories the driver added for it. Today they have to inspect verbose or `-###` output.
Add options to print this information directly:
`--print-cxx-stdlib`
`--print-cxx-stdlib-include-dirs`
For MSVC targets, the default printed library is `msvcstl`. Explicit `-stdlib=` values are still reported as requested. The include-dir query reuses toolchain logic, including the MSVC STL include path for the default MSVC case.
---
clang/include/clang/Driver/ToolChain.h | 4 ++
clang/include/clang/Options/Options.td | 8 ++++
clang/lib/Driver/Driver.cpp | 30 ++++++++++++++
clang/lib/Driver/ToolChain.cpp | 10 +++++
clang/lib/Driver/ToolChains/MSVC.cpp | 56 ++++++++++++++++++++------
clang/lib/Driver/ToolChains/MSVC.h | 8 ++++
clang/test/Driver/print-cxx-stdlib.cpp | 56 ++++++++++++++++++++++++++
7 files changed, 160 insertions(+), 12 deletions(-)
create mode 100644 clang/test/Driver/print-cxx-stdlib.cpp
diff --git a/clang/include/clang/Driver/ToolChain.h b/clang/include/clang/Driver/ToolChain.h
index 863ba1084cb1a..c5633e10933e8 100644
--- a/clang/include/clang/Driver/ToolChain.h
+++ b/clang/include/clang/Driver/ToolChain.h
@@ -783,6 +783,10 @@ class ToolChain {
// given compilation arguments.
virtual CXXStdlibType GetCXXStdlibType(const llvm::opt::ArgList &Args) const;
+ // GetCXXStdlibName - Determine the name of the C++ standard library to use
+ // with the given compilation arguments.
+ virtual StringRef GetCXXStdlibName(const llvm::opt::ArgList &Args) const;
+
// GetUnwindLibType - Determine the unwind library type to use with the
// given compilation arguments.
virtual UnwindLibType GetUnwindLibType(const llvm::opt::ArgList &Args) const;
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 4afb089e8a51f..95f0bbada11ea 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -6585,6 +6585,14 @@ def print_libgcc_file_name : Flag<["-", "--"], "print-libgcc-file-name">,
HelpText<"Print the library path for the currently used compiler runtime "
"library (\"libgcc.a\" or \"libclang_rt.builtins.*.a\")">,
Visibility<[ClangOption, CLOption]>;
+def print_cxx_stdlib : Flag<["-", "--"], "print-cxx-stdlib">,
+ HelpText<"Print the C++ standard library selected by the driver">,
+ Visibility<[ClangOption]>;
+def print_cxx_stdlib_include_dirs :
+ Flag<["-", "--"], "print-cxx-stdlib-include-dirs">,
+ HelpText<"Print the C++ standard library include directories selected by "
+ "the driver">,
+ Visibility<[ClangOption]>;
def print_multi_directory : Flag<["-", "--"], "print-multi-directory">;
def print_multi_lib : Flag<["-", "--"], "print-multi-lib">;
def print_multi_flags : Flag<["-", "--"], "print-multi-flags-experimental">,
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index e606cdc4c1cf8..2ef9ffe0b9426 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -136,6 +136,26 @@ template <typename F> static bool usesInput(const ArgList &Args, F &&Fn) {
});
}
+static bool isIncludeDirArg(StringRef Arg) {
+ return Arg == "-internal-isystem" || Arg == "-internal-externc-isystem" ||
+ Arg == "-isystem" || Arg == "-cxx-isystem" || Arg == "-idirafter";
+}
+
+static void printCXXStdlibIncludeDirs(const ToolChain &TC,
+ const ArgList &Args) {
+ ArgStringList CC1Args;
+ if (Args.hasArg(options::OPT_stdlibxx_isystem))
+ TC.AddClangCXXStdlibIsystemArgs(Args, CC1Args);
+ else
+ TC.AddClangCXXStdlibIncludeArgs(Args, CC1Args);
+
+ for (size_t I = 0; I < CC1Args.size(); ++I) {
+ StringRef Arg(CC1Args[I]);
+ if (isIncludeDirArg(Arg) && I + 1 < CC1Args.size())
+ llvm::outs() << CC1Args[++I] << '\n';
+ }
+}
+
CUIDOptions::CUIDOptions(llvm::opt::DerivedArgList &Args, const Driver &D)
: UseCUID(Kind::Hash) {
if (Arg *A = Args.getLastArg(options::OPT_fuse_cuid_EQ)) {
@@ -2674,6 +2694,16 @@ bool Driver::HandleImmediateArgs(Compilation &C) {
return false;
}
+ if (C.getArgs().hasArg(options::OPT_print_cxx_stdlib)) {
+ llvm::outs() << TC.GetCXXStdlibName(C.getArgs()) << '\n';
+ return false;
+ }
+
+ if (C.getArgs().hasArg(options::OPT_print_cxx_stdlib_include_dirs)) {
+ printCXXStdlibIncludeDirs(TC, C.getArgs());
+ return false;
+ }
+
if (C.getArgs().hasArg(options::OPT_print_std_module_manifest_path)) {
llvm::outs() << GetStdModuleManifestPath(C, C.getDefaultToolChain())
<< '\n';
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 0002d40bf1aa3..6a928890df7c4 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -1629,6 +1629,16 @@ ToolChain::CXXStdlibType ToolChain::GetCXXStdlibType(const ArgList &Args) const{
return *cxxStdlibType;
}
+StringRef ToolChain::GetCXXStdlibName(const ArgList &Args) const {
+ switch (GetCXXStdlibType(Args)) {
+ case ToolChain::CST_Libcxx:
+ return "libc++";
+ case ToolChain::CST_Libstdcxx:
+ return "libstdc++";
+ }
+ llvm_unreachable("unknown C++ standard library type");
+}
+
ToolChain::CStdlibType ToolChain::GetCStdlibType(const ArgList &Args) const {
if (cStdlibType)
return *cStdlibType;
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index 2aa69ec62cfba..07ad9215fc029 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -776,6 +776,37 @@ void MSVCToolChain::AddSystemIncludeWithSubfolder(
addSystemInclude(DriverArgs, CC1Args, path);
}
+void MSVCToolChain::AddMSVCStdlibMultilibIncludeArgs(
+ const ArgList &DriverArgs, ArgStringList &CC1Args,
+ bool HonorNostdincxx) const {
+ if (DriverArgs.hasArg(options::OPT_nostdinc, options::OPT_nostdlibinc) ||
+ (HonorNostdincxx && DriverArgs.hasArg(options::OPT_nostdincxx)))
+ return;
+
+ // Add multilib variant include paths in priority order.
+ for (const Multilib &M : getOrderedMultilibs()) {
+ if (M.isDefault())
+ continue;
+ if (std::optional<std::string> StdlibIncDir = getStdlibIncludePath()) {
+ SmallString<128> Dir(*StdlibIncDir);
+ llvm::sys::path::append(Dir, M.includeSuffix());
+ if (getDriver().getVFS().exists(Dir))
+ addSystemInclude(DriverArgs, CC1Args, Dir);
+ }
+ }
+}
+
+void MSVCToolChain::AddMSVCStdlibIncludeArgs(const ArgList &DriverArgs,
+ ArgStringList &CC1Args) const {
+ AddMSVCStdlibMultilibIncludeArgs(DriverArgs, CC1Args,
+ /*HonorNostdincxx=*/true);
+
+ if (!DriverArgs.hasArg(options::OPT_nostdinc, options::OPT_nostdlibinc) &&
+ !DriverArgs.hasArg(options::OPT_nostdincxx) && !VCToolChainPath.empty())
+ addSystemInclude(DriverArgs, CC1Args,
+ getSubDirectoryPath(llvm::SubDirectoryType::Include));
+}
+
void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const {
if (DriverArgs.hasArg(options::OPT_nostdinc))
@@ -824,17 +855,8 @@ void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
if (DriverArgs.hasArg(options::OPT_nostdlibinc))
return;
- // Add multilib variant include paths in priority order.
- for (const Multilib &M : getOrderedMultilibs()) {
- if (M.isDefault())
- continue;
- if (std::optional<std::string> StdlibIncDir = getStdlibIncludePath()) {
- SmallString<128> Dir(*StdlibIncDir);
- llvm::sys::path::append(Dir, M.includeSuffix());
- if (getDriver().getVFS().exists(Dir))
- addSystemInclude(DriverArgs, CC1Args, Dir);
- }
- }
+ AddMSVCStdlibMultilibIncludeArgs(DriverArgs, CC1Args,
+ /*HonorNostdincxx=*/false);
// Honor %INCLUDE% and %EXTERNAL_INCLUDE%. It should have essential search
// paths set by vcvarsall.bat. Skip if the user expressly set any of the
@@ -929,7 +951,17 @@ 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.
+ // MSVC STL paths are added from AddClangSystemIncludeArgs during normal
+ // compilation to preserve clang-cl header search order.
+ if (DriverArgs.hasArg(options::OPT_print_cxx_stdlib_include_dirs) &&
+ !DriverArgs.hasArg(options::OPT_stdlib_EQ))
+ AddMSVCStdlibIncludeArgs(DriverArgs, CC1Args);
+}
+
+StringRef MSVCToolChain::GetCXXStdlibName(const ArgList &DriverArgs) const {
+ if (!DriverArgs.hasArg(options::OPT_stdlib_EQ))
+ return "msvcstl";
+ return ToolChain::GetCXXStdlibName(DriverArgs);
}
VersionTuple MSVCToolChain::computeMSVCVersion(const Driver *D,
diff --git a/clang/lib/Driver/ToolChains/MSVC.h b/clang/lib/Driver/ToolChains/MSVC.h
index 6878d6a195d78..8ff8b2815d8df 100644
--- a/clang/lib/Driver/ToolChains/MSVC.h
+++ b/clang/lib/Driver/ToolChains/MSVC.h
@@ -101,6 +101,8 @@ class LLVM_LIBRARY_VISIBILITY MSVCToolChain : public ToolChain {
void
AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args) const override;
+ llvm::StringRef
+ GetCXXStdlibName(const llvm::opt::ArgList &DriverArgs) const override;
void AddClangCXXStdlibIncludeArgs(
const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args) const override;
@@ -143,6 +145,12 @@ class LLVM_LIBRARY_VISIBILITY MSVCToolChain : public ToolChain {
Action::OffloadKind DeviceOffloadKind) const override;
protected:
+ void AddMSVCStdlibMultilibIncludeArgs(const llvm::opt::ArgList &DriverArgs,
+ llvm::opt::ArgStringList &CC1Args,
+ bool HonorNostdincxx) const;
+ void AddMSVCStdlibIncludeArgs(const llvm::opt::ArgList &DriverArgs,
+ llvm::opt::ArgStringList &CC1Args) const;
+
void AddSystemIncludeWithSubfolder(const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args,
const std::string &folder,
diff --git a/clang/test/Driver/print-cxx-stdlib.cpp b/clang/test/Driver/print-cxx-stdlib.cpp
new file mode 100644
index 0000000000000..58620e7ee34dc
--- /dev/null
+++ b/clang/test/Driver/print-cxx-stdlib.cpp
@@ -0,0 +1,56 @@
+// RUN: %clangxx -print-cxx-stdlib -stdlib=libc++ \
+// RUN: --target=x86_64-unknown-linux-gnu | FileCheck %s --check-prefix=LIBCXX
+// RUN: %clangxx --print-cxx-stdlib -stdlib=libstdc++ \
+// RUN: --target=x86_64-unknown-linux-gnu | FileCheck %s --check-prefix=LIBSTDCXX
+// RUN: %clangxx --print-cxx-stdlib --target=x86_64-pc-windows-msvc \
+// RUN: | FileCheck %s --check-prefix=MSVC-STL
+// RUN: %clangxx --print-cxx-stdlib -stdlib=libc++ \
+// RUN: --target=x86_64-pc-windows-msvc | FileCheck %s --check-prefix=LIBCXX
+
+// RUN: mkdir -p %t/bin
+// RUN: mkdir -p %t/include/c++/v1
+// RUN: %clangxx -print-cxx-stdlib-include-dirs -stdlib=libc++ \
+// RUN: --target=x86_64-unknown-linux-gnu -ccc-install-dir %t/bin \
+// RUN: | FileCheck %s --check-prefix=LIBCXX-INCLUDES
+
+// RUN: %clangxx --print-cxx-stdlib-include-dirs -stdlib++-isystem /tmp/foo \
+// RUN: -stdlib++-isystem /tmp/bar --target=x86_64-unknown-linux-gnu \
+// RUN: | FileCheck %s --check-prefix=STDLIBXX-ISYSTEM
+// RUN: %clangxx --print-cxx-stdlib-include-dirs -stdlib++-isystem /tmp/foo \
+// RUN: -stdlib++-isystem /tmp/bar -nostdinc++ \
+// RUN: --target=x86_64-unknown-linux-gnu \
+// RUN: | FileCheck %s --check-prefix=NO-INCLUDES --allow-empty
+
+// RUN: %clangxx --print-cxx-stdlib-include-dirs -stdlib=libstdc++ \
+// RUN: --gcc-toolchain=%S/Inputs/gcc_version_parsing_rt_libs \
+// RUN: --target=x86_64-redhat-linux \
+// RUN: | FileCheck %s --check-prefix=LIBSTDCXX-INCLUDES
+// RUN: %clangxx --print-cxx-stdlib-include-dirs \
+// RUN: --target=x86_64-pc-windows-msvc \
+// RUN: -Xmicrosoft-visualc-tools-root %t/VC/Tools/MSVC/27.1828.18284 \
+// RUN: | FileCheck %s --check-prefix=MSVC-INCLUDES
+// RUN: %clangxx --print-cxx-stdlib-include-dirs \
+// RUN: --target=x86_64-pc-windows-msvc \
+// RUN: -Xmicrosoft-visualc-tools-root %t/VC/Tools/MSVC/27.1828.18284 \
+// RUN: -nostdinc++ \
+// RUN: | FileCheck %s --check-prefix=NO-INCLUDES --allow-empty
+// RUN: %clangxx --print-cxx-stdlib-include-dirs -stdlib=libc++ \
+// RUN: --target=x86_64-pc-windows-msvc \
+// RUN: -Xmicrosoft-visualc-tools-root %t/VC/Tools/MSVC/27.1828.18284 \
+// RUN: | FileCheck %s --check-prefix=NO-INCLUDES --allow-empty
+
+// LIBCXX: libc++
+// LIBSTDCXX: libstdc++
+// MSVC-STL: msvcstl
+
+// LIBCXX-INCLUDES: {{.*}}{{/|\\}}include{{/|\\}}c++{{/|\\}}v1
+
+// STDLIBXX-ISYSTEM: /tmp/foo
+// STDLIBXX-ISYSTEM-NEXT: /tmp/bar
+
+// NO-INCLUDES-NOT: {{.}}
+
+// LIBSTDCXX-INCLUDES: {{.*}}gcc_version_parsing_rt_libs{{/|\\}}lib{{/|\\}}gcc{{/|\\}}x86_64-redhat-linux{{/|\\}}10.2.0{{/|\\}}..{{/|\\}}..{{/|\\}}..{{/|\\}}gcc{{/|\\}}x86_64-redhat-linux{{/|\\}}10.2.0{{/|\\}}include{{/|\\}}c++
+
+// MSVC-INCLUDES: {{.*}}VC{{/|\\}}Tools{{/|\\}}MSVC{{/|\\}}27.1828.18284{{/|\\}}include
+// MSVC-INCLUDES-NOT: atlmfc
More information about the cfe-commits
mailing list