[cfe-commits] r129924 - in /cfe/trunk: include/clang/Tooling/ include/clang/Tooling/Tooling.h lib/CMakeLists.txt lib/Tooling/ lib/Tooling/CMakeLists.txt lib/Tooling/Tooling.cpp unittests/CMakeLists.txt unittests/Tooling/ unittests/Tooling/ToolingTest.cpp

Manuel Klimek klimek at google.com
Thu Apr 21 11:37:41 PDT 2011


Author: klimek
Date: Thu Apr 21 13:37:41 2011
New Revision: 129924

URL: http://llvm.org/viewvc/llvm-project?rev=129924&view=rev
Log:
Adds a function to run FrontendActions over in-memory code. This is
the first step towards a standalone Clang tool infrastructure.
The plan is to make it easy to build command line tools that run over
the AST of source files in a project outside of the build system.

Added:
    cfe/trunk/include/clang/Tooling/
    cfe/trunk/include/clang/Tooling/Tooling.h
    cfe/trunk/lib/Tooling/
    cfe/trunk/lib/Tooling/CMakeLists.txt
    cfe/trunk/lib/Tooling/Tooling.cpp
    cfe/trunk/unittests/Tooling/
    cfe/trunk/unittests/Tooling/ToolingTest.cpp
Modified:
    cfe/trunk/lib/CMakeLists.txt
    cfe/trunk/unittests/CMakeLists.txt

Added: cfe/trunk/include/clang/Tooling/Tooling.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Tooling.h?rev=129924&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Tooling.h (added)
+++ cfe/trunk/include/clang/Tooling/Tooling.h Thu Apr 21 13:37:41 2011
@@ -0,0 +1,38 @@
+//===--- Tooling.h - Framework for standalone Clang tools -------*- C++ -*-===//
+//
+//                     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.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_TOOLING_H
+#define LLVM_CLANG_TOOLING_TOOLING_H
+
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+
+class FrontendAction;
+
+namespace tooling {
+
+/// \brief Runs (and deletes) the tool on 'Code' with the -fsynatx-only flag.
+///
+/// \param ToolAction The action to run over the code.
+//  \param Code C++ code.
+///
+/// \return - True if 'ToolAction' was successfully executed.
+bool RunSyntaxOnlyToolOnCode(
+    clang::FrontendAction *ToolAction, llvm::StringRef Code);
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_TOOLING_H

Modified: cfe/trunk/lib/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CMakeLists.txt?rev=129924&r1=129923&r2=129924&view=diff
==============================================================================
--- cfe/trunk/lib/CMakeLists.txt (original)
+++ cfe/trunk/lib/CMakeLists.txt Thu Apr 21 13:37:41 2011
@@ -13,3 +13,4 @@
 add_subdirectory(FrontendTool)
 add_subdirectory(Index)
 add_subdirectory(StaticAnalyzer)
+add_subdirectory(Tooling)

Added: cfe/trunk/lib/Tooling/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/CMakeLists.txt?rev=129924&view=auto
==============================================================================
--- cfe/trunk/lib/Tooling/CMakeLists.txt (added)
+++ cfe/trunk/lib/Tooling/CMakeLists.txt Thu Apr 21 13:37:41 2011
@@ -0,0 +1,5 @@
+SET(LLVM_USED_LIBS clangBasic clangFrontend clangAST)
+
+add_clang_library(clangTooling
+  Tooling.cpp
+  )

Added: cfe/trunk/lib/Tooling/Tooling.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Tooling.cpp?rev=129924&view=auto
==============================================================================
--- cfe/trunk/lib/Tooling/Tooling.cpp (added)
+++ cfe/trunk/lib/Tooling/Tooling.cpp Thu Apr 21 13:37:41 2011
@@ -0,0 +1,218 @@
+//===--- 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 "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 <string>
+#include <map>
+#include <vector>
+
+namespace clang {
+namespace tooling {
+
+// 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;
+}
+
+/// \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);
+}
+
+} // end namespace tooling
+} // end namespace clang
+

