]> granicus.if.org Git - clang/commitdiff
[libclang] add CompilationDatabase support
authorArnaud A. de Grandmaison <arnaud.adegm@gmail.com>
Sat, 30 Jun 2012 11:27:57 +0000 (11:27 +0000)
committerArnaud A. de Grandmaison <arnaud.adegm@gmail.com>
Sat, 30 Jun 2012 11:27:57 +0000 (11:27 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159484 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang-c/CXCompilationDatabase.h [new file with mode: 0644]
test/Index/compile_commands.json [new file with mode: 0644]
tools/c-index-test/c-index-test.c
tools/libclang/CIndexCompilationDB.cpp [new file with mode: 0644]
tools/libclang/CMakeLists.txt
tools/libclang/libclang.exports

diff --git a/include/clang-c/CXCompilationDatabase.h b/include/clang-c/CXCompilationDatabase.h
new file mode 100644 (file)
index 0000000..e4da627
--- /dev/null
@@ -0,0 +1,143 @@
+/*===-- clang-c/CXCompilationDatabase.h - Compilation database  ---*- C -*-===*\
+|*                                                                            *|
+|*                     The LLVM Compiler Infrastructure                       *|
+|*                                                                            *|
+|* This file is distributed under the University of Illinois Open Source      *|
+|* License. See LICENSE.TXT for details.                                      *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header provides a public inferface to use CompilationDatabase without *|
+|* the full Clang C++ API.                                                    *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef CLANG_CXCOMPILATIONDATABASE_H
+#define CLANG_CXCOMPILATIONDATABASE_H
+
+#include "clang-c/Platform.h"
+#include "clang-c/CXString.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \defgroup COMPILATIONDB CompilationDatabase functions
+ * \ingroup CINDEX
+ *
+ * @{
+ */
+
+/**
+ * \brief Represents clang::tooling::CompilationDatabase
+ *
+ * Must be freed by \c clang_tooling_CompilationDatabase_dispose
+ */
+typedef void * CXCompilationDatabase;
+
+/**
+ * \brief Contains the results of a search in the compilation database
+ *
+ * When searching for the compile command for a file, the compilation db can
+ * return several commands, as the file may have been compiled with
+ * different options in different places of the project. This choice of compile
+ * commands is wrapped in this opaque data structure. It must be freed by
+ * \c clang_tooling_CompileCommands_dispose.
+ */
+typedef void * CXCompileCommands;
+
+/**
+ * \brief Represents the command line invocation to compile a specific file.
+ */
+typedef void * CXCompileCommand;
+
+/**
+ * \brief Error codes for Compilation Database
+ */
+typedef enum  {
+  /*
+   * \brief No error occured
+   */
+  CXCompilationDatabase_NoError = 0,
+
+  /*
+   * \brief Database can not be loaded
+   */
+  CXCompilationDatabase_CanNotLoadDatabase = 1
+
+} CXCompilationDatabase_Error;
+
+/**
+ * \brief Creates a compilation database from the database found in directory
+ * buildDir. It must be freed by \c clang_tooling_CompilationDatabase_dispose.
+ */
+CINDEX_LINKAGE CXCompilationDatabase
+clang_tooling_CompilationDatabase_fromDirectory(
+  const char *BuildDir,
+  CXCompilationDatabase_Error *ErrorCode);
+
+/**
+ * \brief Free the given compilation database
+ */
+CINDEX_LINKAGE void
+clang_tooling_CompilationDatabase_dispose(CXCompilationDatabase);
+
+/**
+ * \brief Find the compile commands used for a file. The compile commands
+ * must be freed by \c clang_tooling_CompileCommands_dispose.
+ */
+CINDEX_LINKAGE CXCompileCommands
+clang_tooling_CompilationDatabase_getCompileCommands(
+  CXCompilationDatabase,
+  const char *CompleteFileName);
+
+/**
+ * \brief Free the given CompileCommands
+ */
+CINDEX_LINKAGE void clang_tooling_CompileCommands_dispose(CXCompileCommands);
+
+/**
+ * \brief Get the number of CompileCommand we have for a file
+ */
+CINDEX_LINKAGE unsigned
+clang_tooling_CompileCommands_getSize(CXCompileCommands);
+
+/**
+ * \brief Get the I'th CompileCommand for a file
+ *
+ * Note : 0 <= i < clang_tooling_CompileCommands_getSize(CXCompileCommands)
+ */
+CINDEX_LINKAGE CXCompileCommand
+clang_tooling_CompileCommands_getCommand(CXCompileCommands, unsigned I);
+
+/**
+ * \brief Get the working directory where the CompileCommand was executed from
+ */
+CINDEX_LINKAGE CXString
+clang_tooling_CompileCommand_getDirectory(CXCompileCommand);
+
+/**
+ * \brief Get the number of arguments in the compiler invocation.
+ *
+ */
+CINDEX_LINKAGE unsigned
+clang_tooling_CompileCommand_getNumArgs(CXCompileCommand);
+
+/**
+ * \brief Get the I'th argument value in the compiler invocations
+ *
+ * Invariant :
+ *  - argument 0 is the compiler executable
+ */
+CINDEX_LINKAGE CXString
+clang_tooling_CompileCommand_getArg(CXCompileCommand, unsigned I);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+
diff --git a/test/Index/compile_commands.json b/test/Index/compile_commands.json
new file mode 100644 (file)
index 0000000..89d9f0d
--- /dev/null
@@ -0,0 +1,27 @@
+[
+{
+  "directory": "/home/john.doe/MyProject",
+  "command": "clang++  -o project.o -c /home/john.doe/MyProject/project.cpp",
+  "file": "/home/john.doe/MyProject/project.cpp"
+},
+{
+  "directory": "/home/john.doe/MyProjectA",
+  "command": "clang++ -o project2.o -c /home/john.doe/MyProject/project2.cpp",
+  "file": "/home/john.doe/MyProject/project2.cpp"
+},
+{
+  "directory": "/home/john.doe/MyProjectB",
+  "command": "clang++  -DFEATURE=1 -o project2-feature.o -c /home/john.doe/MyProject/project2.cpp",
+  "file": "/home/john.doe/MyProject/project2.cpp"
+}
+]
+# RUN: c-index-test -compilation-db %s
+# RUN: c-index-test -compilation-db lookup file_does_not_exists.cpp %s | FileCheck -check-prefix=FILE-NOT-FOUND %s
+# FILE-NOT-FOUND: file file_does_not_exists.cpp not found in compilation db
+
+# RUN: c-index-test -compilation-db lookup /home/john.doe/MyProject/project.cpp %s | FileCheck -check-prefix=FILE-1-CMD %s
+# FILE-1-CMD: workdir:'/home/john.doe/MyProject' cmdline:'clang++  -o project.o -c /home/john.doe/MyProject/project.cpp'
+
+# RUN: c-index-test -compilation-db lookup /home/john.doe/MyProject/project2.cpp %s | FileCheck -check-prefix=FILE-2-CMD %s
+# FILE-2-CMD: workdir:'/home/john.doe/MyProjectA' cmdline:'clang++ -o project2.o -c /home/john.doe/MyProject/project2.cpp'
+# FILE-2-CMD: workdir:'/home/john.doe/MyProjectB' cmdline:'clang++ -DFEATURE=1 -o project2-feature.o -c /home/john.doe/MyProject/project2.cpp'
index 73e3e589ce0ab1f4083fa521525a6cd771becffd..7f60925984188805bc1de7b702aee97eaf6a8634 100644 (file)
@@ -1,6 +1,7 @@
 /* c-index-test.c */
 
 #include "clang-c/Index.h"
+#include "clang-c/CXCompilationDatabase.h"
 #include <ctype.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -25,8 +26,25 @@ char *basename(const char* path)
 
     return((char*)path);
 }
