llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Anutosh Bhat (anutosh491)

<details>
<summary>Changes</summary>

## Motivation
Clang’s -v output is informational, but several paths currently write it 
directly to `llvm::errs()`. That is a reasonable default for standalone 
command-line tools, but applications embedding Clang may need to route verbose 
output somewhere else .. for example, to an application-owned stream, log 
buffer, notebook output channel, or browser console.

The immediate motivation is xeus-cpp, the C++ kernel for JupyterLite(try 
[here](https://notebook.link/@<!-- -->quantstack/xeus-cpp)). Jupyterlite is a 
Jupyter lab distribution that runs entirely in the browser. Its flow is:
```
xeus-cpp → CppInterOp → clang::Interpreter → Clang Driver/Frontend
```

The webassembly build for llvm libs and `ClangInterpreter` used by this kernel 
is maintained in 
[emscripten-forge](https://github.com/emscripten-forge/recipes). Jupyter 
frontends render stderr as red error output, so Clang’s initialization 
information—such as the version, generated -cc1 command, and header search 
paths—looks like a compiler failure. Something like this 

&lt;img width="1363" height="507" alt="image" 
src="https://github.com/user-attachments/assets/146250c2-dee5-4c59-b7ba-5fc811e33611";
 /&gt;

To fix this the build carries a downstream 
[patch](https://github.com/emscripten-forge/recipes/blob/main/recipes/recipes_emscripten/llvm/patches/fix_initialization_logs.patch)
 that changes those writes from `llvm::errs()` to `llvm::outs()`.
Hardcoding stdout downstream solves the presentation problem, but it replaces 
one fixed policy with another and requires us to maintain the patch across LLVM 
releases.

The same issue also arises in 
[WasmBolt](https://github.com/anutosh491/WasmBolt) that I've been developing 
recently (read 
[RFC](https://discourse.llvm.org/t/rfc-embeddable-llvm-tool-drivers-for-long-lived-hosts/91754)
 &amp; try [here](https://anutosh21.github.io/WasmBolt/)). WasmBolt embeds 
Clang through `clang::tooling::ToolInvocation` and a custom `ToolAction`, 
running the compiler inside a long-lived browser process. Such applications 
need to direct informational output to UI-owned streams without changing where 
actual compiler diagnostics are reported.

## Existing precedent

`CompilerInstance` already provides:

```cpp
setVerboseOutputStream(llvm::raw_ostream &amp;)
```

However, not all frontend verbosity respects it. Header-search output, for 
example, still writes directly to `llvm::errs()`. The Driver and higher-level 
Interpreter and Tooling entry points also do not currently expose an equivalent 
configuration path.

This patch extends the existing `CompilerInstance` model instead of introducing 
a separate logging abstraction.

## Changes

This patch:

- adds a configurable verbose output stream to `clang::driver::Driver`.
- makes header-search verbosity respect `CompilerInstance`’s existing verbose 
stream.
- lets `IncrementalCompilerBuilder` propagate a caller-selected stream through 
both the Driver and frontend.
- lets `ToolInvocation` and `ClangTool` propagate the stream through the Driver 
and `FrontendActionFactory`.
- makes the selected stream available to custom `ToolAction` implementations.

For example, a notebook kernel can select stdout:

```cpp
clang::IncrementalCompilerBuilder Builder;
Builder.SetVerboseOutputStream(llvm::outs());
```

A Tooling-based embedder can similarly use:

```cpp
clang::tooling::ToolInvocation Invocation(...);
Invocation.setVerboseOutputStream(llvm::outs());
```

The stream is a general `llvm::raw_ostream`, so callers may also use a file, 
string buffer, application-specific stream, or `llvm::nulls()`.

## Compatibility

- The default remains `llvm::errs()` everywhere. Therefore no standalone 
behaviour for clang/clang-repl etc changes
- Compiler diagnostics are not redirected by this API. They continue to use 
Clang’s diagnostics engine and its configured diagnostic consumer. This change 
only affects informational verbose output.


This framing makes the important distinction clear: stderr remains a compatible 
CLI default, while embedded clients gain control over presentation and routing.



---

Patch is 26.41 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/226022.diff


14 Files Affected:

- (modified) clang/include/clang/Driver/Driver.h (+13) 
- (modified) clang/include/clang/Interpreter/Interpreter.h (+8) 
- (modified) clang/include/clang/Lex/HeaderSearch.h (+3-1) 
- (modified) clang/include/clang/Tooling/Tooling.h (+27) 
- (modified) clang/lib/Driver/Compilation.cpp (+1-1) 
- (modified) clang/lib/Driver/Driver.cpp (+10-10) 
- (modified) clang/lib/Driver/ToolChains/Darwin.cpp (+4-4) 
- (modified) clang/lib/Driver/ToolChains/ZOS.cpp (+1-1) 
- (modified) clang/lib/Frontend/CompilerInstance.cpp (+2-1) 
- (modified) clang/lib/Interpreter/Interpreter.cpp (+8-2) 
- (modified) clang/lib/Lex/InitHeaderSearch.cpp (+25-18) 
- (modified) clang/lib/Tooling/Tooling.cpp (+13-3) 
- (modified) clang/unittests/Interpreter/IncrementalCompilerBuilderTest.cpp 
(+23) 
- (modified) clang/unittests/Tooling/ToolingTest.cpp (+66) 


``````````diff
diff --git a/clang/include/clang/Driver/Driver.h 
b/clang/include/clang/Driver/Driver.h
index e653d8e3a2dbe..0d492b49b8ed3 100644
--- a/clang/include/clang/Driver/Driver.h
+++ b/clang/include/clang/Driver/Driver.h
@@ -95,6 +95,10 @@ class Driver {
 
   IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS;
 
+  /// Stream used for informational output produced by verbose driver modes.
+  /// Diagnostics continue to use the diagnostics engine.
+  raw_ostream *VerboseOutputStream;
+
   enum DriverMode {
     GCCMode,
     GXXMode,
@@ -413,6 +417,15 @@ class Driver {
 
   void setCheckInputsExist(bool Value) { CheckInputsExist = Value; }
 
+  /// Replace the stream used for informational verbose output. The caller
+  /// retains ownership and must keep the stream alive for this Driver.
+  void setVerboseOutputStream(raw_ostream &Value) {
+    VerboseOutputStream = &Value;
+  }
+
+  /// Return the stream used for informational verbose output.
+  raw_ostream &getVerboseOutputStream() const { return *VerboseOutputStream; }
+
   bool getProbePrecompiled() const { return ProbePrecompiled; }
   void setProbePrecompiled(bool Value) { ProbePrecompiled = Value; }
 
diff --git a/clang/include/clang/Interpreter/Interpreter.h 
b/clang/include/clang/Interpreter/Interpreter.h
index c2622b23d5d9c..3235b9de44da5 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -59,6 +59,13 @@ class IncrementalCompilerBuilder {
 
   void SetTargetTriple(std::string TT) { TargetTriple = TT; }
 
+  /// Set the stream used for informational verbose output. The caller must
+  /// ensure that the stream outlives compiler instances created by this
+  /// builder. Diagnostics are not redirected.
+  void SetVerboseOutputStream(llvm::raw_ostream &OS) {
+    VerboseOutputStream = &OS;
+  }
+
   // General C++
   llvm::Expected<std::unique_ptr<CompilerInstance>> CreateCpp();
 
@@ -84,6 +91,7 @@ class IncrementalCompilerBuilder {
 
   std::vector<const char *> UserArgs;
   std::optional<std::string> TargetTriple;
+  llvm::raw_ostream *VerboseOutputStream = nullptr;
 
   llvm::StringRef OffloadArch;
   llvm::StringRef CudaSDKPath;
diff --git a/clang/include/clang/Lex/HeaderSearch.h 
b/clang/include/clang/Lex/HeaderSearch.h
index adaa126024c3c..09dfa39088682 100644
--- a/clang/include/clang/Lex/HeaderSearch.h
+++ b/clang/include/clang/Lex/HeaderSearch.h
@@ -37,6 +37,7 @@
 
 namespace llvm {
 
+class raw_ostream;
 class Triple;
 
 } // namespace llvm
@@ -1061,7 +1062,8 @@ class HeaderSearch {
 void ApplyHeaderSearchOptions(HeaderSearch &HS,
                               const HeaderSearchOptions &HSOpts,
                               const LangOptions &Lang,
-                              const llvm::Triple &triple);
+                              const llvm::Triple &triple,
+                              llvm::raw_ostream *VerboseOutput = nullptr);
 
 void normalizeModuleCachePath(FileManager &FileMgr, StringRef Path,
                               SmallVectorImpl<char> &NormalizedPath);
diff --git a/clang/include/clang/Tooling/Tooling.h 
b/clang/include/clang/Tooling/Tooling.h
index 9909394495496..f2bdeb0420a11 100644
--- a/clang/include/clang/Tooling/Tooling.h
+++ b/clang/include/clang/Tooling/Tooling.h
@@ -79,15 +79,28 @@ getCC1Arguments(DiagnosticsEngine *Diagnostics,
 /// If your tool is based on FrontendAction, you should be deriving from
 /// FrontendActionFactory instead.
 class ToolAction {
+  llvm::raw_ostream *VerboseOutputStream = nullptr;
+
 public:
   virtual ~ToolAction();
 
+  /// Set the stream used for informational verbose output. The caller retains
+  /// ownership and must keep the stream alive while this action runs.
+  void setVerboseOutputStream(llvm::raw_ostream &OS) {
+    VerboseOutputStream = &OS;
+  }
+
   /// Perform an action for an invocation.
   virtual bool
   runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
                 FileManager *Files,
                 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
                 DiagnosticConsumer *DiagConsumer) = 0;
+
+protected:
+  llvm::raw_ostream *getVerboseOutputStream() const {
+    return VerboseOutputStream;
+  }
 };
 
 /// Interface to generate clang::FrontendActions.
@@ -289,6 +302,12 @@ class ToolInvocation {
     this->DiagOpts = DiagOpts;
   }
 
+  /// Set the stream used for informational verbose output. The caller retains
+  /// ownership and must keep the stream alive while this invocation runs.
+  void setVerboseOutputStream(llvm::raw_ostream &OS) {
+    VerboseOutputStream = &OS;
+  }
+
   /// Run the clang invocation.
   ///
   /// \returns True if there were no errors during execution.
@@ -307,6 +326,7 @@ class ToolInvocation {
   std::shared_ptr<PCHContainerOperations> PCHContainerOps;
   DiagnosticConsumer *DiagConsumer = nullptr;
   DiagnosticOptions *DiagOpts = nullptr;
+  llvm::raw_ostream *VerboseOutputStream = nullptr;
 };
 
 /// Utility to run a FrontendAction over a set of files.
@@ -345,6 +365,12 @@ class ClangTool {
     this->DiagConsumer = DiagConsumer;
   }
 
+  /// Set the stream used for informational verbose output. The caller retains
+  /// ownership and must keep the stream alive while this tool runs.
+  void setVerboseOutputStream(llvm::raw_ostream &OS) {
+    VerboseOutputStream = &OS;
+  }
+
   /// Map a virtual file to be used while running the tool.
   ///
   /// \param FilePath The path at which the content will be mapped.
@@ -400,6 +426,7 @@ class ClangTool {
   ArgumentsAdjuster ArgsAdjuster;
 
   DiagnosticConsumer *DiagConsumer = nullptr;
+  llvm::raw_ostream *VerboseOutputStream = nullptr;
 
   bool PrintErrorMessage = true;
 };
diff --git a/clang/lib/Driver/Compilation.cpp b/clang/lib/Driver/Compilation.cpp
index c81c4445a29f9..54e6d7767942c 100644
--- a/clang/lib/Driver/Compilation.cpp
+++ b/clang/lib/Driver/Compilation.cpp
@@ -168,7 +168,7 @@ int Compilation::ExecuteCommand(const Command &C,
                                 bool LogOnly) const {
   if ((getDriver().CCPrintOptions ||
        getArgs().hasArg(options::OPT_v)) && !getDriver().CCGenDiagnostics) {
-    raw_ostream *OS = &llvm::errs();
+    raw_ostream *OS = &getDriver().getVerboseOutputStream();
     std::unique_ptr<llvm::raw_fd_ostream> OwnedStream;
 
     // Follow gcc implementation of CC_PRINT_OPTIONS; we could also cache the
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 7a742e404bf5c..29700f7f3631b 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -200,8 +200,8 @@ std::string CUIDOptions::getCUID(StringRef InputFile,
 Driver::Driver(StringRef DriverExecutable, StringRef TargetTriple,
                DiagnosticsEngine &Diags, std::string Title,
                IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)
-    : Diags(Diags), VFS(std::move(VFS)), Mode(GCCMode),
-      SaveTemps(SaveTempsNone), BitcodeEmbed(EmbedNone),
+    : Diags(Diags), VFS(std::move(VFS)), VerboseOutputStream(&llvm::errs()),
+      Mode(GCCMode), SaveTemps(SaveTempsNone), BitcodeEmbed(EmbedNone),
       Offload(OffloadHostDevice), CXX20HeaderType(HeaderMode_None),
       ModulesModeCXX20(false), DriverExecutable(DriverExecutable),
       SysRoot(DEFAULT_SYSROOT), DriverTitle(Title), CCCPrintBindings(false),
@@ -2432,7 +2432,7 @@ int Driver::ExecuteCompilation(
     SmallVectorImpl<std::pair<int, const Command *>> &FailingCommands) {
   if (C.getArgs().hasArg(options::OPT_fdriver_only)) {
     if (C.getArgs().hasArg(options::OPT_v))
-      C.getJobs().Print(llvm::errs(), "\n", true);
+      C.getJobs().Print(getVerboseOutputStream(), "\n", true);
 
     C.ExecuteJobs(C.getJobs(), FailingCommands, /*LogOnly=*/true);
 
@@ -2445,7 +2445,7 @@ int Driver::ExecuteCompilation(
 
   // Just print if -### was present.
   if (C.getArgs().hasArg(options::OPT__HASH_HASH_HASH)) {
-    C.getJobs().Print(llvm::errs(), "\n", true);
+    C.getJobs().Print(getVerboseOutputStream(), "\n", true);
     return Diags.hasErrorOccurred() ? 1 : 0;
   }
 
@@ -2709,23 +2709,23 @@ bool Driver::HandleImmediateArgs(Compilation &C) {
       C.getArgs().hasArg(options::OPT_print_supported_cpus) ||
       C.getArgs().hasArg(options::OPT_print_supported_extensions) ||
       C.getArgs().hasArg(options::OPT_print_enabled_extensions)) {
-    PrintVersion(C, llvm::errs());
+    PrintVersion(C, getVerboseOutputStream());
     SuppressMissingInputWarning = true;
   }
 
   if (C.getArgs().hasArg(options::OPT_v)) {
     if (!SystemConfigDir.empty())
-      llvm::errs() << "System configuration file directory: "
-                   << SystemConfigDir << "\n";
+      getVerboseOutputStream()
+          << "System configuration file directory: " << SystemConfigDir << 
"\n";
     if (!UserConfigDir.empty())
-      llvm::errs() << "User configuration file directory: "
-                   << UserConfigDir << "\n";
+      getVerboseOutputStream()
+          << "User configuration file directory: " << UserConfigDir << "\n";
   }
 
   const ToolChain &TC = C.getDefaultToolChain();
 
   if (C.getArgs().hasArg(options::OPT_v))
-    TC.printVerboseInfo(llvm::errs());
+    TC.printVerboseInfo(getVerboseOutputStream());
 
   if (C.getArgs().hasArg(options::OPT_print_resource_dir)) {
     llvm::outs() << ResourceDir << '\n';
diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp 
b/clang/lib/Driver/ToolChains/Darwin.cpp
index 0fb94d8712e69..d376992cc0e1b 100644
--- a/clang/lib/Driver/ToolChains/Darwin.cpp
+++ b/clang/lib/Driver/ToolChains/Darwin.cpp
@@ -3078,8 +3078,8 @@ void AppleMachO::AddClangCXXStdlibIncludeArgs(
       addSystemInclude(DriverArgs, CC1Args, InstallBin);
       return;
     } else if (DriverArgs.hasArg(options::OPT_v)) {
-      llvm::errs() << "ignoring nonexistent directory \"" << InstallBin
-                   << "\"\n";
+      getDriver().getVerboseOutputStream()
+          << "ignoring nonexistent directory \"" << InstallBin << "\"\n";
     }
 
     // Otherwise, check for (2)
@@ -3089,8 +3089,8 @@ void AppleMachO::AddClangCXXStdlibIncludeArgs(
       addSystemInclude(DriverArgs, CC1Args, SysrootUsr);
       return;
     } else if (DriverArgs.hasArg(options::OPT_v)) {
-      llvm::errs() << "ignoring nonexistent directory \"" << SysrootUsr
-                   << "\"\n";
+      getDriver().getVerboseOutputStream()
+          << "ignoring nonexistent directory \"" << SysrootUsr << "\"\n";
     }
 
     // Otherwise, don't add any path.
diff --git a/clang/lib/Driver/ToolChains/ZOS.cpp 
b/clang/lib/Driver/ToolChains/ZOS.cpp
index 4c6df90e419b9..01cdb13869116 100644
--- a/clang/lib/Driver/ToolChains/ZOS.cpp
+++ b/clang/lib/Driver/ToolChains/ZOS.cpp
@@ -331,7 +331,7 @@ void ZOS::TryAddIncludeFromPath(llvm::SmallString<128> Path,
                                 llvm::opt::ArgStringList &CC1Args) const {
   if (!getVFS().exists(Path)) {
     if (DriverArgs.hasArg(options::OPT_v))
-      WithColor::warning(errs(), "Clang")
+      WithColor::warning(getDriver().getVerboseOutputStream(), "Clang")
           << "ignoring nonexistent directory \"" << Path << "\"\n";
     if (!DriverArgs.hasArg(options::OPT__HASH_HASH_HASH))
       return;
diff --git a/clang/lib/Frontend/CompilerInstance.cpp 
b/clang/lib/Frontend/CompilerInstance.cpp
index 87abcd38c1a92..bf599ef9a11c2 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -504,7 +504,8 @@ void 
CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) {
     HeaderSearchTriple = &PP->getAuxTargetInfo()->getTriple();
 
   ApplyHeaderSearchOptions(PP->getHeaderSearchInfo(), getHeaderSearchOpts(),
-                           PP->getLangOpts(), *HeaderSearchTriple);
+                           PP->getLangOpts(), *HeaderSearchTriple,
+                           &getVerboseOutputStream());
 
   PP->setPreprocessedOutput(getPreprocessorOutputOpts().ShowCPP);
 
diff --git a/clang/lib/Interpreter/Interpreter.cpp 
b/clang/lib/Interpreter/Interpreter.cpp
index 512b28be5a544..543a6a03e81e6 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -269,6 +269,9 @@ IncrementalCompilerBuilder::create(std::string TT,
 
   driver::Driver Driver(/*MainBinaryName=*/ClangArgv[0], TT, Diags);
   Driver.setCheckInputsExist(false); // the input comes from mem buffers
+  llvm::raw_ostream &VerboseOS =
+      VerboseOutputStream ? *VerboseOutputStream : llvm::errs();
+  Driver.setVerboseOutputStream(VerboseOS);
   llvm::ArrayRef<const char *> RF = llvm::ArrayRef(ClangArgv);
   std::unique_ptr<driver::Compilation> 
Compilation(Driver.BuildCompilation(RF));
 
@@ -277,13 +280,16 @@ IncrementalCompilerBuilder::create(std::string TT,
       return std::move(Err);
 
   if (Compilation->getArgs().hasArg(options::OPT_v))
-    Compilation->getJobs().Print(llvm::errs(), "\n", /*Quote=*/false);
+    Compilation->getJobs().Print(VerboseOS, "\n", /*Quote=*/false);
 
   auto ErrOrCC1Args = GetCC1Arguments(&Diags, Compilation.get());
   if (auto Err = ErrOrCC1Args.takeError())
     return std::move(Err);
 
-  return CreateCI(**ErrOrCC1Args);
+  auto CI = CreateCI(**ErrOrCC1Args);
+  if (CI)
+    (*CI)->setVerboseOutputStream(VerboseOS);
+  return CI;
 }
 
 llvm::Expected<std::unique_ptr<CompilerInstance>>
diff --git a/clang/lib/Lex/InitHeaderSearch.cpp 
b/clang/lib/Lex/InitHeaderSearch.cpp
index e894086b66e76..5dba00b330ce6 100644
--- a/clang/lib/Lex/InitHeaderSearch.cpp
+++ b/clang/lib/Lex/InitHeaderSearch.cpp
@@ -47,13 +47,16 @@ class InitHeaderSearch {
   std::vector<DirectoryLookupInfo> IncludePath;
   std::vector<std::pair<std::string, bool> > SystemHeaderPrefixes;
   HeaderSearch &Headers;
+  llvm::raw_ostream &VerboseOutput;
   bool Verbose;
   std::string IncludeSysroot;
   bool HasSysroot;
 
 public:
-  InitHeaderSearch(HeaderSearch &HS, bool verbose, StringRef sysroot)
-      : Headers(HS), Verbose(verbose), IncludeSysroot(std::string(sysroot)),
+  InitHeaderSearch(HeaderSearch &HS, bool verbose, StringRef sysroot,
+                   llvm::raw_ostream &VerboseOutput)
+      : Headers(HS), VerboseOutput(VerboseOutput), Verbose(verbose),
+        IncludeSysroot(std::string(sysroot)),
         HasSysroot(!(sysroot.empty() || sysroot == "/")) {}
 
   /// Add the specified path to the specified group list, prefixing the sysroot
@@ -165,8 +168,8 @@ bool InitHeaderSearch::AddUnmappedPath(const Twine &Path, 
IncludeDirGroup Group,
   }
 
   if (Verbose)
-    llvm::errs() << "ignoring nonexistent directory \""
-                 << MappedPathStr << "\"\n";
+    VerboseOutput << "ignoring nonexistent directory \"" << MappedPathStr
+                  << "\"\n";
   return false;
 }
 
@@ -282,7 +285,8 @@ void InitHeaderSearch::AddDefaultIncludePaths(
 /// remove the later (dead) ones.  Returns the number of non-system headers
 /// removed, which is used to update NumAngled.
 static unsigned RemoveDuplicates(std::vector<DirectoryLookupInfo> &SearchList,
-                                 unsigned First, bool Verbose) {
+                                 unsigned First, bool Verbose,
+                                 llvm::raw_ostream &VerboseOutput) {
   llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenDirs;
   llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenFrameworkDirs;
   llvm::SmallPtrSet<const HeaderMap *, 8> SeenHeaderMaps;
@@ -347,11 +351,11 @@ static unsigned 
RemoveDuplicates(std::vector<DirectoryLookupInfo> &SearchList,
     }
 
     if (Verbose) {
-      llvm::errs() << "ignoring duplicate directory \""
-                   << CurEntry.getName() << "\"\n";
+      VerboseOutput << "ignoring duplicate directory \"" << CurEntry.getName()
+                    << "\"\n";
       if (DirToRemove != i)
-        llvm::errs() << "  as it is a non-system directory that duplicates "
-                     << "a system directory\n";
+        VerboseOutput << "  as it is a non-system directory that duplicates "
+                      << "a system directory\n";
     }
     if (DirToRemove != i)
       ++NonSystemRemoved;
@@ -397,14 +401,14 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) {
       SearchList.push_back(Include);
 
   // Deduplicate and remember index.
-  RemoveDuplicates(SearchList, 0, Verbose);
+  RemoveDuplicates(SearchList, 0, Verbose, VerboseOutput);
   unsigned NumQuoted = SearchList.size();
 
   for (auto &Include : IncludePath)
     if (Include.Group == Angled)
       SearchList.push_back(Include);
 
-  RemoveDuplicates(SearchList, NumQuoted, Verbose);
+  RemoveDuplicates(SearchList, NumQuoted, Verbose, VerboseOutput);
   unsigned NumAngled = SearchList.size();
 
   for (auto &Include : IncludePath)
@@ -423,7 +427,8 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) {
   // Remove duplicates across both the Angled and System directories.  GCC does
   // this and failing to remove duplicates across these two groups breaks
   // #include_next.
-  unsigned NonSystemRemoved = RemoveDuplicates(SearchList, NumQuoted, Verbose);
+  unsigned NonSystemRemoved =
+      RemoveDuplicates(SearchList, NumQuoted, Verbose, VerboseOutput);
   NumAngled -= NonSystemRemoved;
 
   Headers.SetSearchPaths(extractLookups(SearchList), NumQuoted, NumAngled,
@@ -433,10 +438,10 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) {
 
   // If verbose, print the list of directories that will be searched.
   if (Verbose) {
-    llvm::errs() << "#include \"...\" search starts here:\n";
+    VerboseOutput << "#include \"...\" search starts here:\n";
     for (unsigned i = 0, e = SearchList.size(); i != e; ++i) {
       if (i == NumQuoted)
-        llvm::errs() << "#include <...> search starts here:\n";
+        VerboseOutput << "#include <...> search starts here:\n";
       StringRef Name = SearchList[i].Lookup.getName();
       const char *Suffix;
       if (SearchList[i].Lookup.isNormalDir())
@@ -447,17 +452,19 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) {
         assert(SearchList[i].Lookup.isHeaderMap() && "Unknown 
DirectoryLookup");
         Suffix = " (headermap)";
       }
-      llvm::errs() << " " << Name << Suffix << "\n";
+      VerboseOutput << " " << Name << Suffix << "\n";
     }
-    llvm::errs() << "End of search list.\n";
+    VerboseOutput << "End of search list.\n";
   }
 }
 
 void clang::ApplyHeaderSearchOptions(HeaderSearch &HS,
                                      const HeaderSearchOptions &HSOpts,
                                      const LangOptions &Lang,
-                                     const llvm::Triple &Triple) {
-  InitHeaderSearch Init(HS, HSOpts.Verbose, HSOpts.Sysroot);
+                                     const llvm::Triple &Triple,
+                                     llvm::raw_ostream *VerboseOutput) {
+  InitHeaderSearch Init(HS, HSOpts.Verbose, HSOpts.Sysroot,
+                        VerboseOutput ? *VerboseOutput : llvm::errs());
 
   // Add the user defined entries.
   for (unsigned i = 0, e = HSOpts.UserEntries.size(); i != e; ++i) {
diff --git a/clang/lib/Tooling/Tooling.cpp b/clang/lib/Tooling/Tooling.cpp
index 71307d1fe7307..a9eeec8cf550b 100644
--- a/clang/lib/Tooling/Tooling.cpp
+++ b/clang/lib/Tooling/Tooling.cpp
@@ -372,6 +372,9 @@ bool ToolInvocation::run() {
   for (const std::string &Str : CommandLine)
     Argv.push_back(Str.c_str());
   const char *const BinaryName = Argv[0];
+  llvm::raw_ostream &VerboseOS =
+      VerboseOutputStream ? *VerboseOutputStream : llvm::errs();
+  Action->setVerboseOutputStream(VerboseOS);
 
   // Parse diagnostic options from the driver command-line only if none were
   // explicitly set.
@@ -405,6 +408,7 @@ bool ToolInvocation::run() {
 
   const std::unique_ptr<driver::Driver> Driver(
       newDriver(&*Diagnostics, BinaryName, Files->getVirtualFileSystemPtr()));
+  Driver->setVerboseOutputStream(VerboseOS);
   // The "input file not found" diagnostics from the driver are useful.
   // The driver is only aware of the VFS working directory, but some clients
   // change this at the FileManager level instead.
@@ -431,9 +435,11 @@ bool ToolInvocation::runInvocation(
     std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
   // Show the invocation, with -v.
   if (Invocation->getHeaderSearchOpts().Verbose) {
-    llvm::errs() << "clang Invocation:\n";
-    Compilation->getJobs().Print(llvm::errs(), "\n", true);
-    llvm::errs() << "\n";
+    llvm::raw_ostream &OS =
+        VerboseOutputStream ? *VerboseOutputStream : llvm::errs();
+    OS << "clang Invocation:\n";
+ ...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/226022
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to