diff options
author | Manuel Klimek <klimek@google.com> | 2011-06-02 16:58:33 +0000 |
---|---|---|
committer | Manuel Klimek <klimek@google.com> | 2011-06-02 16:58:33 +0000 |
commit | 16f213142f8f8f5410672205a19f79ed3c232929 (patch) | |
tree | a11e620036d64fa4f97884a4ea22c26c06ee3b24 /lib/Tooling/Tooling.cpp | |
parent | fb3f4aad0436a9c40e9130598162150890c405b5 (diff) |
Reverts the Tooling changes as requested by Chris.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@132462 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Tooling/Tooling.cpp')
-rw-r--r-- | lib/Tooling/Tooling.cpp | 403 |
1 files changed, 0 insertions, 403 deletions
diff --git a/lib/Tooling/Tooling.cpp b/lib/Tooling/Tooling.cpp deleted file mode 100644 index 97a9463852..0000000000 --- a/lib/Tooling/Tooling.cpp +++ /dev/null @@ -1,403 +0,0 @@ -//===--- Tooling.cpp - Running clang standalone tools ---------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements functions to run clang tools standalone instead -// of running them as a plugin. -// -//===----------------------------------------------------------------------===// - -#include "clang/Tooling/Tooling.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Host.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/ManagedStatic.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/system_error.h" -#include "clang/Basic/DiagnosticIDs.h" -#include "clang/Driver/Compilation.h" -#include "clang/Driver/Driver.h" -#include "clang/Driver/Tool.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/FrontendAction.h" -#include "clang/Frontend/FrontendDiagnostic.h" -#include "clang/Frontend/TextDiagnosticPrinter.h" -#include "JsonCompileCommandLineDatabase.h" -#include <map> -#include <cstdio> - -namespace clang { -namespace tooling { - -namespace { - -// Checks that the input conforms to the argv[] convention as in -// main(). Namely: -// - it must contain at least a program path, -// - argv[0], ..., and argv[argc - 1] mustn't be NULL, and -// - argv[argc] must be NULL. -void ValidateArgv(int argc, char *argv[]) { - if (argc < 1) { - fprintf(stderr, "ERROR: argc is %d. It must be >= 1.\n", argc); - abort(); - } - - for (int i = 0; i < argc; ++i) { - if (argv[i] == NULL) { - fprintf(stderr, "ERROR: argv[%d] is NULL.\n", i); - abort(); - } - } - - if (argv[argc] != NULL) { - fprintf(stderr, "ERROR: argv[argc] isn't NULL.\n"); - abort(); - } -} - -} // end namespace - -// FIXME: This file contains structural duplication with other parts of the -// code that sets up a compiler to run tools on it, and we should refactor -// it to be based on the same framework. - -static clang::Diagnostic *NewTextDiagnostics() { - llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs( - new clang::DiagnosticIDs()); - clang::TextDiagnosticPrinter *DiagClient = new clang::TextDiagnosticPrinter( - llvm::errs(), clang::DiagnosticOptions()); - return new clang::Diagnostic(DiagIDs, DiagClient); -} - -// Exists solely for the purpose of lookup of the main executable. -static int StaticSymbol; - -/// \brief Builds a clang driver initialized for running clang tools. -static clang::driver::Driver *NewDriver(clang::Diagnostic *Diagnostics, - const char *BinaryName) { - // This just needs to be some symbol in the binary. - void *const SymbolAddr = &StaticSymbol; - const llvm::sys::Path ExePath = - llvm::sys::Path::GetMainExecutable(BinaryName, SymbolAddr); - - const std::string DefaultOutputName = "a.out"; - clang::driver::Driver *CompilerDriver = new clang::driver::Driver( - ExePath.str(), llvm::sys::getHostTriple(), - DefaultOutputName, false, false, *Diagnostics); - CompilerDriver->setTitle("clang_based_tool"); - return CompilerDriver; -} - -/// \brief Retrieves the clang CC1 specific flags out of the compilation's jobs. -/// Returns NULL on error. -static const clang::driver::ArgStringList *GetCC1Arguments( - clang::Diagnostic *Diagnostics, clang::driver::Compilation *Compilation) { - // We expect to get back exactly one Command job, if we didn't something - // failed. Extract that job from the Compilation. - const clang::driver::JobList &Jobs = Compilation->getJobs(); - if (Jobs.size() != 1 || !isa<clang::driver::Command>(*Jobs.begin())) { - llvm::SmallString<256> error_msg; - llvm::raw_svector_ostream error_stream(error_msg); - Compilation->PrintJob(error_stream, Compilation->getJobs(), "; ", true); - Diagnostics->Report(clang::diag::err_fe_expected_compiler_job) - << error_stream.str(); - return NULL; - } - - // The one job we find should be to invoke clang again. - const clang::driver::Command *Cmd = - cast<clang::driver::Command>(*Jobs.begin()); - if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") { - Diagnostics->Report(clang::diag::err_fe_expected_clang_command); - return NULL; - } - - return &Cmd->getArguments(); -} - -/// \brief Returns a clang build invocation initialized from the CC1 flags. -static clang::CompilerInvocation *NewInvocation( - clang::Diagnostic *Diagnostics, - const clang::driver::ArgStringList &CC1Args) { - clang::CompilerInvocation *Invocation = new clang::CompilerInvocation; - clang::CompilerInvocation::CreateFromArgs( - *Invocation, CC1Args.data(), CC1Args.data() + CC1Args.size(), - *Diagnostics); - Invocation->getFrontendOpts().DisableFree = false; - return Invocation; -} - -/// \brief Runs the specified clang tool action and returns whether it executed -/// successfully. -static bool RunInvocation(const char *BinaryName, - clang::driver::Compilation *Compilation, - clang::CompilerInvocation *Invocation, - const clang::driver::ArgStringList &CC1Args, - clang::FrontendAction *ToolAction) { - llvm::OwningPtr<clang::FrontendAction> ScopedToolAction(ToolAction); - // Show the invocation, with -v. - if (Invocation->getHeaderSearchOpts().Verbose) { - llvm::errs() << "clang Invocation:\n"; - Compilation->PrintJob(llvm::errs(), Compilation->getJobs(), "\n", true); - llvm::errs() << "\n"; - } - - // Create a compiler instance to handle the actual work. - clang::CompilerInstance Compiler; - Compiler.setInvocation(Invocation); - - // Create the compilers actual diagnostics engine. - Compiler.createDiagnostics(CC1Args.size(), - const_cast<char**>(CC1Args.data())); - if (!Compiler.hasDiagnostics()) - return false; - - // Infer the builtin include path if unspecified. - if (Compiler.getHeaderSearchOpts().UseBuiltinIncludes && - Compiler.getHeaderSearchOpts().ResourceDir.empty()) { - // This just needs to be some symbol in the binary. - void *const SymbolAddr = &StaticSymbol; - Compiler.getHeaderSearchOpts().ResourceDir = - clang::CompilerInvocation::GetResourcesPath(BinaryName, SymbolAddr); - } - - const bool Success = Compiler.ExecuteAction(*ToolAction); - return Success; -} - -/// \brief Converts a string vector representing a Command line into a C -/// string vector representing the Argv (including the trailing NULL). -std::vector<char*> CommandLineToArgv(const std::vector<std::string> *Command) { - std::vector<char*> Result(Command->size() + 1); - for (std::vector<char*>::size_type I = 0; I < Command->size(); ++I) { - Result[I] = const_cast<char*>((*Command)[I].c_str()); - } - Result[Command->size()] = NULL; - return Result; -} - -bool RunToolWithFlags( - clang::FrontendAction *ToolAction, int Args, char *Argv[]) { - ValidateArgv(Args, Argv); - const llvm::OwningPtr<clang::Diagnostic> Diagnostics(NewTextDiagnostics()); - const llvm::OwningPtr<clang::driver::Driver> Driver( - NewDriver(Diagnostics.get(), Argv[0])); - const llvm::OwningPtr<clang::driver::Compilation> Compilation( - Driver->BuildCompilation(llvm::ArrayRef<const char*>(Argv, Args))); - const clang::driver::ArgStringList *const CC1Args = GetCC1Arguments( - Diagnostics.get(), Compilation.get()); - if (CC1Args == NULL) { - return false; - } - llvm::OwningPtr<clang::CompilerInvocation> Invocation( - NewInvocation(Diagnostics.get(), *CC1Args)); - return RunInvocation(Argv[0], Compilation.get(), Invocation.take(), - *CC1Args, ToolAction); -} - -/// \brief Runs 'ToolAction' on the code specified by 'FileContents'. -/// -/// \param FileContents A mapping from file name to source code. For each -/// entry a virtual file mapping will be created when running the tool. -bool RunToolWithFlagsOnCode( - const std::vector<std::string> &CommandLine, - const std::map<std::string, std::string> &FileContents, - clang::FrontendAction *ToolAction) { - const std::vector<char*> Argv = CommandLineToArgv(&CommandLine); - const char *const BinaryName = Argv[0]; - - const llvm::OwningPtr<clang::Diagnostic> Diagnostics(NewTextDiagnostics()); - const llvm::OwningPtr<clang::driver::Driver> Driver( - NewDriver(Diagnostics.get(), BinaryName)); - - // Since the Input is only virtual, don't check whether it exists. - Driver->setCheckInputsExist(false); - - const llvm::OwningPtr<clang::driver::Compilation> Compilation( - Driver->BuildCompilation(llvm::ArrayRef<const char*>(&Argv[0], - Argv.size() - 1))); - const clang::driver::ArgStringList *const CC1Args = GetCC1Arguments( - Diagnostics.get(), Compilation.get()); - if (CC1Args == NULL) { - return false; - } - llvm::OwningPtr<clang::CompilerInvocation> Invocation( - NewInvocation(Diagnostics.get(), *CC1Args)); - - for (std::map<std::string, std::string>::const_iterator - It = FileContents.begin(), End = FileContents.end(); - It != End; ++It) { - // Inject the code as the given file name into the preprocessor options. - const llvm::MemoryBuffer *Input = - llvm::MemoryBuffer::getMemBuffer(It->second.c_str()); - Invocation->getPreprocessorOpts().addRemappedFile(It->first.c_str(), Input); - } - - return RunInvocation(BinaryName, Compilation.get(), - Invocation.take(), *CC1Args, ToolAction); -} - -bool RunSyntaxOnlyToolOnCode( - clang::FrontendAction *ToolAction, llvm::StringRef Code) { - const char *const FileName = "input.cc"; - const char *const CommandLine[] = { - "clang-tool", "-fsyntax-only", FileName - }; - std::map<std::string, std::string> FileContents; - FileContents[FileName] = Code; - return RunToolWithFlagsOnCode( - std::vector<std::string>( - CommandLine, - CommandLine + sizeof(CommandLine)/sizeof(CommandLine[0])), - FileContents, ToolAction); -} - -namespace { - -// A CompileCommandHandler implementation that finds compile commands for a -// specific input file. -// -// FIXME: Implement early exit when JsonCompileCommandLineParser supports it. -class FindHandler : public clang::tooling::CompileCommandHandler { - public: - explicit FindHandler(llvm::StringRef File) - : FileToMatch(File), FoundMatchingCommand(false) {} - - virtual void EndTranslationUnits() { - if (!FoundMatchingCommand && ErrorMessage.empty()) { - ErrorMessage = "ERROR: No matching command found."; - } - } - - virtual void EndTranslationUnit() { - if (File == FileToMatch) { - FoundMatchingCommand = true; - MatchingCommand.Directory = Directory; - MatchingCommand.CommandLine = UnescapeJsonCommandLine(Command); - } - } - - virtual void HandleKeyValue(llvm::StringRef Key, llvm::StringRef Value) { - if (Key == "directory") { Directory = Value; } - else if (Key == "file") { File = Value; } - else if (Key == "command") { Command = Value; } - else { - ErrorMessage = (llvm::Twine("Unknown key: \"") + Key + "\"").str(); - } - } - - const llvm::StringRef FileToMatch; - bool FoundMatchingCommand; - CompileCommand MatchingCommand; - std::string ErrorMessage; - - llvm::StringRef Directory; - llvm::StringRef File; - llvm::StringRef Command; -}; - -} // end namespace - -CompileCommand FindCompileArgsInJsonDatabase( - llvm::StringRef FileName, llvm::StringRef JsonDatabase, - std::string &ErrorMessage) { - FindHandler find_handler(FileName); - JsonCompileCommandLineParser parser(JsonDatabase, &find_handler); - if (!parser.Parse()) { - ErrorMessage = parser.GetErrorMessage(); - return CompileCommand(); - } - return find_handler.MatchingCommand; -} - -/// \brief Returns the absolute path of 'File', by prepending it with -/// 'BaseDirectory' if 'File' is not absolute. Otherwise returns 'File'. -/// If 'File' starts with "./", the returned path will not contain the "./". -/// Otherwise, the returned path will contain the literal path-concatenation of -/// 'BaseDirectory' and 'File'. -/// -/// \param File Either an absolute or relative path. -/// \param BaseDirectory An absolute path. -static std::string GetAbsolutePath( - llvm::StringRef File, llvm::StringRef BaseDirectory) { - assert(llvm::sys::path::is_absolute(BaseDirectory)); - if (llvm::sys::path::is_absolute(File)) { - return File; - } - llvm::StringRef RelativePath(File); - if (RelativePath.startswith("./")) { - RelativePath = RelativePath.substr(strlen("./")); - } - llvm::SmallString<1024> AbsolutePath(BaseDirectory); - llvm::sys::path::append(AbsolutePath, RelativePath); - return AbsolutePath.str(); -} - -FrontendActionFactory::~FrontendActionFactory() {} - -ClangTool::ClangTool(int argc, char **argv) { - if (argc < 3) { - llvm::outs() << "Usage: " << argv[0] << " <cmake-output-dir> " - << "<file1> <file2> ...\n"; - exit(1); - } - llvm::SmallString<1024> JsonDatabasePath(argv[1]); - llvm::sys::path::append(JsonDatabasePath, "compile_commands.json"); - llvm::error_code Result = - llvm::MemoryBuffer::getFile(JsonDatabasePath, JsonDatabase); - if (Result != 0) { - llvm::outs() << "Error while opening JSON database: " << Result.message() - << "\n"; - exit(1); - } - Files = std::vector<std::string>(argv + 2, argv + argc); -} - -int ClangTool::Run(FrontendActionFactory *ActionFactory) { - llvm::StringRef BaseDirectory(::getenv("PWD")); - bool ProcessingFailed = false; - for (unsigned I = 0; I < Files.size(); ++I) { - llvm::SmallString<1024> File(GetAbsolutePath(Files[I], BaseDirectory)); - llvm::outs() << "Processing " << File << ".\n"; - std::string ErrorMessage; - clang::tooling::CompileCommand LookupResult = - clang::tooling::FindCompileArgsInJsonDatabase( - File.str(), JsonDatabase->getBuffer(), ErrorMessage); - if (!LookupResult.CommandLine.empty()) { - if (!LookupResult.Directory.empty()) { - // FIXME: What should happen if CommandLine includes -working-directory - // as well? - LookupResult.CommandLine.push_back( - "-working-directory=" + LookupResult.Directory); - } - if (!clang::tooling::RunToolWithFlags( - ActionFactory->New(), - LookupResult.CommandLine.size(), - &clang::tooling::CommandLineToArgv( - &LookupResult.CommandLine)[0])) { - llvm::outs() << "Error while processing " << File << ".\n"; - ProcessingFailed = true; - } - } else { - // FIXME: There are two use cases here: doing a fuzzy - // "find . -name '*.cc' |xargs tool" match, where as a user I don't care - // about the .cc files that were not found, and the use case where I - // specify all files I want to run over explicitly, where this should - // be an error. We'll want to add an option for this. - llvm::outs() << "Skipping " << File << ". Command line not found.\n"; - } - } - return ProcessingFailed ? 1 : 0; -} - -} // end namespace tooling -} // end namespace clang - |