Modified: cfe/trunk/unittests/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/CMakeLists.txt?rev=129924&r1=129923&r2=129924&view=diff
==============================================================================
--- cfe/trunk/unittests/CMakeLists.txt (original)
+++ cfe/trunk/unittests/CMakeLists.txt Thu Apr 21 13:37:41 2011
@@ -59,3 +59,8 @@
   Frontend/FrontendActionTest.cpp
   USED_LIBS gtest gtest_main clangFrontend
  )
+
+add_clang_unittest(Tooling
+  Tooling/ToolingTest.cpp
+  USED_LIBS gtest gtest_main clangTooling
+ )

Added: cfe/trunk/unittests/Tooling/ToolingTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/ToolingTest.cpp?rev=129924&view=auto
==============================================================================
--- cfe/trunk/unittests/Tooling/ToolingTest.cpp (added)
+++ cfe/trunk/unittests/Tooling/ToolingTest.cpp Thu Apr 21 13:37:41 2011
@@ -0,0 +1,91 @@
+//===- unittest/Tooling/ToolingTest.cpp - Tooling unit 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/DeclGroup.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+
+namespace {
+/// Takes an ast consumer and returns it from CreateASTConsumer. This only
+/// works with single translation unit compilations.
+class TestAction : public clang::ASTFrontendAction {
+ public:
+  /// Takes ownership of TestConsumer.
+  explicit TestAction(clang::ASTConsumer *TestConsumer)
+      : TestConsumer(TestConsumer) {}
+
+ protected:
+  virtual clang::ASTConsumer* CreateASTConsumer(
+      clang::CompilerInstance& compiler, llvm::StringRef dummy) {
+    /// TestConsumer will be deleted by the framework calling us.
+    return TestConsumer;
+  }
+
+ private:
+  clang::ASTConsumer * const TestConsumer;
+};
+
+class FindTopLevelDeclConsumer : public clang::ASTConsumer {
+ public:
+  explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl)
+      : FoundTopLevelDecl(FoundTopLevelDecl) {}
+  virtual void HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) {
+    *FoundTopLevelDecl = true;
+  }
+ private:
+  bool * const FoundTopLevelDecl;
+};
+} // end namespace
+
+TEST(RunSyntaxOnlyToolOnCode, FindsTopLevelDeclOnEmptyCode) {
+  bool FoundTopLevelDecl = false;
+  EXPECT_TRUE(RunSyntaxOnlyToolOnCode(
+      new TestAction(new FindTopLevelDeclConsumer(&FoundTopLevelDecl)), ""));
+  EXPECT_TRUE(FoundTopLevelDecl);
+}
+
+namespace {
+class FindClassDeclXConsumer : public clang::ASTConsumer {
+ public:
+  FindClassDeclXConsumer(bool *FoundClassDeclX)
+      : FoundClassDeclX(FoundClassDeclX) {}
+  virtual void HandleTopLevelDecl(clang::DeclGroupRef GroupRef) {
+    if (CXXRecordDecl* Record = llvm::dyn_cast<clang::CXXRecordDecl>(
+            *GroupRef.begin())) {
+      if (Record->getName() == "X") {
+        *FoundClassDeclX = true;
+      }
+    }
+  }
+ private:
+  bool *FoundClassDeclX;
+};
+} // end namespace
+
+TEST(RunSyntaxOnlyToolOnCode, FindsClassDecl) {
+  bool FoundClassDeclX = false;
+  EXPECT_TRUE(RunSyntaxOnlyToolOnCode(new TestAction(
+      new FindClassDeclXConsumer(&FoundClassDeclX)), "class X;"));
+  EXPECT_TRUE(FoundClassDeclX);
+
+  FoundClassDeclX = false;
+  EXPECT_TRUE(RunSyntaxOnlyToolOnCode(new TestAction(
+      new FindClassDeclXConsumer(&FoundClassDeclX)), "class Y;"));
+  EXPECT_FALSE(FoundClassDeclX);
+}
+
+} // end namespace tooling
+} // end namespace clang
+





More information about the cfe-commits mailing list