cc1: Factor out CompilerInstance::ExecuteAction which has the majority of the

clang -cc1 logic for running an action against a set of options.
 - This should make it easier to build tools that have a clang -cc1 like
   interface, but aren't actually part of clang -cc1.

llvm-svn: 93282
This commit is contained in:
Daniel Dunbar 2010-01-13 00:48:06 +00:00
parent 7d38d4da61
commit 4f2bc55d4e
3 changed files with 137 additions and 87 deletions

View File

@ -32,6 +32,7 @@ class Diagnostic;
class DiagnosticClient;
class ExternalASTSource;
class FileManager;
class FrontendAction;
class Preprocessor;
class Source;
class SourceManager;
@ -103,6 +104,42 @@ public:
bool _OwnsLLVMContext = true);
~CompilerInstance();
/// @name High-Level Operations
/// {
/// ExecuteAction - Execute the provided action against the compiler's
/// CompilerInvocation object.
///
/// This function makes the following assumptions:
///
/// - The invocation options should be initialized. This function does not
/// handle the '-help' or '-version' options, clients should handle those
/// directly.
///
/// - The diagnostics engine should have already been created by the client.
///
/// - No other CompilerInstance state should have been initialized (this is
/// an unchecked error).
///
/// - Clients should have initialized any LLVM target features that may be
/// required.
///
/// - Clients should eventually call llvm_shutdown() upon the completion of
/// this routine to ensure that any managed objects are properly destroyed.
///
/// Note that this routine may write output to 'stderr'.
///
/// \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);
/// }
/// @name LLVM Context
/// {

View File

@ -14,10 +14,12 @@
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/Version.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PTHManager.h"
#include "clang/Frontend/ChainedDiagnosticClient.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/PCHReader.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
@ -28,6 +30,7 @@
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Timer.h"
#include "llvm/System/Host.h"
#include "llvm/System/Path.h"
#include "llvm/System/Program.h"
using namespace clang;
@ -409,3 +412,87 @@ bool CompilerInstance::InitializeSourceManager(llvm::StringRef InputFile,
return true;
}
// High-Level Operations
bool CompilerInstance::ExecuteAction(FrontendAction &Act) {
assert(hasDiagnostics() && "Diagnostics engine is not initialized!");
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.
llvm::raw_ostream &OS = llvm::errs();
// Create the target instance.
setTarget(TargetInfo::CreateTargetInfo(getDiagnostics(), getTargetOpts()));
if (!hasTarget())
return false;
// Inform the target of the language options.
//
// FIXME: We shouldn't need to do this, the target should be immutable once
// created. This complexity should be lifted elsewhere.
getTarget().setForcedLangOptions(getLangOpts());
// Validate/process some options.
if (getHeaderSearchOpts().Verbose)
OS << "clang -cc1 version " CLANG_VERSION_STRING
<< " based upon " << PACKAGE_STRING
<< " hosted on " << llvm::sys::getHostTriple() << "\n";
if (getFrontendOpts().ShowTimers)
createFrontendTimer();
for (unsigned i = 0, e = getFrontendOpts().Inputs.size(); i != e; ++i) {
const std::string &InFile = getFrontendOpts().Inputs[i].second;
// If we aren't using an AST file, setup the file and source managers and
// the preprocessor.
bool IsAST = getFrontendOpts().Inputs[i].first == FrontendOptions::IK_AST;
if (!IsAST) {
if (!i) {
// Create a file manager object to provide access to and cache the
// filesystem.
createFileManager();
// Create the source manager.
createSourceManager();
} else {
// Reset the ID tables if we are reusing the SourceManager.
getSourceManager().clearIDTables();
}
// Create the preprocessor.
createPreprocessor();
}
if (Act.BeginSourceFile(*this, InFile, IsAST)) {
Act.Execute();
Act.EndSourceFile();
}
}
if (getDiagnosticOpts().ShowCarets)
if (unsigned NumDiagnostics = getDiagnostics().getNumDiagnostics())
OS << NumDiagnostics << " diagnostic"
<< (NumDiagnostics == 1 ? "" : "s")
<< " generated.\n";
if (getFrontendOpts().ShowStats) {
getFileManager().PrintStats();
OS << "\n";
}
// Return the appropriate status when verifying diagnostics.
//
// FIXME: If we could make getNumErrors() do the right thing, we wouldn't need
// this.
if (getDiagnosticOpts().VerifyDiagnostics)
return !static_cast<VerifyDiagnosticsClient&>(
getDiagnosticClient()).HadErrors();
return !getDiagnostics().getNumErrors();
}

View File

