mirror of https://github.com/microsoft/clang.git
[Tooling] A new framework for executing clang frontend actions.
Summary: This defines a `clang::tooling::ToolExecutor` interface that can be extended to support different execution plans including standalone execution on a given set of TUs or parallel execution on all TUs in a codebase. In order to enable multiprocessing execution, tool actions are expected to output result into a `ToolResults` interface provided by executors. The `ToolResults` interface abstracts how results are stored e.g. in-memory for standalone executions or on-disk for large-scale execution. New executors can be registered as `ToolExecutorPlugin`s via the `ToolExecutorPluginRegistry`. CLI tools can use `createExecutorFromCommandLineArgs` to create a specific registered executor according to the command-line arguments. This patch also implements `StandaloneToolExecutor` which has the same behavior as the current `ClangTool` interface, i.e. execute frontend actions on a given set of TUs. At this point, it's simply a wrapper around `ClangTool` at this point. This is still experimental but expected to replace the existing `ClangTool` interface so that specific tools would not need to worry about execution. Reviewers: klimek, arphaman, hokein, sammccall Reviewed By: klimek Subscribers: cfe-commits, djasper, mgorny, omtcyfz Differential Revision: https://reviews.llvm.org/D34272 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@316653 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
9bc9ee5d23
commit
e7a10639e3
|
@ -109,6 +109,10 @@ public:
|
||||||
return SourcePathList;
|
return SourcePathList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the argument adjuster calculated from "--extra-arg" and
|
||||||
|
//"--extra-arg-before" options.
|
||||||
|
ArgumentsAdjuster getArgumentsAdjuster() { return Adjuster; }
|
||||||
|
|
||||||
static const char *const HelpMessage;
|
static const char *const HelpMessage;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -121,6 +125,7 @@ private:
|
||||||
|
|
||||||
std::unique_ptr<CompilationDatabase> Compilations;
|
std::unique_ptr<CompilationDatabase> Compilations;
|
||||||
std::vector<std::string> SourcePathList;
|
std::vector<std::string> SourcePathList;
|
||||||
|
ArgumentsAdjuster Adjuster;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ArgumentsAdjustingCompilations : public CompilationDatabase {
|
class ArgumentsAdjustingCompilations : public CompilationDatabase {
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
//===--- Execution.h - Executing clang frontend actions -*- C++ ---------*-===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This file defines framework for executing clang frontend actions.
|
||||||
|
//
|
||||||
|
// The framework can be extended to support different execution plans including
|
||||||
|
// standalone execution on the given TUs or parallel execution on all TUs in
|
||||||
|
// the codebase.
|
||||||
|
//
|
||||||
|
// In order to enable multiprocessing execution, tool actions are expected to
|
||||||
|
// output result into the ToolResults provided by the executor. The
|
||||||
|
// `ToolResults` is an interface that abstracts how results are stored e.g.
|
||||||
|
// in-memory for standalone execution or on-disk for large-scale execution.
|
||||||
|
//
|
||||||
|
// New executors can be registered as ToolExecutorPlugins via the
|
||||||
|
// `ToolExecutorPluginRegistry`. CLI tools can use
|
||||||
|
// `createExecutorFromCommandLineArgs` to create a specific registered executor
|
||||||
|
// according to the command-line arguments.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_CLANG_TOOLING_EXECUTION_H
|
||||||
|
#define LLVM_CLANG_TOOLING_EXECUTION_H
|
||||||
|
|
||||||
|
#include "clang/Tooling/CommonOptionsParser.h"
|
||||||
|
#include "clang/Tooling/Tooling.h"
|
||||||
|
#include "llvm/Support/Error.h"
|
||||||
|
#include "llvm/Support/Registry.h"
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
namespace tooling {
|
||||||
|
|
||||||
|
/// \brief An abstraction for the result of a tool execution. For example, the
|
||||||
|
/// underlying result can be in-memory or on-disk.
|
||||||
|
///
|
||||||
|
/// Results should be string key-value pairs. For example, a refactoring tool
|
||||||
|
/// can use source location as key and a replacement in YAML format as value.
|
||||||
|
class ToolResults {
|
||||||
|
public:
|
||||||
|
virtual ~ToolResults() = default;
|
||||||
|
virtual void addResult(StringRef Key, StringRef Value) = 0;
|
||||||
|
virtual std::vector<std::pair<std::string, std::string>> AllKVResults() = 0;
|
||||||
|
virtual void forEachResult(
|
||||||
|
llvm::function_ref<void(StringRef Key, StringRef Value)> Callback) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class InMemoryToolResults : public ToolResults {
|
||||||
|
public:
|
||||||
|
void addResult(StringRef Key, StringRef Value) override;
|
||||||
|
std::vector<std::pair<std::string, std::string>> AllKVResults() override;
|
||||||
|
void forEachResult(llvm::function_ref<void(StringRef Key, StringRef Value)>
|
||||||
|
Callback) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::pair<std::string, std::string>> KVResults;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief The context of an execution, including the information about
|
||||||
|
/// compilation and results.
|
||||||
|
class ExecutionContext {
|
||||||
|
public:
|
||||||
|
virtual ~ExecutionContext() {}
|
||||||
|
|
||||||
|
/// \brief Initializes a context. This does not take ownership of `Results`.
|
||||||
|
explicit ExecutionContext(ToolResults *Results) : Results(Results) {}
|
||||||
|
|
||||||
|
/// \brief Adds a KV pair to the result container of this execution.
|
||||||
|
void reportResult(StringRef Key, StringRef Value);
|
||||||
|
|
||||||
|
// Returns the source control system's revision number if applicable.
|
||||||
|
// Otherwise returns an empty string.
|
||||||
|
virtual std::string getRevision() { return ""; }
|
||||||
|
|
||||||
|
// Returns the corpus being analyzed, e.g. "llvm" for the LLVM codebase, if
|
||||||
|
// applicable.
|
||||||
|
virtual std::string getCorpus() { return ""; }
|
||||||
|
|
||||||
|
// Returns the currently processed compilation unit if available.
|
||||||
|
virtual std::string getCurrentCompilationUnit() { return ""; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ToolResults *Results;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Interface for executing clang frontend actions.
|
||||||
|
///
|
||||||
|
/// This can be extended to support running tool actions in different
|
||||||
|
/// execution mode, e.g. on a specific set of TUs or many TUs in parallel.
|
||||||
|
///
|
||||||
|
/// New executors can be registered as ToolExecutorPlugins via the
|
||||||
|
/// `ToolExecutorPluginRegistry`. CLI tools can use
|
||||||
|
/// `createExecutorFromCommandLineArgs` to create a specific registered
|
||||||
|
/// executor according to the command-line arguments.
|
||||||
|
class ToolExecutor {
|
||||||
|
public:
|
||||||
|
virtual ~ToolExecutor() {}
|
||||||
|
|
||||||
|
/// \brief Returns the name of a specific executor.
|
||||||
|
virtual StringRef getExecutorName() const = 0;
|
||||||
|
|
||||||
|
/// \brief Executes each action with a corresponding arguments adjuster.
|
||||||
|
virtual llvm::Error
|
||||||
|
execute(llvm::ArrayRef<
|
||||||
|
std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>>
|
||||||
|
Actions) = 0;
|
||||||
|
|
||||||
|
/// \brief Convenient functions for the above `execute`.
|
||||||
|
llvm::Error execute(std::unique_ptr<FrontendActionFactory> Action);
|
||||||
|
/// Executes an action with an argument adjuster.
|
||||||
|
llvm::Error execute(std::unique_ptr<FrontendActionFactory> Action,
|
||||||
|
ArgumentsAdjuster Adjuster);
|
||||||
|
|
||||||
|
/// \brief Returns a reference to the execution context.
|
||||||
|
///
|
||||||
|
/// This should be passed to tool callbacks, and tool callbacks should report
|
||||||
|
/// results via the returned context.
|
||||||
|
virtual ExecutionContext *getExecutionContext() = 0;
|
||||||
|
|
||||||
|
/// \brief Returns a reference to the result container.
|
||||||
|
///
|
||||||
|
/// NOTE: This should only be used after the execution finishes. Tool
|
||||||
|
/// callbacks should report results via `ExecutionContext` instead.
|
||||||
|
virtual ToolResults *getToolResults() = 0;
|
||||||
|
|
||||||
|
/// \brief Map a virtual file to be used while running the tool.
|
||||||
|
///
|
||||||
|
/// \param FilePath The path at which the content will be mapped.
|
||||||
|
/// \param Content A buffer of the file's content.
|
||||||
|
virtual void mapVirtualFile(StringRef FilePath, StringRef Content) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Interface for factories that create specific executors. This is also
|
||||||
|
/// used as a plugin to be registered into ToolExecutorPluginRegistry.
|
||||||
|
class ToolExecutorPlugin {
|
||||||
|
public:
|
||||||
|
virtual ~ToolExecutorPlugin() {}
|
||||||
|
|
||||||
|
/// \brief Create an `ToolExecutor`.
|
||||||
|
///
|
||||||
|
/// `OptionsParser` can be consumed (e.g. moved) if the creation succeeds.
|
||||||
|
virtual llvm::Expected<std::unique_ptr<ToolExecutor>>
|
||||||
|
create(CommonOptionsParser &OptionsParser) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief This creates a ToolExecutor that is in the global registry based on
|
||||||
|
/// commandline arguments.
|
||||||
|
///
|
||||||
|
/// This picks the right executor based on the `--executor` option. This parses
|
||||||
|
/// the commandline arguments with `CommonOptionsParser`, so caller does not
|
||||||
|
/// need to parse again.
|
||||||
|
///
|
||||||
|
/// By default, this creates a `StandaloneToolExecutor` ("standalone") if
|
||||||
|
/// `--executor` is not provided.
|
||||||
|
llvm::Expected<std::unique_ptr<ToolExecutor>>
|
||||||
|
createExecutorFromCommandLineArgs(int &argc, const char **argv,
|
||||||
|
llvm::cl::OptionCategory &Category,
|
||||||
|
const char *Overview = nullptr);
|
||||||
|
|
||||||
|
} // end namespace tooling
|
||||||
|
} // end namespace clang
|
||||||
|
|
||||||
|
#endif // LLVM_CLANG_TOOLING_EXECUTION_H
|
|
@ -0,0 +1,97 @@
|
||||||
|
//===--- StandaloneExecution.h - Standalone execution. -*- C++ ----------*-===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This file defines standalone execution of clang tools.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_CLANG_TOOLING_STANDALONEEXECUTION_H
|
||||||
|
#define LLVM_CLANG_TOOLING_STANDALONEEXECUTION_H
|
||||||
|
|
||||||
|
#include "clang/Tooling/ArgumentsAdjusters.h"
|
||||||
|
#include "clang/Tooling/Execution.h"
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
namespace tooling {
|
||||||
|
|
||||||
|
/// \brief A standalone executor that runs FrontendActions on a given set of
|
||||||
|
/// TUs in sequence.
|
||||||
|
///
|
||||||
|
/// By default, this executor uses the following arguments adjusters (as defined
|
||||||
|
/// in `clang/Tooling/ArgumentsAdjusters.h`):
|
||||||
|
/// - `getClangStripOutputAdjuster()`
|
||||||
|
/// - `getClangSyntaxOnlyAdjuster()`
|
||||||
|
/// - `getClangStripDependencyFileAdjuster()`
|
||||||
|
class StandaloneToolExecutor : public ToolExecutor {
|
||||||
|
public:
|
||||||
|
static const char *ExecutorName;
|
||||||
|
|
||||||
|
/// \brief Init with \p CompilationDatabase and the paths of all files to be
|
||||||
|
/// proccessed.
|
||||||
|
StandaloneToolExecutor(
|
||||||
|
const CompilationDatabase &Compilations,
|
||||||
|
llvm::ArrayRef<std::string> SourcePaths,
|
||||||
|
std::shared_ptr<PCHContainerOperations> PCHContainerOps =
|
||||||
|
std::make_shared<PCHContainerOperations>());
|
||||||
|
|
||||||
|
/// \brief Init with \p CommonOptionsParser. This is expected to be used by
|
||||||
|
/// `createExecutorFromCommandLineArgs` based on commandline options.
|
||||||
|
///
|
||||||
|
/// The executor takes ownership of \p Options.
|
||||||
|
StandaloneToolExecutor(
|
||||||
|
CommonOptionsParser Options,
|
||||||
|
std::shared_ptr<PCHContainerOperations> PCHContainerOps =
|
||||||
|
std::make_shared<PCHContainerOperations>());
|
||||||
|
|
||||||
|
StringRef getExecutorName() const override { return ExecutorName; }
|
||||||
|
|
||||||
|
using ToolExecutor::execute;
|
||||||
|
|
||||||
|
llvm::Error
|
||||||
|
execute(llvm::ArrayRef<
|
||||||
|
std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>>
|
||||||
|
Actions) override;
|
||||||
|
|
||||||
|
/// \brief Set a \c DiagnosticConsumer to use during parsing.
|
||||||
|
void setDiagnosticConsumer(DiagnosticConsumer *DiagConsumer) {
|
||||||
|
Tool.setDiagnosticConsumer(DiagConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecutionContext *getExecutionContext() override { return &Context; };
|
||||||
|
|
||||||
|
ToolResults *getToolResults() override { return &Results; }
|
||||||
|
|
||||||
|
llvm::ArrayRef<std::string> getSourcePaths() const {
|
||||||
|
return Tool.getSourcePaths();
|
||||||
|
}
|
||||||
|
|
||||||
|
void mapVirtualFile(StringRef FilePath, StringRef Content) override {
|
||||||
|
Tool.mapVirtualFile(FilePath, Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Returns the file manager used in the tool.
|
||||||
|
///
|
||||||
|
/// The file manager is shared between all translation units.
|
||||||
|
FileManager &getFiles() { return Tool.getFiles(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Used to store the parser when the executor is initialized with parser.
|
||||||
|
llvm::Optional<CommonOptionsParser> OptionsParser;
|
||||||
|
// FIXME: The standalone executor is currently just a wrapper of `ClangTool`.
|
||||||
|
// Merge `ClangTool` implementation into the this.
|
||||||
|
ClangTool Tool;
|
||||||
|
ExecutionContext Context;
|
||||||
|
InMemoryToolResults Results;
|
||||||
|
ArgumentsAdjuster ArgsAdjuster;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end namespace tooling
|
||||||
|
} // end namespace clang
|
||||||
|
|
||||||
|
#endif // LLVM_CLANG_TOOLING_STANDALONEEXECUTION_H
|
|
@ -0,0 +1,24 @@
|
||||||
|
//===--- ToolExecutorPluginRegistry.h - -------------------------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_CLANG_TOOLING_TOOLEXECUTORPLUGINREGISTRY_H
|
||||||
|
#define LLVM_CLANG_TOOLING_TOOLEXECUTORPLUGINREGISTRY_H
|
||||||
|
|
||||||
|
#include "clang/Tooling/Execution.h"
|
||||||
|
#include "llvm/Support/Registry.h"
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
namespace tooling {
|
||||||
|
|
||||||
|
typedef llvm::Registry<ToolExecutorPlugin> ToolExecutorPluginRegistry;
|
||||||
|
|
||||||
|
} // end namespace tooling
|
||||||
|
} // end namespace clang
|
||||||
|
|
||||||
|
#endif // LLVM_CLANG_TOOLING_TOOLEXECUTORPLUGINREGISTRY_H
|
|
@ -31,12 +31,12 @@
|
||||||
#define LLVM_CLANG_TOOLING_TOOLING_H
|
#define LLVM_CLANG_TOOLING_TOOLING_H
|
||||||
|
|
||||||
#include "clang/AST/ASTConsumer.h"
|
#include "clang/AST/ASTConsumer.h"
|
||||||
#include "clang/Frontend/PCHContainerOperations.h"
|
|
||||||
#include "clang/Basic/Diagnostic.h"
|
#include "clang/Basic/Diagnostic.h"
|
||||||
#include "clang/Basic/FileManager.h"
|
#include "clang/Basic/FileManager.h"
|
||||||
#include "clang/Basic/LLVM.h"
|
#include "clang/Basic/LLVM.h"
|
||||||
#include "clang/Driver/Util.h"
|
#include "clang/Driver/Util.h"
|
||||||
#include "clang/Frontend/FrontendAction.h"
|
#include "clang/Frontend/FrontendAction.h"
|
||||||
|
#include "clang/Frontend/PCHContainerOperations.h"
|
||||||
#include "clang/Lex/ModuleLoader.h"
|
#include "clang/Lex/ModuleLoader.h"
|
||||||
#include "clang/Tooling/ArgumentsAdjusters.h"
|
#include "clang/Tooling/ArgumentsAdjusters.h"
|
||||||
#include "clang/Tooling/CompilationDatabase.h"
|
#include "clang/Tooling/CompilationDatabase.h"
|
||||||
|
@ -337,7 +337,9 @@ class ClangTool {
|
||||||
/// The file manager is shared between all translation units.
|
/// The file manager is shared between all translation units.
|
||||||
FileManager &getFiles() { return *Files; }
|
FileManager &getFiles() { return *Files; }
|
||||||
|
|
||||||
private:
|
llvm::ArrayRef<std::string> getSourcePaths() const { return SourcePaths; }
|
||||||
|
|
||||||
|
private:
|
||||||
const CompilationDatabase &Compilations;
|
const CompilationDatabase &Compilations;
|
||||||
std::vector<std::string> SourcePaths;
|
std::vector<std::string> SourcePaths;
|
||||||
std::shared_ptr<PCHContainerOperations> PCHContainerOps;
|
std::shared_ptr<PCHContainerOperations> PCHContainerOps;
|
||||||
|
|
|
@ -96,6 +96,10 @@ ArgumentsAdjuster getInsertArgumentAdjuster(const char *Extra,
|
||||||
|
|
||||||
ArgumentsAdjuster combineAdjusters(ArgumentsAdjuster First,
|
ArgumentsAdjuster combineAdjusters(ArgumentsAdjuster First,
|
||||||
ArgumentsAdjuster Second) {
|
ArgumentsAdjuster Second) {
|
||||||
|
if (!First)
|
||||||
|
return Second;
|
||||||
|
if (!Second)
|
||||||
|
return First;
|
||||||
return [First, Second](const CommandLineArguments &Args, StringRef File) {
|
return [First, Second](const CommandLineArguments &Args, StringRef File) {
|
||||||
return Second(First(Args, File), File);
|
return Second(First(Args, File), File);
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,11 +11,13 @@ add_clang_library(clangTooling
|
||||||
ArgumentsAdjusters.cpp
|
ArgumentsAdjusters.cpp
|
||||||
CommonOptionsParser.cpp
|
CommonOptionsParser.cpp
|
||||||
CompilationDatabase.cpp
|
CompilationDatabase.cpp
|
||||||
|
Execution.cpp
|
||||||
FileMatchTrie.cpp
|
FileMatchTrie.cpp
|
||||||
FixIt.cpp
|
FixIt.cpp
|
||||||
JSONCompilationDatabase.cpp
|
JSONCompilationDatabase.cpp
|
||||||
Refactoring.cpp
|
Refactoring.cpp
|
||||||
RefactoringCallbacks.cpp
|
RefactoringCallbacks.cpp
|
||||||
|
StandaloneExecution.cpp
|
||||||
Tooling.cpp
|
Tooling.cpp
|
||||||
|
|
||||||
DEPENDS
|
DEPENDS
|
||||||
|
|
|
@ -147,10 +147,12 @@ llvm::Error CommonOptionsParser::init(
|
||||||
auto AdjustingCompilations =
|
auto AdjustingCompilations =
|
||||||
llvm::make_unique<ArgumentsAdjustingCompilations>(
|
llvm::make_unique<ArgumentsAdjustingCompilations>(
|
||||||
std::move(Compilations));
|
std::move(Compilations));
|
||||||
AdjustingCompilations->appendArgumentsAdjuster(
|
Adjuster =
|
||||||
getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN));
|
getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN);
|
||||||
AdjustingCompilations->appendArgumentsAdjuster(
|
Adjuster = combineAdjusters(
|
||||||
|
std::move(Adjuster),
|
||||||
getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END));
|
getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END));
|
||||||
|
AdjustingCompilations->appendArgumentsAdjuster(Adjuster);
|
||||||
Compilations = std::move(AdjustingCompilations);
|
Compilations = std::move(AdjustingCompilations);
|
||||||
return llvm::Error::success();
|
return llvm::Error::success();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
//===- lib/Tooling/Execution.cpp - Implements tool execution framework. ---===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "clang/Tooling/Execution.h"
|
||||||
|
#include "clang/Tooling/ToolExecutorPluginRegistry.h"
|
||||||
|
#include "clang/Tooling/Tooling.h"
|
||||||
|
|
||||||
|
LLVM_INSTANTIATE_REGISTRY(clang::tooling::ToolExecutorPluginRegistry)
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
namespace tooling {
|
||||||
|
|
||||||
|
static llvm::cl::opt<std::string>
|
||||||
|
ExecutorName("executor", llvm::cl::desc("The name of the executor to use."),
|
||||||
|
llvm::cl::init("standalone"));
|
||||||
|
|
||||||
|
void InMemoryToolResults::addResult(StringRef Key, StringRef Value) {
|
||||||
|
KVResults.push_back({Key.str(), Value.str()});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, std::string>>
|
||||||
|
InMemoryToolResults::AllKVResults() {
|
||||||
|
return KVResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InMemoryToolResults::forEachResult(
|
||||||
|
llvm::function_ref<void(StringRef Key, StringRef Value)> Callback) {
|
||||||
|
for (const auto &KV : KVResults) {
|
||||||
|
Callback(KV.first, KV.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExecutionContext::reportResult(StringRef Key, StringRef Value) {
|
||||||
|
Results->addResult(Key, Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Error
|
||||||
|
ToolExecutor::execute(std::unique_ptr<FrontendActionFactory> Action) {
|
||||||
|
return execute(std::move(Action), ArgumentsAdjuster());
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Error ToolExecutor::execute(std::unique_ptr<FrontendActionFactory> Action,
|
||||||
|
ArgumentsAdjuster Adjuster) {
|
||||||
|
std::vector<
|
||||||
|
std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>>
|
||||||
|
Actions;
|
||||||
|
Actions.emplace_back(std::move(Action), std::move(Adjuster));
|
||||||
|
return execute(Actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Expected<std::unique_ptr<ToolExecutor>>
|
||||||
|
createExecutorFromCommandLineArgs(int &argc, const char **argv,
|
||||||
|
llvm::cl::OptionCategory &Category,
|
||||||
|
const char *Overview) {
|
||||||
|
auto OptionsParser =
|
||||||
|
CommonOptionsParser::create(argc, argv, Category, llvm::cl::ZeroOrMore,
|
||||||
|
/*Overview=*/nullptr);
|
||||||
|
if (!OptionsParser)
|
||||||
|
return OptionsParser.takeError();
|
||||||
|
for (auto I = ToolExecutorPluginRegistry::begin(),
|
||||||
|
E = ToolExecutorPluginRegistry::end();
|
||||||
|
I != E; ++I) {
|
||||||
|
if (I->getName() != ExecutorName) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::unique_ptr<ToolExecutorPlugin> Plugin(I->instantiate());
|
||||||
|
llvm::Expected<std::unique_ptr<ToolExecutor>> Executor =
|
||||||
|
Plugin->create(*OptionsParser);
|
||||||
|
if (!Executor) {
|
||||||
|
return llvm::make_error<llvm::StringError>(
|
||||||
|
llvm::Twine("Failed to create '") + I->getName() +
|
||||||
|
"': " + llvm::toString(Executor.takeError()) + "\n",
|
||||||
|
llvm::inconvertibleErrorCode());
|
||||||
|
}
|
||||||
|
return std::move(*Executor);
|
||||||
|
}
|
||||||
|
return llvm::make_error<llvm::StringError>(
|
||||||
|
llvm::Twine("Executor \"") + ExecutorName + "\" is not registered.",
|
||||||
|
llvm::inconvertibleErrorCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace tooling
|
||||||
|
} // end namespace clang
|
|
@ -0,0 +1,91 @@
|
||||||
|
//===- lib/Tooling/Execution.cpp - Standalone clang action execution. -----===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "clang/Tooling/StandaloneExecution.h"
|
||||||
|
#include "clang/Tooling/ToolExecutorPluginRegistry.h"
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
namespace tooling {
|
||||||
|
|
||||||
|
static llvm::Error make_string_error(const llvm::Twine &Message) {
|
||||||
|
return llvm::make_error<llvm::StringError>(Message,
|
||||||
|
llvm::inconvertibleErrorCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *StandaloneToolExecutor::ExecutorName = "StandaloneToolExecutor";
|
||||||
|
|
||||||
|
static ArgumentsAdjuster getDefaultArgumentsAdjusters() {
|
||||||
|
return combineAdjusters(
|
||||||
|
getClangStripOutputAdjuster(),
|
||||||
|
combineAdjusters(getClangSyntaxOnlyAdjuster(),
|
||||||
|
getClangStripDependencyFileAdjuster()));
|
||||||
|
}
|
||||||
|
|
||||||
|
StandaloneToolExecutor::StandaloneToolExecutor(
|
||||||
|
const CompilationDatabase &Compilations,
|
||||||
|
llvm::ArrayRef<std::string> SourcePaths,
|
||||||
|
std::shared_ptr<PCHContainerOperations> PCHContainerOps)
|
||||||
|
: Tool(Compilations, SourcePaths), Context(&Results),
|
||||||
|
ArgsAdjuster(getDefaultArgumentsAdjusters()) {
|
||||||
|
// Use self-defined default argument adjusters instead of the default
|
||||||
|
// adjusters that come with the old `ClangTool`.
|
||||||
|
Tool.clearArgumentsAdjusters();
|
||||||
|
}
|
||||||
|
|
||||||
|
StandaloneToolExecutor::StandaloneToolExecutor(
|
||||||
|
CommonOptionsParser Options,
|
||||||
|
std::shared_ptr<PCHContainerOperations> PCHContainerOps)
|
||||||
|
: OptionsParser(std::move(Options)),
|
||||||
|
Tool(OptionsParser->getCompilations(), OptionsParser->getSourcePathList(),
|
||||||
|
PCHContainerOps),
|
||||||
|
Context(&Results), ArgsAdjuster(getDefaultArgumentsAdjusters()) {
|
||||||
|
Tool.clearArgumentsAdjusters();
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Error StandaloneToolExecutor::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.");
|
||||||
|
|
||||||
|
auto &Action = Actions.front();
|
||||||
|
Tool.appendArgumentsAdjuster(Action.second);
|
||||||
|
Tool.appendArgumentsAdjuster(ArgsAdjuster);
|
||||||
|
if (int Ret = Tool.run(Action.first.get()))
|
||||||
|
return make_string_error("Failed to run action.");
|
||||||
|
|
||||||
|
return llvm::Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
class StandaloneToolExecutorPlugin : public ToolExecutorPlugin {
|
||||||
|
public:
|
||||||
|
llvm::Expected<std::unique_ptr<ToolExecutor>>
|
||||||
|
create(CommonOptionsParser &OptionsParser) override {
|
||||||
|
if (OptionsParser.getSourcePathList().empty())
|
||||||
|
return make_string_error(
|
||||||
|
"[StandaloneToolExecutorPlugin] No positional argument found.");
|
||||||
|
return llvm::make_unique<StandaloneToolExecutor>(std::move(OptionsParser));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// This anchor is used to force the linker to link in the generated object file
|
||||||
|
// and thus register the plugin.
|
||||||
|
volatile int ToolExecutorPluginAnchorSource = 0;
|
||||||
|
|
||||||
|
static ToolExecutorPluginRegistry::Add<StandaloneToolExecutorPlugin>
|
||||||
|
X("standalone", "Runs FrontendActions on a set of files provided "
|
||||||
|
"via positional arguments.");
|
||||||
|
|
||||||
|
} // end namespace tooling
|
||||||
|
} // end namespace clang
|
|
@ -29,6 +29,7 @@
|
||||||
#include "llvm/Config/llvm-config.h"
|
#include "llvm/Config/llvm-config.h"
|
||||||
#include "llvm/Option/ArgList.h"
|
#include "llvm/Option/ArgList.h"
|
||||||
#include "llvm/Option/Option.h"
|
#include "llvm/Option/Option.h"
|
||||||
|
#include "llvm/Support/CommandLine.h"
|
||||||
#include "llvm/Support/Debug.h"
|
#include "llvm/Support/Debug.h"
|
||||||
#include "llvm/Support/FileSystem.h"
|
#include "llvm/Support/FileSystem.h"
|
||||||
#include "llvm/Support/Host.h"
|
#include "llvm/Support/Host.h"
|
||||||
|
@ -347,11 +348,7 @@ void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster Adjuster) {
|
void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster Adjuster) {
|
||||||
if (ArgsAdjuster)
|
ArgsAdjuster = combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
|
||||||
ArgsAdjuster =
|
|
||||||
combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
|
|
||||||
else
|
|
||||||
ArgsAdjuster = std::move(Adjuster);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangTool::clearArgumentsAdjusters() {
|
void ClangTool::clearArgumentsAdjusters() {
|
||||||
|
|
|
@ -16,6 +16,7 @@ add_clang_unittest(ToolingTests
|
||||||
CommentHandlerTest.cpp
|
CommentHandlerTest.cpp
|
||||||
CompilationDatabaseTest.cpp
|
CompilationDatabaseTest.cpp
|
||||||
DiagnosticsYamlTest.cpp
|
DiagnosticsYamlTest.cpp
|
||||||
|
ExecutionTest.cpp
|
||||||
FixItTest.cpp
|
FixItTest.cpp
|
||||||
LexicallyOrderedRecursiveASTVisitorTest.cpp
|
LexicallyOrderedRecursiveASTVisitorTest.cpp
|
||||||
LookupTest.cpp
|
LookupTest.cpp
|
||||||
|
|
|
@ -0,0 +1,228 @@
|
||||||
|
//===- unittest/Tooling/ExecutionTest.cpp - Tool execution tests. --------===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "clang/AST/ASTConsumer.h"
|
||||||
|
#include "clang/AST/DeclCXX.h"
|
||||||
|
#include "clang/AST/RecursiveASTVisitor.h"
|
||||||
|
#include "clang/Frontend/ASTUnit.h"
|
||||||
|
#include "clang/Frontend/FrontendAction.h"
|
||||||
|
#include "clang/Frontend/FrontendActions.h"
|
||||||
|
#include "clang/Tooling/CompilationDatabase.h"
|
||||||
|
#include "clang/Tooling/Execution.h"
|
||||||
|
#include "clang/Tooling/StandaloneExecution.h"
|
||||||
|
#include "clang/Tooling/ToolExecutorPluginRegistry.h"
|
||||||
|
#include "clang/Tooling/Tooling.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
namespace tooling {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// This traverses the AST and outputs function name as key and "1" as value for
|
||||||
|
// each function declaration.
|
||||||
|
class ASTConsumerWithResult
|
||||||
|
: public ASTConsumer,
|
||||||
|
public RecursiveASTVisitor<ASTConsumerWithResult> {
|
||||||
|
public:
|
||||||
|
using ASTVisitor = RecursiveASTVisitor<ASTConsumerWithResult>;
|
||||||
|
|
||||||
|
explicit ASTConsumerWithResult(ExecutionContext *Context) : Context(Context) {
|
||||||
|
assert(Context != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleTranslationUnit(clang::ASTContext &Context) override {
|
||||||
|
TraverseDecl(Context.getTranslationUnitDecl());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TraverseFunctionDecl(clang::FunctionDecl *Decl) {
|
||||||
|
Context->reportResult(Decl->getNameAsString(), "1");
|
||||||
|
return ASTVisitor::TraverseFunctionDecl(Decl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ExecutionContext *const Context;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ReportResultAction : public ASTFrontendAction {
|
||||||
|
public:
|
||||||
|
explicit ReportResultAction(ExecutionContext *Context) : Context(Context) {
|
||||||
|
assert(Context != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::unique_ptr<clang::ASTConsumer>
|
||||||
|
CreateASTConsumer(clang::CompilerInstance &compiler,
|
||||||
|
StringRef /* dummy */) override {
|
||||||
|
std::unique_ptr<clang::ASTConsumer> ast_consumer{
|
||||||
|
new ASTConsumerWithResult(Context)};
|
||||||
|
return ast_consumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ExecutionContext *const Context;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ReportResultActionFactory : public FrontendActionFactory {
|
||||||
|
public:
|
||||||
|
ReportResultActionFactory(ExecutionContext *Context) : Context(Context) {}
|
||||||
|
FrontendAction *create() override { return new ReportResultAction(Context); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ExecutionContext *const Context;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class TestToolExecutor : public ToolExecutor {
|
||||||
|
public:
|
||||||
|
static const char *ExecutorName;
|
||||||
|
|
||||||
|
TestToolExecutor(CommonOptionsParser Options)
|
||||||
|
: OptionsParser(std::move(Options)) {}
|
||||||
|
|
||||||
|
StringRef getExecutorName() const override { return ExecutorName; }
|
||||||
|
|
||||||
|
llvm::Error
|
||||||
|
execute(llvm::ArrayRef<std::pair<std::unique_ptr<FrontendActionFactory>,
|
||||||
|
ArgumentsAdjuster>>) override {
|
||||||
|
return llvm::Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecutionContext *getExecutionContext() override { return nullptr; };
|
||||||
|
|
||||||
|
ToolResults *getToolResults() override { return nullptr; }
|
||||||
|
|
||||||
|
llvm::ArrayRef<std::string> getSourcePaths() const {
|
||||||
|
return OptionsParser.getSourcePathList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void mapVirtualFile(StringRef FilePath, StringRef Content) override {
|
||||||
|
VFS[FilePath] = Content;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CommonOptionsParser OptionsParser;
|
||||||
|
std::string SourcePaths;
|
||||||
|
std::map<std::string, std::string> VFS;
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *TestToolExecutor::ExecutorName = "test-executor";
|
||||||
|
|
||||||
|
class TestToolExecutorPlugin : public ToolExecutorPlugin {
|
||||||
|
public:
|
||||||
|
llvm::Expected<std::unique_ptr<ToolExecutor>>
|
||||||
|
create(CommonOptionsParser &OptionsParser) override {
|
||||||
|
return llvm::make_unique<TestToolExecutor>(std::move(OptionsParser));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// This anchor is used to force the linker to link in the generated object file
|
||||||
|
// and thus register the plugin.
|
||||||
|
extern volatile int ToolExecutorPluginAnchorSource;
|
||||||
|
|
||||||
|
static int LLVM_ATTRIBUTE_UNUSED TestToolExecutorPluginAnchorDest =
|
||||||
|
ToolExecutorPluginAnchorSource;
|
||||||
|
|
||||||
|
static ToolExecutorPluginRegistry::Add<TestToolExecutorPlugin>
|
||||||
|
X("test-executor", "Plugin for TestToolExecutor.");
|
||||||
|
|
||||||
|
llvm::cl::OptionCategory TestCategory("execution-test options");
|
||||||
|
|
||||||
|
TEST(CreateToolExecutorTest, FailedCreateExecutorUndefinedFlag) {
|
||||||
|
std::vector<const char *> argv = {"prog", "--fake_flag_no_no_no", "f"};
|
||||||
|
int argc = argv.size();
|
||||||
|
auto Executor =
|
||||||
|
createExecutorFromCommandLineArgs(argc, &argv[0], TestCategory);
|
||||||
|
ASSERT_FALSE((bool)Executor);
|
||||||
|
llvm::consumeError(Executor.takeError());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CreateToolExecutorTest, RegisterFlagsBeforeReset) {
|
||||||
|
llvm::cl::opt<std::string> BeforeReset(
|
||||||
|
"before_reset", llvm::cl::desc("Defined before reset."),
|
||||||
|
llvm::cl::init(""));
|
||||||
|
|
||||||
|
llvm::cl::ResetAllOptionOccurrences();
|
||||||
|
|
||||||
|
std::vector<const char *> argv = {"prog", "--before_reset=set", "f"};
|
||||||
|
int argc = argv.size();
|
||||||
|
auto Executor =
|
||||||
|
createExecutorFromCommandLineArgs(argc, &argv[0], TestCategory);
|
||||||
|
ASSERT_TRUE((bool)Executor);
|
||||||
|
EXPECT_EQ(BeforeReset, "set");
|
||||||
|
BeforeReset.removeArgument();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CreateToolExecutorTest, CreateStandaloneToolExecutor) {
|
||||||
|
std::vector<const char *> argv = {"prog", "standalone.cpp"};
|
||||||
|
int argc = argv.size();
|
||||||
|
auto Executor =
|
||||||
|
createExecutorFromCommandLineArgs(argc, &argv[0], TestCategory);
|
||||||
|
ASSERT_TRUE((bool)Executor);
|
||||||
|
EXPECT_EQ(Executor->get()->getExecutorName(),
|
||||||
|
StandaloneToolExecutor::ExecutorName);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CreateToolExecutorTest, CreateTestToolExecutor) {
|
||||||
|
std::vector<const char *> argv = {"prog", "test.cpp",
|
||||||
|
"--executor=test-executor"};
|
||||||
|
int argc = argv.size();
|
||||||
|
auto Executor =
|
||||||
|
createExecutorFromCommandLineArgs(argc, &argv[0], TestCategory);
|
||||||
|
ASSERT_TRUE((bool)Executor);
|
||||||
|
EXPECT_EQ(Executor->get()->getExecutorName(), TestToolExecutor::ExecutorName);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StandaloneToolTest, SynctaxOnlyActionOnSimpleCode) {
|
||||||
|
FixedCompilationDatabase Compilations("/", std::vector<std::string>());
|
||||||
|
StandaloneToolExecutor Executor(Compilations,
|
||||||
|
std::vector<std::string>(1, "/a.cc"));
|
||||||
|
Executor.mapVirtualFile("/a.cc", "int x = 0;");
|
||||||
|
|
||||||
|
auto Err = Executor.execute(newFrontendActionFactory<SyntaxOnlyAction>(),
|
||||||
|
getClangSyntaxOnlyAdjuster());
|
||||||
|
ASSERT_TRUE(!Err);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StandaloneToolTest, SimpleAction) {
|
||||||
|
FixedCompilationDatabase Compilations("/", std::vector<std::string>());
|
||||||
|
StandaloneToolExecutor Executor(Compilations,
|
||||||
|
std::vector<std::string>(1, "/a.cc"));
|
||||||
|
Executor.mapVirtualFile("/a.cc", "int x = 0;");
|
||||||
|
|
||||||
|
auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>(
|
||||||
|
new ReportResultActionFactory(Executor.getExecutionContext())));
|
||||||
|
ASSERT_TRUE(!Err);
|
||||||
|
auto KVs = Executor.getToolResults()->AllKVResults();
|
||||||
|
ASSERT_EQ(KVs.size(), 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StandaloneToolTest, SimpleActionWithResult) {
|
||||||
|
FixedCompilationDatabase Compilations("/", std::vector<std::string>());
|
||||||
|
StandaloneToolExecutor Executor(Compilations,
|
||||||
|
std::vector<std::string>(1, "/a.cc"));
|
||||||
|
Executor.mapVirtualFile("/a.cc", "int x = 0; void f() {}");
|
||||||
|
|
||||||
|
auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>(
|
||||||
|
new ReportResultActionFactory(Executor.getExecutionContext())));
|
||||||
|
ASSERT_TRUE(!Err);
|
||||||
|
auto KVs = Executor.getToolResults()->AllKVResults();
|
||||||
|
ASSERT_EQ(KVs.size(), 1u);
|
||||||
|
EXPECT_EQ("f", KVs[0].first);
|
||||||
|
EXPECT_EQ("1", KVs[0].second);
|
||||||
|
|
||||||
|
Executor.getToolResults()->forEachResult(
|
||||||
|
[](StringRef, StringRef Value) { EXPECT_EQ("1", Value); });
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace tooling
|
||||||
|
} // end namespace clang
|
Loading…
Reference in New Issue