diff --git a/clang/include/clang/Driver/Compilation.h b/clang/include/clang/Driver/Compilation.h --- a/clang/include/clang/Driver/Compilation.h +++ b/clang/include/clang/Driver/Compilation.h @@ -115,6 +115,11 @@ /// Optional redirection for stdin, stdout, stderr. std::vector> Redirects; + /// Callback called after compilation job has been finished. + /// Arguments of the callback are the compilation job as an instance of + /// class Command and the exit status of the corresponding child process. + std::function PostCallback; + /// Whether we're compiling for diagnostic purposes. bool ForDiagnostics = false; @@ -212,6 +217,14 @@ return FailureResultFiles; } + /// Installs a handler that is executed when a compilation job is finished. + /// The arguments of the callback specify the compilation job as an instance + /// of class Command and the exit status of the child process executed that + /// job. + void setPostCallback(const std::function &CB) { + PostCallback = CB; + } + /// Returns the sysroot path. StringRef getSysRoot() const; diff --git a/clang/lib/Driver/Compilation.cpp b/clang/lib/Driver/Compilation.cpp --- a/clang/lib/Driver/Compilation.cpp +++ b/clang/lib/Driver/Compilation.cpp @@ -193,6 +193,8 @@ std::string Error; bool ExecutionFailed; int Res = C.Execute(Redirects, &Error, &ExecutionFailed); + if (PostCallback) + PostCallback(C, Res); if (!Error.empty()) { assert(Res && "Error string set with 0 result code!"); getDriver().Diag(diag::err_drv_command_failure) << Error; diff --git a/clang/unittests/Driver/ToolChainTest.cpp b/clang/unittests/Driver/ToolChainTest.cpp --- a/clang/unittests/Driver/ToolChainTest.cpp +++ b/clang/unittests/Driver/ToolChainTest.cpp @@ -289,4 +289,28 @@ EXPECT_EQ("a.out", ExeFile); } +TEST(ToolChainTest, PostCallback) { + IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); + IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); + struct TestDiagnosticConsumer : public DiagnosticConsumer {}; + DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); + IntrusiveRefCntPtr InMemoryFileSystem( + new llvm::vfs::InMemoryFileSystem); + + // The executable path must not exist. + Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, + "clang LLVM compiler", InMemoryFileSystem); + CCDriver.setCheckInputsExist(false); + std::unique_ptr CC( + CCDriver.BuildCompilation({"/home/test/bin/clang", "foo.cpp"})); + bool CallbackHasCalled = false; + CC->setPostCallback( + [&](const Command &C, int Ret) { CallbackHasCalled = true; }); + const JobList &Jobs = CC->getJobs(); + auto &CmdCompile = Jobs.getJobs().front(); + const Command *FailingCmd = nullptr; + CC->ExecuteCommand(*CmdCompile, FailingCmd); + EXPECT_TRUE(CallbackHasCalled); +} + } // end anonymous namespace.