+char *dirname(char* path)
+{
+    char* base1 = (char*)strrchr(path, '/');
+    char* base2 = (char*)strrchr(path, '\\');
+    if (base1 && base2)
+        if (base1 > base2)
+          *base1 = 0;
+        else
+          *base2 = 0;
+    else if (base1)
+        *base1 = 0
+    else if (base2)
+        *base2 = 0
+
+    return path;
+}
 #else
 extern char *basename(const char *);
+extern char *dirname(char *);
 #endif
 
 /** \brief Return the default parsing options. */
@@ -2361,6 +2379,89 @@ int perform_token_annotation(int argc, const char **argv) {
   return errorCode;
 }
 
+static int
+perform_test_compilation_db(const char *database, int argc, const char **argv) {
+  CXCompilationDatabase db;
+  CXCompileCommands CCmds;
+  CXCompileCommand CCmd;
+  CXCompilationDatabase_Error ec;
+  CXString wd;
+  CXString arg;
+  int errorCode = 0;
+  char *tmp;
+  unsigned len;
+  char *buildDir;
+  int i, j, a, numCmds, numArgs;
+
+  len = strlen(database);
+  tmp = (char *) malloc(len+1);
+  memcpy(tmp, database, len+1);
+  buildDir = dirname(tmp);
+
+  db = clang_tooling_CompilationDatabase_fromDirectory(buildDir, &ec);
+
+  if (db) {
+
+    if (ec!=CXCompilationDatabase_NoError) {
+      printf("unexpected error %d code while loading compilation database\n", ec);
+      errorCode = -1;
+      goto cdb_end;
+    }
+
+    for (i=0; i<argc && errorCode==0; ) {
+      if (strcmp(argv[i],"lookup")==0){
+        CCmds = clang_tooling_CompilationDatabase_getCompileCommands(db, argv[i+1]);
+
+        if (!CCmds) {
+          printf("file %s not found in compilation db\n", argv[i+1]);
+          errorCode = -1;
+          break;
+        }
+
+        numCmds = clang_tooling_CompileCommands_getSize(CCmds);
+
+        if (numCmds==0) {
+          fprintf(stderr, "should not get an empty compileCommand set for file"
+                          " '%s'\n", argv[i+1]);
+          errorCode = -1;
+          break;
+        }
+
+        for (j=0; j<numCmds; ++j) {
+          CCmd = clang_tooling_CompileCommands_getCommand(CCmds, j);
+
+          wd = clang_tooling_CompileCommand_getDirectory(CCmd);
+          printf("workdir:'%s'", clang_getCString(wd));
+          clang_disposeString(wd);
+
+          printf(" cmdline:'");
+          numArgs = clang_tooling_CompileCommand_getNumArgs(CCmd);
+          for (a=0; a<numArgs; ++a) {
+            if (a) printf(" ");
+            arg = clang_tooling_CompileCommand_getArg(CCmd, a);
+            printf("%s", clang_getCString(arg));
+            clang_disposeString(arg);
+          }
+          printf("'\n");
+        }
+
+        clang_tooling_CompileCommands_dispose(CCmds);
+
+        i += 2;
+      }
+    }
+    clang_tooling_CompilationDatabase_dispose(db);
+  } else {
+    printf("database loading failed with error code %d.\n", ec);
+    errorCode = -1;
+  }
+
+cdb_end:
+  free(tmp);
+
+  return errorCode;
+}
+
 /******************************************************************************/
 /* USR printing.                                                              */
 /******************************************************************************/
