From: Daniel Dunbar Date: Wed, 2 Dec 2009 03:23:45 +0000 (+0000) Subject: Add ASTUnit::LoadFromCommandLine, which creates an ASTUnit out of a list of X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7b55668db7618334cc40011d3c1e128524d89462;p=clang Add ASTUnit::LoadFromCommandLine, which creates an ASTUnit out of a list of (clang/driver) command line arguments (including the source file). - The arguments are expected to include the source file. - The idea is that even though this is a somewhat odd API, its the form which many tools can most easily use (for example, by interposing with the compiler). Also, switch index-test's -ast-from-source to use this entry point, and provide a -arg command line argument which can be used to test that the command line arguments are handled correctly. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@90288 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index cbc287c58c..9dbd9cfa21 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -15,8 +15,8 @@ def err_fe_error_reading_stdin : Error<"error reading stdin">; def err_fe_error_backend : Error<"error in backend: %0">, DefaultFatal; def err_fe_invalid_ast_file : Error<"invalid AST file: '%0'">, DefaultFatal; def err_fe_invalid_ast_action : Error<"invalid action for AST input">, DefaultFatal; -def err_fe_invalid_code_complete_file - : Error<"cannot locate code-completion file %0">, DefaultFatal; +def err_fe_invalid_code_complete_file : Error< + "cannot locate code-completion file %0">, DefaultFatal; def err_fe_stdout_binary : Error<"unable to change standard output to binary">, DefaultFatal; def err_fe_dependency_file_requires_MT : Error< @@ -29,6 +29,10 @@ def err_fe_unable_to_find_fixit_file : Error< "FIX-IT could not find file '%0'">; def err_fe_invalid_plugin_name : Error< "unable to find plugin '%0'">; +def err_fe_expected_compiler_job : Error< + "unable to handle compilation, expected exactly one compiler job in '%0'">; +def err_fe_expected_clang_command : Error< + "expected a clang compiler command">; def err_verify_bogus_characters : Error< "bogus characters before '{{' in expected string">; diff --git a/include/clang/Frontend/ASTUnit.h b/include/clang/Frontend/ASTUnit.h index 04dc5ed8cc..c55638a237 100644 --- a/include/clang/Frontend/ASTUnit.h +++ b/include/clang/Frontend/ASTUnit.h @@ -122,6 +122,30 @@ public: bool OnlyLocalDecls = false, bool UseBumpAllocator = false); + /// LoadFromCommandLine - Create an ASTUnit from a vector of command line + /// arguments, which must specify exactly one source file. + /// + /// \param ArgBegin - The beginning of the argument vector. + /// + /// \param ArgEnd - The end of the argument vector. + /// + /// \param Diags - The diagnostics engine to use for reporting errors. + /// + /// \param Argv0 - The program path (from argv[0]), for finding the builtin + /// compiler path. + /// + /// \param MainAddr - The address of main (or some other function in the main + /// executable), for finding the builtin compiler path. + // + // FIXME: Move OnlyLocalDecls, UseBumpAllocator to setters on the ASTUnit, we + // shouldn't need to specify them at construction time. + static ASTUnit *LoadFromCommandLine(const char **ArgBegin, + const char **ArgEnd, + Diagnostic &Diags, + const char *Arg0, + void *MainAddr, + bool OnlyLocalDecls = false, + bool UseBumpAllocator = false); }; } // namespace clang diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp index 3f07b880f7..6e3a1e1fa7 100644 --- a/lib/Frontend/ASTUnit.cpp +++ b/lib/Frontend/ASTUnit.cpp @@ -17,8 +17,13 @@ #include "clang/AST/ASTConsumer.h" #include "clang/AST/DeclVisitor.h" #include "clang/AST/StmtVisitor.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/Job.h" +#include "clang/Driver/Tool.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/FrontendOptions.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" @@ -26,6 +31,7 @@ #include "clang/Basic/TargetInfo.h" #include "clang/Basic/Diagnostic.h" #include "llvm/LLVMContext.h" +#include "llvm/System/Host.h" #include "llvm/System/Path.h" using namespace clang; @@ -262,3 +268,52 @@ error: Clang.takeDiagnostics(); return 0; } + +ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, + const char **ArgEnd, + Diagnostic &Diags, + const char *Argv0, + void *MainAddr, + bool OnlyLocalDecls, + bool UseBumpAllocator) { + llvm::SmallVector Args; + Args.push_back(""); // FIXME: Remove dummy argument. + Args.insert(Args.end(), ArgBegin, ArgEnd); + + // FIXME: Find a cleaner way to force the driver into restricted modes. We + // also want to force it to use clang. + Args.push_back("-fsyntax-only"); + + llvm::sys::Path Path = llvm::sys::Path::GetMainExecutable(Argv0, MainAddr); + driver::Driver TheDriver(Path.getBasename().c_str(),Path.getDirname().c_str(), + llvm::sys::getHostTriple().c_str(), + "a.out", false, Diags); + llvm::OwningPtr C( + TheDriver.BuildCompilation(Args.size(), Args.data())); + + // We expect to get back exactly one command job, if we didn't something + // failed. + const driver::JobList &Jobs = C->getJobs(); + if (Jobs.size() != 1 || !isa(Jobs.begin())) { + llvm::SmallString<256> Msg; + llvm::raw_svector_ostream OS(Msg); + C->PrintJob(OS, C->getJobs(), "; ", true); + Diags.Report(diag::err_fe_expected_compiler_job) << OS.str(); + return 0; + } + + const driver::Command *Cmd = cast(*Jobs.begin()); + if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") { + Diags.Report(diag::err_fe_expected_clang_command); + return 0; + } + + const driver::ArgStringList &CCArgs = Cmd->getArguments(); + CompilerInvocation CI; + CompilerInvocation::CreateFromArgs(CI, (const char**) CCArgs.data(), + (const char**) CCArgs.data()+CCArgs.size(), + Argv0, MainAddr, Diags); + + return LoadFromCompilerInvocation(CI, Diags, OnlyLocalDecls, + UseBumpAllocator); +} diff --git a/tools/index-test/index-test.cpp b/tools/index-test/index-test.cpp index dd7cbb2164..fc8547b784 100644 --- a/tools/index-test/index-test.cpp +++ b/tools/index-test/index-test.cpp @@ -208,20 +208,24 @@ static void ProcessASTLocation(ASTLocation ASTLoc, Indexer &Idxer) { static llvm::cl::opt ASTFromSource("ast-from-source", - llvm::cl::desc("Treat the inputs as source files to parse.")); + llvm::cl::desc("Treat the inputs as source files to parse")); + +static llvm::cl::list +CompilerArgs("arg", llvm::cl::desc("Extra arguments to use during parsing")); static llvm::cl::list InputFilenames(llvm::cl::Positional, llvm::cl::desc("")); -void CreateCompilerInvocation(const std::string &Filename, - CompilerInvocation &CI, Diagnostic &Diags, - const char *argv0) { +ASTUnit *CreateFromSource(const std::string &Filename, Diagnostic &Diags, + const char *Argv0) { llvm::SmallVector Args; Args.push_back(Filename.c_str()); + for (unsigned i = 0, e = CompilerArgs.size(); i != e; ++i) + Args.push_back(CompilerArgs[i].c_str()); - void *MainAddr = (void*) (intptr_t) CreateCompilerInvocation; - CompilerInvocation::CreateFromArgs(CI, Args.data(), Args.data() + Args.size(), - argv0, MainAddr, Diags); + return ASTUnit::LoadFromCommandLine(Args.data(), Args.data() + Args.size(), + Diags, Argv0, + (void*) (intptr_t) CreateFromSource); } int main(int argc, char **argv) { @@ -249,10 +253,8 @@ int main(int argc, char **argv) { llvm::OwningPtr AST; if (ASTFromSource) { - CompilerInvocation CI; - CreateCompilerInvocation(InFile, CI, *Diags, argv[0]); - AST.reset(ASTUnit::LoadFromCompilerInvocation(CI, *Diags)); - if (!AST) + AST.reset(CreateFromSource(InFile, *Diags, argv[0])); + if (!AST || Diags->getNumErrors()) ErrMsg = "unable to create AST"; } else AST.reset(ASTUnit::LoadFromPCHFile(InFile, &ErrMsg));