to reflect the new license. We understand that people may be surprised that we're moving the header entirely to discuss the new license. We checked this carefully with the Foundation's lawyer and we believe this is the correct approach. Essentially, all code in the project is now made available by the LLVM project under our new license, so you will see that the license headers include that license only. Some of our contributors have contributed code under our old license, and accordingly, we have retained a copy of our old license notice in the top-level files in each project and repository. llvm-svn: 351636
187 lines
6.4 KiB
C++
187 lines
6.4 KiB
C++
//===- lib/Tooling/AllTUsExecution.cpp - Execute actions on all TUs. ------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Tooling/AllTUsExecution.h"
|
|
#include "clang/Tooling/ToolExecutorPluginRegistry.h"
|
|
#include "llvm/Support/ThreadPool.h"
|
|
|
|
namespace clang {
|
|
namespace tooling {
|
|
|
|
const char *AllTUsToolExecutor::ExecutorName = "AllTUsToolExecutor";
|
|
|
|
namespace {
|
|
llvm::Error make_string_error(const llvm::Twine &Message) {
|
|
return llvm::make_error<llvm::StringError>(Message,
|
|
llvm::inconvertibleErrorCode());
|
|
}
|
|
|
|
ArgumentsAdjuster getDefaultArgumentsAdjusters() {
|
|
return combineAdjusters(
|
|
getClangStripOutputAdjuster(),
|
|
combineAdjusters(getClangSyntaxOnlyAdjuster(),
|
|
getClangStripDependencyFileAdjuster()));
|
|
}
|
|
|
|
class ThreadSafeToolResults : public ToolResults {
|
|
public:
|
|
void addResult(StringRef Key, StringRef Value) override {
|
|
std::unique_lock<std::mutex> LockGuard(Mutex);
|
|
Results.addResult(Key, Value);
|
|
}
|
|
|
|
std::vector<std::pair<llvm::StringRef, llvm::StringRef>>
|
|
AllKVResults() override {
|
|
return Results.AllKVResults();
|
|
}
|
|
|
|
void forEachResult(llvm::function_ref<void(StringRef Key, StringRef Value)>
|
|
Callback) override {
|
|
Results.forEachResult(Callback);
|
|
}
|
|
|
|
private:
|
|
InMemoryToolResults Results;
|
|
std::mutex Mutex;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
llvm::cl::opt<std::string>
|
|
Filter("filter",
|
|
llvm::cl::desc("Only process files that match this filter. "
|
|
"This flag only applies to all-TUs."),
|
|
llvm::cl::init(".*"));
|
|
|
|
AllTUsToolExecutor::AllTUsToolExecutor(
|
|
const CompilationDatabase &Compilations, unsigned ThreadCount,
|
|
std::shared_ptr<PCHContainerOperations> PCHContainerOps)
|
|
: Compilations(Compilations), Results(new ThreadSafeToolResults),
|
|
Context(Results.get()), ThreadCount(ThreadCount) {}
|
|
|
|
AllTUsToolExecutor::AllTUsToolExecutor(
|
|
CommonOptionsParser Options, unsigned ThreadCount,
|
|
std::shared_ptr<PCHContainerOperations> PCHContainerOps)
|
|
: OptionsParser(std::move(Options)),
|
|
Compilations(OptionsParser->getCompilations()),
|
|
Results(new ThreadSafeToolResults), Context(Results.get()),
|
|
ThreadCount(ThreadCount) {}
|
|
|
|
llvm::Error AllTUsToolExecutor::execute(
|
|
llvm::ArrayRef<
|
|
std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>>
|
|
Actions) {
|
|
if (Actions.empty())
|
|
return make_string_error("No action to execute.");
|
|
|
|
if (Actions.size() != 1)
|
|
return make_string_error(
|
|
"Only support executing exactly 1 action at this point.");
|
|
|
|
std::string ErrorMsg;
|
|
std::mutex TUMutex;
|
|
auto AppendError = [&](llvm::Twine Err) {
|
|
std::unique_lock<std::mutex> LockGuard(TUMutex);
|
|
ErrorMsg += Err.str();
|
|
};
|
|
|
|
auto Log = [&](llvm::Twine Msg) {
|
|
std::unique_lock<std::mutex> LockGuard(TUMutex);
|
|
llvm::errs() << Msg.str() << "\n";
|
|
};
|
|
|
|
std::vector<std::string> Files;
|
|
llvm::Regex RegexFilter(Filter);
|
|
for (const auto& File : Compilations.getAllFiles()) {
|
|
if (RegexFilter.match(File))
|
|
Files.push_back(File);
|
|
}
|
|
// Add a counter to track the progress.
|
|
const std::string TotalNumStr = std::to_string(Files.size());
|
|
unsigned Counter = 0;
|
|
auto Count = [&]() {
|
|
std::unique_lock<std::mutex> LockGuard(TUMutex);
|
|
return ++Counter;
|
|
};
|
|
|
|
auto &Action = Actions.front();
|
|
|
|
{
|
|
llvm::ThreadPool Pool(ThreadCount == 0 ? llvm::hardware_concurrency()
|
|
: ThreadCount);
|
|
llvm::SmallString<128> InitialWorkingDir;
|
|
if (auto EC = llvm::sys::fs::current_path(InitialWorkingDir)) {
|
|
InitialWorkingDir = "";
|
|
llvm::errs() << "Error while getting current working directory: "
|
|
<< EC.message() << "\n";
|
|
}
|
|
for (std::string File : Files) {
|
|
Pool.async(
|
|
[&](std::string Path) {
|
|
Log("[" + std::to_string(Count()) + "/" + TotalNumStr +
|
|
"] Processing file " + Path);
|
|
ClangTool Tool(Compilations, {Path});
|
|
Tool.appendArgumentsAdjuster(Action.second);
|
|
Tool.appendArgumentsAdjuster(getDefaultArgumentsAdjusters());
|
|
for (const auto &FileAndContent : OverlayFiles)
|
|
Tool.mapVirtualFile(FileAndContent.first(),
|
|
FileAndContent.second);
|
|
// Do not restore working dir from multiple threads to avoid races.
|
|
Tool.setRestoreWorkingDir(false);
|
|
if (Tool.run(Action.first.get()))
|
|
AppendError(llvm::Twine("Failed to run action on ") + Path +
|
|
"\n");
|
|
},
|
|
File);
|
|
}
|
|
// Make sure all tasks have finished before resetting the working directory.
|
|
Pool.wait();
|
|
if (!InitialWorkingDir.empty()) {
|
|
if (auto EC = llvm::sys::fs::set_current_path(InitialWorkingDir))
|
|
llvm::errs() << "Error while restoring working directory: "
|
|
<< EC.message() << "\n";
|
|
}
|
|
}
|
|
|
|
if (!ErrorMsg.empty())
|
|
return make_string_error(ErrorMsg);
|
|
|
|
return llvm::Error::success();
|
|
}
|
|
|
|
static llvm::cl::opt<unsigned> ExecutorConcurrency(
|
|
"execute-concurrency",
|
|
llvm::cl::desc("The number of threads used to process all files in "
|
|
"parallel. Set to 0 for hardware concurrency. "
|
|
"This flag only applies to all-TUs."),
|
|
llvm::cl::init(0));
|
|
|
|
class AllTUsToolExecutorPlugin : public ToolExecutorPlugin {
|
|
public:
|
|
llvm::Expected<std::unique_ptr<ToolExecutor>>
|
|
create(CommonOptionsParser &OptionsParser) override {
|
|
if (OptionsParser.getSourcePathList().empty())
|
|
return make_string_error(
|
|
"[AllTUsToolExecutorPlugin] Please provide a directory/file path in "
|
|
"the compilation database.");
|
|
return llvm::make_unique<AllTUsToolExecutor>(std::move(OptionsParser),
|
|
ExecutorConcurrency);
|
|
}
|
|
};
|
|
|
|
static ToolExecutorPluginRegistry::Add<AllTUsToolExecutorPlugin>
|
|
X("all-TUs", "Runs FrontendActions on all TUs in the compilation database. "
|
|
"Tool results are stored in memory.");
|
|
|
|
// This anchor is used to force the linker to link in the generated object file
|
|
// and thus register the plugin.
|
|
volatile int AllTUsToolExecutorAnchorSource = 0;
|
|
|
|
} // end namespace tooling
|
|
} // end namespace clang
|