@ -14,10 +14,6 @@
//===----------------------------------------------------------------------===//
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/Version.h"
#include "clang/Driver/Arg.h"
#include "clang/Driver/ArgList.h"
#include "clang/Driver/CC1Options.h"
@ -30,7 +26,6 @@
#include "clang/Frontend/FrontendPluginRegistry.h"
#include "clang/Frontend/TextDiagnosticBuffer.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/VerifyDiagnosticsClient.h"
#include "llvm/LLVMContext.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/Support/ErrorHandling.h"
@ -38,8 +33,6 @@
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/System/DynamicLibrary.h"
#include "llvm/System/Host.h"
#include "llvm/System/Path.h"
#include "llvm/System/Signals.h"
#include "llvm/Target/TargetSelect.h"
#include <cstdio>
@ -207,13 +200,13 @@ int cc1_main(const char **ArgBegin, const char **ArgEnd,
Diags);
// Infer the builtin include path if unspecified.
if (Clang.getInvocation().getHeaderSearchOpts().UseBuiltinIncludes &&
Clang.getInvocation().getHeaderSearchOpts().ResourceDir.empty())
Clang.getInvocation().getHeaderSearchOpts().ResourceDir =
if (Clang.getHeaderSearchOpts().UseBuiltinIncludes &&
Clang.getHeaderSearchOpts().ResourceDir.empty())
Clang.getHeaderSearchOpts().ResourceDir =
CompilerInvocation::GetResourcesPath(Argv0, MainAddr);
// Honor -help.
if (Clang.getInvocation().getFrontendOpts().ShowHelp) {
if (Clang.getFrontendOpts().ShowHelp) {
llvm::OwningPtr<driver::OptTable> Opts(driver::createCC1OptTable());
Opts->PrintHelp(llvm::outs(), "clang -cc1",
"LLVM 'Clang' Compiler: http://clang.llvm.org");
@ -223,7 +216,7 @@ int cc1_main(const char **ArgBegin, const char **ArgEnd,
// Honor -version.
//
// FIXME: Use a better -version message?
if (Clang.getInvocation().getFrontendOpts().ShowVersion) {
if (Clang.getFrontendOpts().ShowVersion) {
llvm::cl::PrintVersionMessage();
return 0;
}
@ -249,85 +242,18 @@ int cc1_main(const char **ArgBegin, const char **ArgEnd,
Diags.Report(diag::err_fe_unable_to_load_plugin) << Path << Error;
}
// Create the frontend action.
llvm::OwningPtr<FrontendAction> Act(CreateFrontendAction(Clang));
// If there were any errors in processing arguments, exit now.
if (!Act || Clang.getDiagnostics().getNumErrors())
return 1;
// Create the target instance.
Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(),
Clang.getTargetOpts()));
if (!Clang.hasTarget())
return 1;
// Inform the target of the language options.
//
// FIXME: We shouldn't need to do this, the target should be immutable once
// created. This complexity should be lifted elsewhere.
Clang.getTarget().setForcedLangOptions(Clang.getLangOpts());
// Validate/process some options.
if (Clang.getHeaderSearchOpts().Verbose)
llvm::errs() << "clang -cc1 version " CLANG_VERSION_STRING
<< " based upon " << PACKAGE_STRING
<< " hosted on " << llvm::sys::getHostTriple() << "\n";
if (Clang.getFrontendOpts().ShowTimers)
Clang.createFrontendTimer();
for (unsigned i = 0, e = Clang.getFrontendOpts().Inputs.size(); i != e; ++i) {
const std::string &InFile = Clang.getFrontendOpts().Inputs[i].second;
// If we aren't using an AST file, setup the file and source managers and
// the preprocessor.
bool IsAST =
Clang.getFrontendOpts().Inputs[i].first == FrontendOptions::IK_AST;
if (!IsAST) {
if (!i) {
// Create a file manager object to provide access to and cache the
// filesystem.
Clang.createFileManager();
// Create the source manager.
Clang.createSourceManager();
} else {
// Reset the ID tables if we are reusing the SourceManager.
Clang.getSourceManager().clearIDTables();
}
// Create the preprocessor.
Clang.createPreprocessor();
}
if (Act->BeginSourceFile(Clang, InFile, IsAST)) {
Act->Execute();
Act->EndSourceFile();
}
// If there were errors in processing arguments, don't do anything else.
bool Success = false;
if (!Clang.getDiagnostics().getNumErrors()) {
// Create and execute the frontend action.
llvm::OwningPtr<FrontendAction> Act(CreateFrontendAction(Clang));
if (Act)
Success = Clang.ExecuteAction(*Act);
}
if (Clang.getDiagnosticOpts().ShowCarets)
if (unsigned NumDiagnostics = Clang.getDiagnostics().getNumDiagnostics())
fprintf(stderr, "%d diagnostic%s generated.\n", NumDiagnostics,
(NumDiagnostics == 1 ? "" : "s"));
if (Clang.getFrontendOpts().ShowStats) {
Clang.getFileManager().PrintStats();
fprintf(stderr, "\n");
}
// Return the appropriate status when verifying diagnostics.
//
// FIXME: If we could make getNumErrors() do the right thing, we wouldn't need
// this.
if (Clang.getDiagnosticOpts().VerifyDiagnostics)
return static_cast<VerifyDiagnosticsClient&>(
Clang.getDiagnosticClient()).HadErrors();
// Managed static deconstruction. Useful for making things like
// -time-passes usable.
llvm::llvm_shutdown();
return (Clang.getDiagnostics().getNumErrors() != 0);
return !Success;
}