https://github.com/anutosh491 updated https://github.com/llvm/llvm-project/pull/226056
>From e9f405061c36df91b8a9f8c138967eb8b5318e44 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 21b83dfefa8426..791da205e3354f 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 adaa126024c3ca..12d2f2c745348c 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 c81c4445a29f95..071284c1149761 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 7649941a68b1cc..dc35b36fa06269 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 0fb94d8712e69f..fa25cc7aab645d 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 87abcd38c1a927..bf599ef9a11c25 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 512b28be5a544e..b32b43bd71f8a8 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 e894086b66e76d..2f2d0783c2a4a8 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 71307d1fe73076..66a23476a487b4 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 7b4633bfc9e7a3..8e3bbeca1c9cda 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 11a5491dd35fb1..36e9c7dd2e9f97 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 841fe90515608e..f3bb68462c0d83 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 28401d674479cc..132d201643bb17 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 aded02c7b34027..b00f1587e8bfc4 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
