https://github.com/anutosh491 updated 
https://github.com/llvm/llvm-project/pull/226022

>From 0f54ee4d78583656366c1f93bee58b2893cf8f7d Mon Sep 17 00:00:00 2001
From: anutosh491 <[email protected]>
Date: Thu, 24 Sep 2026 10:09:42 +0530
Subject: [PATCH] [clang] Make verbose output streams configurable

---
 clang/include/clang/Driver/Driver.h           | 13 ++++
 clang/include/clang/Interpreter/Interpreter.h |  8 +++
 clang/include/clang/Lex/HeaderSearch.h        |  4 +-
 clang/include/clang/Tooling/Tooling.h         | 27 ++++++++
 clang/lib/Driver/Compilation.cpp              |  2 +-
 clang/lib/Driver/Driver.cpp                   | 20 +++---
 clang/lib/Driver/ToolChains/Darwin.cpp        |  8 +--
 clang/lib/Driver/ToolChains/ZOS.cpp           |  2 +-
 clang/lib/Frontend/CompilerInstance.cpp       |  3 +-
 clang/lib/Interpreter/Interpreter.cpp         | 10 ++-
 clang/lib/Lex/InitHeaderSearch.cpp            | 43 +++++++-----
 clang/lib/Tooling/Tooling.cpp                 | 16 ++++-
 .../IncrementalCompilerBuilderTest.cpp        | 28 ++++++++
 clang/unittests/Tooling/ToolingTest.cpp       | 66 +++++++++++++++++++
 14 files changed, 209 insertions(+), 41 deletions(-)

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";
+    Compilation->getJobs().Print(OS, "\n", true);
+    OS << "\n";
   }
 
   return Action->runInvocation(std::move(Invocation), Files,
@@ -446,6 +452,8 @@ bool FrontendActionFactory::runInvocation(
     DiagnosticConsumer *DiagConsumer) {
   // Create a compiler instance to handle the actual work.
   CompilerInstance Compiler(std::move(Invocation), std::move(PCHContainerOps));
+  if (llvm::raw_ostream *OS = getVerboseOutputStream())
+    Compiler.setVerboseOutputStream(*OS);
   Compiler.setVirtualFileSystem(Files->getVirtualFileSystemPtr());
   Compiler.setFileManager(Files);
   Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
@@ -627,6 +635,8 @@ int ClangTool::run(ToolAction *Action) {
       ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
                                 PCHContainerOps);
       Invocation.setDiagnosticConsumer(DiagConsumer);
+      if (VerboseOutputStream)
+        Invocation.setVerboseOutputStream(*VerboseOutputStream);
 
       if (!Invocation.run()) {
         // FIXME: Diagnostics should be used instead.
diff --git a/clang/unittests/Interpreter/IncrementalCompilerBuilderTest.cpp 
b/clang/unittests/Interpreter/IncrementalCompilerBuilderTest.cpp
index 7b4633bfc9e7a..5c9c8576fc2ea 100644
--- a/clang/unittests/Interpreter/IncrementalCompilerBuilderTest.cpp
+++ b/clang/unittests/Interpreter/IncrementalCompilerBuilderTest.cpp
@@ -6,11 +6,14 @@
 //
 
//===----------------------------------------------------------------------===//
 
+#include "InterpreterTestFixture.h"
+
 #include "clang/Basic/TargetOptions.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Interpreter/Interpreter.h"
 #include "clang/Lex/PreprocessorOptions.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gtest/gtest.h"
 
 using namespace llvm;
@@ -18,6 +21,9 @@ using namespace clang;
 
 namespace {
 
+class IncrementalCompilerBuilderInterpreterTest
+    : public InterpreterTestBase {};
+
 // Usually FrontendAction takes the raw pointers and wraps them back into
 // unique_ptrs in InitializeFileRemapping()
 static void cleanupRemappedFileBuffers(CompilerInstance &CI) {
@@ -36,6 +42,28 @@ TEST(IncrementalCompilerBuilder, SetCompilerArgs) {
   cleanupRemappedFileBuffers(*CI);
 }
 
+TEST_F(IncrementalCompilerBuilderInterpreterTest, SetVerboseOutputStream) {
+  std::string Output;
+  llvm::raw_string_ostream OS(Output);
+  std::vector<const char *> ClangArgv = {"-v", "-nostdinc", "-nostdinc++"};
+  auto CB = clang::IncrementalCompilerBuilder();
+  CB.SetCompilerArgs(ClangArgv);
+  CB.SetVerboseOutputStream(OS);
+
+  auto CI = cantFail(CB.CreateCpp());
+  OS.flush();
+  EXPECT_NE(Output.find("clang version"), std::string::npos);
+  EXPECT_NE(Output.find("-cc1"), std::string::npos);
+
+  auto Interp = cantFail(clang::Interpreter::create(std::move(CI)));
+  OS.flush();
+  EXPECT_NE(Output.find("clang -cc1 version"), std::string::npos);
+  EXPECT_NE(Output.find("#include \"...\" search starts here:"),
+            std::string::npos);
+  EXPECT_NE(Output.find("End of search list."), std::string::npos);
+  cleanupRemappedFileBuffers(*Interp->getCompilerInstance());
+}
+
 TEST(IncrementalCompilerBuilder, SetTargetTriple) {
 // FIXME : This test doesn't current work for Emscripten builds.
 // It should be possible to make it work.For details on how it fails and
diff --git a/clang/unittests/Tooling/ToolingTest.cpp 
b/clang/unittests/Tooling/ToolingTest.cpp
index 11a5491dd35fb..2648a1c5d372a 100644
--- a/clang/unittests/Tooling/ToolingTest.cpp
+++ b/clang/unittests/Tooling/ToolingTest.cpp
@@ -26,6 +26,7 @@
 #include "llvm/Support/JSON.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/raw_ostream.h"
 #include "llvm/TargetParser/Host.h"
 #include "gtest/gtest.h"
 #include <algorithm>
@@ -56,6 +57,23 @@ class TestAction : public clang::ASTFrontendAction {
   std::unique_ptr<clang::ASTConsumer> TestConsumer;
 };
 
+class VerifyVerboseOutputAction : public ToolAction {
+public:
+  VerifyVerboseOutputAction(llvm::raw_ostream &Expected, bool &Observed)
+      : Expected(Expected), Observed(Observed) {}
+
+  bool runInvocation(std::shared_ptr<CompilerInvocation>, FileManager *,
+                     std::shared_ptr<PCHContainerOperations>,
+                     DiagnosticConsumer *) override {
+    Observed = getVerboseOutputStream() == &Expected;
+    return true;
+  }
+
+private:
+  llvm::raw_ostream &Expected;
+  bool &Observed;
+};
+
 class FindTopLevelDeclConsumer : public clang::ASTConsumer {
  public:
   explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl)
@@ -226,6 +244,54 @@ TEST(ToolInvocation, TestMapVirtualFile) {
   EXPECT_TRUE(Invocation.run());
 }
 
+TEST(ToolInvocation, VerboseOutputStream) {
+  auto OverlayFileSystem =
+      llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(
+          llvm::vfs::getRealFileSystem());
+  auto InMemoryFileSystem =
+      llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
+  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
+  auto Files = llvm::makeIntrusiveRefCnt<FileManager>(FileSystemOptions(),
+                                                      OverlayFileSystem);
+  InMemoryFileSystem->addFile("test.cpp", 0,
+                              llvm::MemoryBuffer::getMemBuffer("int 
value;\n"));
+
+  std::vector<std::string> Args = {"tool-executable", "-v", "-fsyntax-only",
+                                   "test.cpp"};
+  ToolInvocation Invocation(Args, std::make_unique<SyntaxOnlyAction>(),
+                            Files.get());
+  std::string Output;
+  llvm::raw_string_ostream OS(Output);
+  Invocation.setVerboseOutputStream(OS);
+
+  EXPECT_TRUE(Invocation.run());
+  OS.flush();
+  EXPECT_NE(Output.find("clang version"), std::string::npos);
+  EXPECT_NE(Output.find("clang Invocation:"), std::string::npos);
+  EXPECT_NE(Output.find("-cc1"), std::string::npos);
+  EXPECT_NE(Output.find("clang -cc1 version"), std::string::npos);
+  EXPECT_NE(Output.find("End of search list."), std::string::npos);
+}
+
+TEST(ToolInvocation, PropagatesVerboseOutputStreamToToolAction) {
+  auto FileSystem = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
+  FileSystem->addFile("test.cpp", 0,
+                      llvm::MemoryBuffer::getMemBuffer("int value;\n"));
+  FileManager Files(FileSystemOptions(), FileSystem);
+  std::string Output;
+  llvm::raw_string_ostream OS(Output);
+  bool Observed = false;
+  VerifyVerboseOutputAction Action(OS, Observed);
+  std::vector<std::string> Args = {"tool-executable", "-v", "-fsyntax-only",
+                                   "test.cpp"};
+  ToolInvocation Invocation(Args, &Action, &Files,
+                            std::make_shared<PCHContainerOperations>());
+  Invocation.setVerboseOutputStream(OS);
+
+  EXPECT_TRUE(Invocation.run());
+  EXPECT_TRUE(Observed);
+}
+
 TEST(ToolInvocation, TestVirtualModulesCompilation) {
   // FIXME: Currently, this only tests that we don't exit with an error if a
   // mapped module.modulemap is found on the include path. In the future, 
expand

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

Reply via email to