https://github.com/anutosh491 created 
https://github.com/llvm/llvm-project/pull/226056

This is an alternative to what we originally proposed in 
[#226022](https://github.com/llvm/llvm-project/pull/226022).

In that PR, we tried to make the verbose output stream configurable through the 
different Clang entry points. Vassil suggested looking at how `llvm::dbgs()` 
works and adding an `llvm::infos()` stream instead, which would use 
`llvm::errs()` by default but could be redirected by the embedding application.

I think this is also a good direction because the use case is not necessarily 
limited to Clang. Other LLVM tools embedded inside an application may also want 
to distinguish informational output from actual errors.

This patch adds the small LLVM API and updates the Clang output relevant to our 
original use case: the version information, generated `-cc1` command, and 
header-search information.

The default behaviour remains unchanged, so command-line tools will continue 
writing this information to stderr. Embedded clients such as 
xeus-cpp/JupyterLite and WasmBolt can instead redirect `llvm::infos()` to 
`llvm::outs()` or an application-owned stream, while actual diagnostics and 
errors continue using their existing paths.

>From fc3b1a5f415c66bfa9e761b21e7b941e5de417ee Mon Sep 17 00:00:00 2001
From: anutosh491 <[email protected]>
Date: Thu, 24 Sep 2026 13:28:00 +0530
Subject: [PATCH] [LLVM][Clang] Add a configurable informational output stream

---
 .../include/clang/Frontend/CompilerInstance.h |  2 +-
 clang/include/clang/Lex/HeaderSearch.h        |  3 +-
 clang/lib/Driver/Compilation.cpp              |  2 +-
 clang/lib/Driver/Driver.cpp                   | 16 +++----
 clang/lib/Driver/ToolChains/Darwin.cpp        |  8 ++--
 clang/lib/Frontend/CompilerInstance.cpp       |  3 +-
 clang/lib/Interpreter/Interpreter.cpp         |  2 +-
 clang/lib/Lex/InitHeaderSearch.cpp            | 43 +++++++++++--------
 clang/lib/Tooling/Tooling.cpp                 |  6 +--
 .../IncrementalCompilerBuilderTest.cpp        | 30 +++++++++++++
 clang/unittests/Tooling/ToolingTest.cpp       | 33 ++++++++++++++
 llvm/include/llvm/Support/raw_ostream.h       | 13 ++++++
 llvm/lib/Support/raw_ostream.cpp              | 10 +++++
 llvm/unittests/Support/raw_ostream_test.cpp   | 14 ++++++
 14 files changed, 147 insertions(+), 38 deletions(-)

diff --git a/clang/include/clang/Frontend/CompilerInstance.h 
b/clang/include/clang/Frontend/CompilerInstance.h
index 21b83dfefa842..791da205e3354 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -187,7 +187,7 @@ class CompilerInstance : public ModuleLoader {
   std::unique_ptr<raw_ostream> OwnedVerboseOutputStream;
 
   /// The stream for verbose output.
-  raw_ostream *VerboseOutputStream = &llvm::errs();
+  raw_ostream *VerboseOutputStream = &llvm::infos();
 
   /// The list of active output files.
   std::list<llvm::vfs::OutputFile> OutputFiles;
diff --git a/clang/include/clang/Lex/HeaderSearch.h 
b/clang/include/clang/Lex/HeaderSearch.h
index adaa126024c3c..12d2f2c745348 100644
--- a/clang/include/clang/Lex/HeaderSearch.h
+++ b/clang/include/clang/Lex/HeaderSearch.h
@@ -1061,7 +1061,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/lib/Driver/Compilation.cpp b/clang/lib/Driver/Compilation.cpp
index c81c4445a29f9..071284c114976 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 = &llvm::infos();
     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 7649941a68b1c..dc35b36fa0626 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -2405,7 +2405,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(llvm::infos(), "\n", true);
 
     C.ExecuteJobs(C.getJobs(), FailingCommands, /*LogOnly=*/true);
 
@@ -2418,7 +2418,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(llvm::infos(), "\n", true);
     return Diags.hasErrorOccurred() ? 1 : 0;
   }
 
@@ -2682,23 +2682,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, llvm::infos());
     SuppressMissingInputWarning = true;
   }
 
   if (C.getArgs().hasArg(options::OPT_v)) {
     if (!SystemConfigDir.empty())
-      llvm::errs() << "System configuration file directory: "
-                   << SystemConfigDir << "\n";
+      llvm::infos() << "System configuration file directory: "
+                    << SystemConfigDir << "\n";
     if (!UserConfigDir.empty())
-      llvm::errs() << "User configuration file directory: "
-                   << UserConfigDir << "\n";
+      llvm::infos() << "User configuration file directory: " << UserConfigDir
+                    << "\n";
   }
 
   const ToolChain &TC = C.getDefaultToolChain();
 
   if (C.getArgs().hasArg(options::OPT_v))