@@ -2800,6 +2901,8 @@ static void print_usage(void) {
     "       c-index-test -print-usr [<CursorKind> {<args>}]*\n"
     "       c-index-test -print-usr-file <file>\n"
     "       c-index-test -write-pch <file> <compiler arguments>\n");
+  fprintf(stderr,
+    "       c-index-test -compilation-db [lookup <filename>] database\n");
   fprintf(stderr,
     "       c-index-test -read-diagnostics <file>\n\n");
   fprintf(stderr,
@@ -2886,7 +2989,9 @@ int cindextest_main(int argc, const char **argv) {
     return print_usrs_file(argv[2]);
   else if (argc > 2 && strcmp(argv[1], "-write-pch") == 0)
     return write_pch_file(argv[2], argc - 3, argv + 3);
-           
+  else if (argc > 2 && strcmp(argv[1], "-compilation-db") == 0)
+    return perform_test_compilation_db(argv[argc-1], argc - 3, argv + 2);
+
   print_usage();
   return 1;
 }
diff --git a/tools/libclang/CIndexCompilationDB.cpp b/tools/libclang/CIndexCompilationDB.cpp
new file mode 100644 (file)
index 0000000..a537c9d
--- /dev/null
@@ -0,0 +1,130 @@
+#include "clang-c/CXCompilationDatabase.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "CXString.h"
+
+using namespace clang;
+using namespace clang::tooling;
+using namespace clang::cxstring;
+
+extern "C" {
+
+// FIXME: do something more usefull with the error message
+CXCompilationDatabase
+clang_tooling_CompilationDatabase_fromDirectory(
+  const char *BuildDir,
+  CXCompilationDatabase_Error *ErrorCode)
+{
+  std::string ErrorMsg;
+  CXCompilationDatabase_Error Err = CXCompilationDatabase_NoError;
+
+  CompilationDatabase *db = CompilationDatabase::loadFromDirectory(BuildDir,
+                                                                   ErrorMsg);
+
+  if (!db) {
+    fprintf(stderr, "LIBCLANG TOOLING ERROR: %s\n", ErrorMsg.c_str());
+    Err = CXCompilationDatabase_CanNotLoadDatabase;
+  }
+
+  if (ErrorCode)
+    *ErrorCode = Err;
+
+  return db;
+}
+
+void
+clang_tooling_CompilationDatabase_dispose(CXCompilationDatabase CDb)
+{
+  delete static_cast<CompilationDatabase *>(CDb);
+}
+
+struct AllocatedCXCompileCommands
+{
+  std::vector<CompileCommand> CCmd;
+
+  AllocatedCXCompileCommands(const std::vector<CompileCommand>& Cmd)
+    : CCmd(Cmd)
+  { }
+};
+
+CXCompileCommands
+clang_tooling_CompilationDatabase_getCompileCommands(CXCompilationDatabase CDb,
+                                 const char *CompleteFileName)
+{
+  if (CompilationDatabase *db = static_cast<CompilationDatabase *>(CDb)) {
+    const std::vector<CompileCommand>
+      CCmd(db->getCompileCommands(CompleteFileName));
+    if (!CCmd.empty())
+      return new AllocatedCXCompileCommands( CCmd );
+  }
+
+  return 0;
+}
+
+void
+clang_tooling_CompileCommands_dispose(CXCompileCommands Cmds)
+{
+  delete static_cast<AllocatedCXCompileCommands *>(Cmds);
+}
+
+unsigned
+clang_tooling_CompileCommands_getSize(CXCompileCommands Cmds)
+{
+  if (!Cmds)
+    return 0;
+
+  AllocatedCXCompileCommands *ACC =
+    static_cast<AllocatedCXCompileCommands *>(Cmds);
+
+  return ACC->CCmd.size();
+}
+
+CXCompileCommand
+clang_tooling_CompileCommands_getCommand(CXCompileCommands Cmds, unsigned I)
+{
+  if (!Cmds)
+    return 0;
+
+  AllocatedCXCompileCommands *ACC =
+    static_cast<AllocatedCXCompileCommands *>(Cmds);
+
+  if (I >= ACC->CCmd.size())
+    return 0;
+
+  return &ACC->CCmd[I];
+}
+
+CXString
+clang_tooling_CompileCommand_getDirectory(CXCompileCommand CCmd)
+{
+  if (!CCmd)
+    return createCXString((const char*)NULL);
+
+  CompileCommand *cmd = static_cast<CompileCommand *>(CCmd);
+  return createCXString(cmd->Directory);
+}
+
+unsigned
+clang_tooling_CompileCommand_getNumArgs(CXCompileCommand CCmd)
+{
+  if (!CCmd)
+    return 0;
+
+  return static_cast<CompileCommand *>(CCmd)->CommandLine.size();
+}
+
+CXString
+clang_tooling_CompileCommand_getArg(CXCompileCommand CCmd, unsigned Arg)
+{
+  if (!CCmd)
+    return createCXString((const char*)NULL);
+
+  CompileCommand *Cmd = static_cast<CompileCommand *>(CCmd);
+
+  if (Arg >= Cmd->CommandLine.size())
+    return createCXString((const char*)NULL);
+
+  return createCXString(Cmd->CommandLine[Arg]);
+}
+
+
+} // end: extern "C"
index a1090fb51f73fdf18a01e027eb4afc8cd4ffd25b..ee401ed44c41afeef8ad214a420d57f8759da8ae 100644 (file)
@@ -8,6 +8,7 @@ set(SOURCES
   CIndex.cpp
   CIndexCXX.cpp
   CIndexCodeCompletion.cpp
+  CIndexCompilationDB.cpp
   CIndexDiagnostic.cpp
   CIndexDiagnostic.h
   CIndexHigh.cpp
@@ -47,6 +48,7 @@ set(LIBRARIES
   clangEdit
   clangAST
   clangLex
+  clangTooling
   clangBasic
   )
 
index c28b3b4083fa95c54fdf59890eeca5f435947d1c..bba883fdda5f6a25f54da3282af8d756e95fa7af 100644 (file)
@@ -204,5 +204,14 @@ clang_saveTranslationUnit
 clang_sortCodeCompletionResults
 clang_toggleCrashRecovery
 clang_tokenize
+clang_tooling_CompilationDatabase_fromDirectory
+clang_tooling_CompilationDatabase_dispose
+clang_tooling_CompilationDatabase_getCompileCommands
+clang_tooling_CompileCommands_dispose
+clang_tooling_CompileCommands_getSize
+clang_tooling_CompileCommands_getCommand
+clang_tooling_CompileCommand_getDirectory
+clang_tooling_CompileCommand_getNumArgs
+clang_tooling_CompileCommand_getArg
 clang_visitChildren
 clang_visitChildrenWithBlock