[Driver] Add callback to Command execution

Summary:
Object of type `Compilation` now can keep a callback that is called
after each execution of `Command`. This must simplify adaptation of
clang in custom distributions and allow facilities like collection of
execution statistics.

Reviewers: rsmith, rjmccall, Eugene.Zelenko

Subscribers: cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D78899
This commit is contained in:
Serge Pavlov
2020-04-24 12:48:39 +07:00
parent ab7ef35d34
commit 20b4f4f760
3 changed files with 39 additions and 0 deletions

View File

@@ -115,6 +115,11 @@ class Compilation {
/// Optional redirection for stdin, stdout, stderr.
std::vector<Optional<StringRef>> 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<void(const Command &, int)> PostCallback;
/// Whether we're compiling for diagnostic purposes.
bool ForDiagnostics = false;
@@ -212,6 +217,14 @@ public:
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<void(const Command &, int)> &CB) {
PostCallback = CB;
}
/// Returns the sysroot path.
StringRef getSysRoot() const;

View File

@@ -193,6 +193,8 @@ int Compilation::ExecuteCommand(const Command &C,
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;

View File

@@ -289,4 +289,28 @@ TEST(ToolChainTest, CommandOutput) {
EXPECT_EQ("a.out", ExeFile);
}
TEST(ToolChainTest, PostCallback) {
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
struct TestDiagnosticConsumer : public DiagnosticConsumer {};
DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer);
IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> 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<Compilation> 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.