[clang] [clang] Make verbose output streams configurable for embedded clients (PR #226022)
Anutosh Bhat via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 23 22:47:05 PDT 2026
https://github.com/anutosh491 updated https://github.com/llvm/llvm-project/pull/226022
>From 0f54ee4d78583656366c1f93bee58b2893cf8f7d Mon Sep 17 00:00:00 2001
From: anutosh491 <andersonbhat491 at gmail.com>
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
More information about the cfe-commits
mailing list