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" <[email protected]> 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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