-    TC.printVerboseInfo(llvm::errs());
+    TC.printVerboseInfo(llvm::infos());
 
   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..fa25cc7aab645 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";
+      llvm::infos() << "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";
+      llvm::infos() << "ignoring nonexistent directory \"" << SysrootUsr
+                    << "\"\n";
     }
 
     // Otherwise, don't add any path.
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..b32b43bd71f8a 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -277,7 +277,7 @@ 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(llvm::infos(), "\n", /*Quote=*/false);
 
   auto ErrOrCC1Args = GetCC1Arguments(&Diags, Compilation.get());
   if (auto Err = ErrOrCC1Args.takeError())
diff --git a/clang/lib/Lex/InitHeaderSearch.cpp 
b/clang/lib/Lex/InitHeaderSearch.cpp
index e894086b66e76..2f2d0783c2a4a 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::infos());
 
   // 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..66a23476a487b 100644
--- a/clang/lib/Tooling/Tooling.cpp
+++ b/clang/lib/Tooling/Tooling.cpp
@@ -431,9 +431,9 @@ 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::infos() << "clang Invocation:\n";
+    Compilation->getJobs().Print(llvm::infos(), "\n", true);
+    llvm::infos() << "\n";
   }
 
   return Action->runInvocation(std::move(Invocation), Files,
diff --git a/clang/unittests/Interpreter/IncrementalCompilerBuilderTest.cpp 
b/clang/unittests/Interpreter/IncrementalCompilerBuilderTest.cpp
index 7b4633bfc9e7a..8e3bbeca1c9cd 100644
--- a/clang/unittests/Interpreter/IncrementalCompilerBuilderTest.cpp
+++ b/clang/unittests/Interpreter/IncrementalCompilerBuilderTest.cpp
@@ -6,11 +6,15 @@
 //
 
//===----------------------------------------------------------------------===//
 
+#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/ADT/ScopeExit.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gtest/gtest.h"
 
 using namespace llvm;
@@ -18,6 +22,9 @@ using namespace clang;
 
 namespace {
 
+class IncrementalCompilerBuilderInfoStreamTest
+    : public InterpreterTestBase {};
+
 // Usually FrontendAction takes the raw pointers and wraps them back into
 // unique_ptrs in InitializeFileRemapping()
 static void cleanupRemappedFileBuffers(CompilerInstance &CI) {
@@ -36,6 +43,29 @@ TEST(IncrementalCompilerBuilder, SetCompilerArgs) {
   cleanupRemappedFileBuffers(*CI);
 }
 
+TEST_F(IncrementalCompilerBuilderInfoStreamTest, RoutesVerboseOutputToInfos) {
+  std::string Output;
+  llvm::raw_string_ostream OS(Output);
+  llvm::setInfoOutputStream(OS);
+  llvm::scope_exit ResetInfoOutput([] { llvm::resetInfoOutputStream(); });
+
+  std::vector<const char *> ClangArgv = {"-v", "-nostdinc", "-nostdinc++"};
+  auto CB = clang::IncrementalCompilerBuilder();
+  CB.SetCompilerArgs(ClangArgv);
+
+  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);
+}
+
 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..36e9c7dd2e9f9 100644
--- a/clang/unittests/Tooling/ToolingTest.cpp
+++ b/clang/unittests/Tooling/ToolingTest.cpp
@@ -21,11 +21,13 @@
 #include "clang/Tooling/ArgumentsAdjusters.h"
 #include "clang/Tooling/CompilationDatabase.h"
 #include "clang/Tooling/JSONCompilationDatabase.h"
+#include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringRef.h"
 #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>
@@ -226,6 +228,37 @@ TEST(ToolInvocation, TestMapVirtualFile) {
   EXPECT_TRUE(Invocation.run());
 }
 
+TEST(ToolInvocation, RoutesVerboseOutputToInfos) {
+  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::string Output;
+  llvm::raw_string_ostream OS(Output);
+  llvm::setInfoOutputStream(OS);
+  llvm::scope_exit ResetInfoOutput([] { llvm::resetInfoOutputStream(); });
+
+  std::vector<std::string> Args = {"tool-executable", "-v", "-fsyntax-only",
+                                   "test.cpp"};
+  ToolInvocation Invocation(Args, std::make_unique<SyntaxOnlyAction>(),
+                            Files.get());
+
+  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, 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
diff --git a/llvm/include/llvm/Support/raw_ostream.h 
b/llvm/include/llvm/Support/raw_ostream.h
index 841fe90515608..f3bb68462c0d8 100644
--- a/llvm/include/llvm/Support/raw_ostream.h
+++ b/llvm/include/llvm/Support/raw_ostream.h
@@ -619,6 +619,19 @@ LLVM_ABI raw_fd_ostream &outs();
 /// expected place.
 LLVM_ABI raw_fd_ostream &errs();
 
+/// This returns the stream used for informational output. By default it is
+/// wired to errs(), preserving the traditional command-line behavior. Hosts
+/// embedding LLVM may redirect it with setInfoOutputStream().
+LLVM_ABI raw_ostream &infos();
+
+/// Redirect informational output process-wide to \p OS. Configure this before
+/// concurrent use. The caller retains ownership and must ensure that \p OS
+/// outlives its use as the informational stream.
+LLVM_ABI void setInfoOutputStream(raw_ostream &OS);
+
+/// Restore the informational output stream to errs().
+LLVM_ABI void resetInfoOutputStream();
+
 /// This returns a reference to a raw_ostream which simply discards output.
 LLVM_ABI raw_ostream &nulls();
 
diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
index 28401d674479c..132d201643bb1 100644
--- a/llvm/lib/Support/raw_ostream.cpp
+++ b/llvm/lib/Support/raw_ostream.cpp
@@ -898,6 +898,16 @@ raw_fd_ostream &llvm::errs() {
   return S;
 }
 
+static raw_ostream *InfoOutputStream;
+
+raw_ostream &llvm::infos() {
+  return InfoOutputStream ? *InfoOutputStream : errs();
+}
+
+void llvm::setInfoOutputStream(raw_ostream &OS) { InfoOutputStream = &OS; }
+
+void llvm::resetInfoOutputStream() { InfoOutputStream = nullptr; }
+
 /// nulls() - This returns a reference to a raw_ostream which discards output.
 raw_ostream &llvm::nulls() {
   static raw_null_ostream S;
diff --git a/llvm/unittests/Support/raw_ostream_test.cpp 
b/llvm/unittests/Support/raw_ostream_test.cpp
index aded02c7b3402..b00f1587e8bfc 100644
--- a/llvm/unittests/Support/raw_ostream_test.cpp
+++ b/llvm/unittests/Support/raw_ostream_test.cpp
@@ -129,6 +129,20 @@ TEST(raw_ostreamTest, Types_Unbuffered) {
   EXPECT_EQ("X", printToString(X{}));
 }
 
+TEST(raw_ostreamTest, InfoOutputStream) {
+  EXPECT_EQ(&errs(), &infos());
+
+  std::string Str;
+  raw_string_ostream OS(Str);
+  setInfoOutputStream(OS);
+  infos() << "information";
+  OS.flush();
+  EXPECT_EQ("information", Str);
+
+  resetInfoOutputStream();
+  EXPECT_EQ(&errs(), &infos());
+}
+
 TEST(raw_ostreamTest, BufferEdge) {  
   EXPECT_EQ("1.20", printToString(format("%.2f", 1.2), 1));
   EXPECT_EQ("1.20", printToString(format("%.2f", 1.2), 2));

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

Reply via email to