Index: include/clang/Frontend/CompilerInstance.h =================================================================== --- include/clang/Frontend/CompilerInstance.h +++ include/clang/Frontend/CompilerInstance.h @@ -161,6 +161,12 @@ /// One or more modules failed to build. bool ModuleBuildFailed = false; + /// Whether we should delete VerboseOutputStream on destruction. + bool OwnsVerboseOutputStream = false; + + /// The stream for verbose output. + raw_ostream *VerboseOutputStream = &llvm::errs(); + /// Holds information about the output file. /// /// If TempFilename is not empty we must rename it to Filename at the end. @@ -223,9 +229,6 @@ /// \param Act - The action to execute. /// \return - True on success. // - // FIXME: This function should take the stream to write any debugging / - // verbose output to as an argument. - // // FIXME: Eliminate the llvm_shutdown requirement, that should either be part // of the context or else not CompilerInstance specific. bool ExecuteAction(FrontendAction &Act); @@ -356,6 +359,21 @@ } /// } + /// @name VerboseOutputStream + /// } + + /// Replace the current stream for verbose output. + /// + /// If not set, this stream defaults to \c llvm::errs(). + void setVerboseOutputStream(raw_ostream &Value, + bool OwnsOutputStream = false); + + /// Get the current stream for verbose output. + raw_ostream &getVerboseOutputStream() { + return *VerboseOutputStream; + } + + /// } /// @name Target Info /// { Index: lib/Frontend/CompilerInstance.cpp =================================================================== --- lib/Frontend/CompilerInstance.cpp +++ lib/Frontend/CompilerInstance.cpp @@ -70,6 +70,8 @@ CompilerInstance::~CompilerInstance() { assert(OutputFiles.empty() && "Still output files in flight?"); + if (OwnsVerboseOutputStream) + delete VerboseOutputStream; } void CompilerInstance::setInvocation( @@ -88,6 +90,12 @@ Diagnostics = Value; } +void CompilerInstance::setVerboseOutputStream(raw_ostream &Value, + bool OwnsOutputStream) { + OwnsVerboseOutputStream = OwnsOutputStream; + VerboseOutputStream = &Value; +} + void CompilerInstance::setTarget(TargetInfo *Value) { Target = Value; } void CompilerInstance::setAuxTarget(TargetInfo *Value) { AuxTarget = Value; } @@ -907,9 +915,7 @@ assert(!getFrontendOpts().ShowHelp && "Client must handle '-help'!"); assert(!getFrontendOpts().ShowVersion && "Client must handle '-version'!"); - // FIXME: Take this as an argument, once all the APIs we used have moved to - // taking it as an input instead of hard-coding llvm::errs. - raw_ostream &OS = llvm::errs(); + raw_ostream &OS = getVerboseOutputStream(); if (!Act.PrepareToExecute(*this)) return false; Index: unittests/Frontend/OutputStreamTest.cpp =================================================================== --- unittests/Frontend/OutputStreamTest.cpp +++ unittests/Frontend/OutputStreamTest.cpp @@ -10,6 +10,7 @@ #include "clang/CodeGen/BackendUtil.h" #include "clang/CodeGen/CodeGenAction.h" #include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/FrontendTool/Utils.h" #include "clang/Lex/PreprocessorOptions.h" #include "gtest/gtest.h" @@ -43,4 +44,29 @@ EXPECT_TRUE(!IRBuffer.empty()); EXPECT_TRUE(StringRef(IRBuffer.data()).startswith("BC")); } + +TEST(FrontendOutputTests, TestVerboseOutputStream) { + auto Invocation = std::make_shared(); + Invocation->getPreprocessorOpts().addRemappedFile( + "test.cc", MemoryBuffer::getMemBuffer("invalid").release()); + Invocation->getFrontendOpts().Inputs.push_back( + FrontendInputFile("test.cc", InputKind::CXX)); + Invocation->getFrontendOpts().ProgramAction = EmitBC; + Invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu"; + CompilerInstance Compiler; + + std::string VerboseBuffer; + raw_string_ostream VerboseStream(VerboseBuffer); + + Compiler.setInvocation(std::move(Invocation)); + IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); + Compiler.createDiagnostics( + new TextDiagnosticPrinter(llvm::nulls(), &*DiagOpts), true); + Compiler.setVerboseOutputStream(VerboseStream); + + bool Success = ExecuteCompilerInvocation(&Compiler); + EXPECT_FALSE(Success); + EXPECT_TRUE(!VerboseStream.str().empty()); + EXPECT_TRUE(StringRef(VerboseBuffer.data()).contains("errors generated")); +} }