Thanks Ted,

I committed (r159382), factoring Index.h

I attached the updated patches for adding CompilationDatabase support to
libclang + python.

The libclang patch builds on the above factoring, with no other
modification. The python patch is unmodified with respect to previous
reviews.

Cheers,

-- 
Arnaud de Grandmaison


On 06/28/2012 06:38 AM, Ted Kremenek wrote:
> I think the CompilationDatabase feature would be a great addition to 
> libclang.  As you suggest, factoring out Index.h into separate header files 
> (with Index.h including them for backwards compatibility) and having a new 
> one for Tooling support sounds reasonable to me.
>
> Sent from my iPad
>
> On Jun 26, 2012, at 2:53 AM, Arnaud de Grandmaison 
> <[email protected]> wrote:
>
>> Ping
>>
>> CC Doug per Chandler's suggestion on irc.
>>
>> Basically, the question is : how do we want to handle the Tooling stuff
>> with libclang ?
>> Should it be part of Index.h ? Should it even be part of libclang ?
>>
>> For now, my patch assumes this is completely part of libclang.
>>
>> My feeling is it should at least be logically separated, with its own
>> header files, with some splitting of Index.h to expose some components
>> (like CXString) without the complete Index.h.
>>
>> Any directions ?
>>
>> Cheers,
>> --
>>
>> Arnaud de Grandmaison
>>
>>
>>
>> On 06/22/2012 02:48 PM, Arnaud de Grandmaison wrote:
>>> Attached are 2 patches :
>>> - 0001-cindex-cdb.patch : adds support for CompilationDatabase to libclang
>>> - 0002-cindex-cdb-python.patch : teaches the python binding to use the
>>> functionality added by the first patch
>>>
>>> Those 2 patches have already been reviewed by Manuel Klimek and Gregory
>>> Szorc, for the C / C++ / python parts.
>>>
>>> The only point left is regarding the location of the code, as the first
>>> patch adds some functionality which is orthogonal to cindex. Should it
>>> live within cindex (as it is in the patch) or should we separate it ?
>>> Can someone knowledgeable with the libclang requirements have a look at it ?
>>>
>>> Cheers,
>>>
>>
>> -- 
>> Arnaud de Grandmaison
>>
>> <0001-cindex-cdb.patch>
>> <0002-cindex-cdb-python.patch>
>> _______________________________________________
>> cfe-commits mailing list
>> [email protected]
>> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>


>From 6e4e66d379fd84155800fd8af048f0884c7812f5 Mon Sep 17 00:00:00 2001
From: Arnaud de Grandmaison <[email protected]>
Date: Sun, 24 Jun 2012 11:49:17 +0200
Subject: [PATCH 1/2] [libclang] add CompilationDatabase support

---
 include/clang-c/CXCompilationDatabase.h |  143 +++++++++++++++++++++++++++++++
 test/Index/compile_commands.json        |   27 ++++++
 tools/c-index-test/c-index-test.c       |  107 +++++++++++++++++++++++-
 tools/libclang/CIndexCompilationDB.cpp  |  130 ++++++++++++++++++++++++++++
 tools/libclang/CMakeLists.txt           |    2 +
 tools/libclang/libclang.exports         |    9 ++
 6 files changed, 417 insertions(+), 1 deletions(-)
 create mode 100644 include/clang-c/CXCompilationDatabase.h
 create mode 100644 test/Index/compile_commands.json
 create mode 100644 tools/libclang/CIndexCompilationDB.cpp

diff --git a/include/clang-c/CXCompilationDatabase.h b/include/clang-c/CXCompilationDatabase.h
new file mode 100644
index 0000000..e4da627
--- /dev/null
+++ b/include/clang-c/CXCompilationDatabase.h
@@ -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
index 0000000..89d9f0d
--- /dev/null
+++ b/test/Index/compile_commands.json
@@ -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'
diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c
index 73e3e58..7f60925 100644
--- a/tools/c-index-test/c-index-test.c
+++ b/tools/c-index-test/c-index-test.c
@@ -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.                                                              */
 /******************************************************************************/
@@ -2801,6 +2902,8 @@ static void print_usage(void) {
     "       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,
     " <symbol filter> values:\n%s",
@@ -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
index 0000000..a537c9d
--- /dev/null
+++ b/tools/libclang/CIndexCompilationDB.cpp
@@ -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"
diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt
index a1090fb..ee401ed 100644
--- a/tools/libclang/CMakeLists.txt
+++ b/tools/libclang/CMakeLists.txt
@@ -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
   )
 
diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports
index c28b3b4..bba883f 100644
--- a/tools/libclang/libclang.exports
+++ b/tools/libclang/libclang.exports
@@ -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
-- 
1.7.8.6

>From 7a186a29d2c0ca03367fa958dfedd8531d4b9356 Mon Sep 17 00:00:00 2001
From: Arnaud de Grandmaison <[email protected]>
Date: Sun, 24 Jun 2012 11:50:09 +0200
Subject: [PATCH 2/2] [clang.py] add CompilationDatabase support

---
 bindings/python/clang/cindex.py                    |  153 ++++++++++++++++++++
 .../tests/cindex/INPUTS/compile_commands.json      |   17 +++
 bindings/python/tests/cindex/test_cdb.py           |   81 +++++++++++
 3 files changed, 251 insertions(+), 0 deletions(-)
 create mode 100644 bindings/python/tests/cindex/INPUTS/compile_commands.json
 create mode 100644 bindings/python/tests/cindex/test_cdb.py

diff --git a/bindings/python/clang/cindex.py b/bindings/python/clang/cindex.py
index 02cec2d..a3da421 100644
--- a/bindings/python/clang/cindex.py
+++ b/bindings/python/clang/cindex.py
@@ -2096,6 +2096,120 @@ class FileInclusion(object):
         """True if the included file is the input file."""
         return self.depth == 0
 
+class CompilationDatabaseError(Exception):
+    """Represents an error that occurred when working with a CompilationDatabase
+
+    Each error is associated to an enumerated value, accessible under
+    e.cdb_error. Consumers can compare the value with one of the ERROR_
+    constants in this class.
+    """
+
+    # An unknown error occured
+    ERROR_UNKNOWN = 0
+
+    # The database could not be loaded
+    ERROR_CANNOTLOADDATABASE = 1
+
+    def __init__(self, enumeration, message):
+        assert isinstance(enumeration, int)
+
+        if enumeration > 1:
+            raise Exception("Encountered undefined CompilationDatabase error "
+                            "constant: %d. Please file a bug to have this "
+                            "value supported." % enumeration)
+
+        self.cdb_error = enumeration
+        Exception.__init__(self, 'Error %d: %s' % (enumeration, message))
+
+class CompileCommand(object):
+    """Represents the compile command used to build a file"""
+    def __init__(self, cmd, ccmds):
+        self.cmd = cmd
+        # Keep a reference to the originating CompileCommands
+        # to prevent garbage collection
+        self.ccmds = ccmds
+
+    @property
+    def directory(self):
+        """Get the working directory for this CompileCommand"""
+        return CompileCommand_getDirectory(self.cmd).spelling
+
+    @property
+    def arguments(self):
+        """
+        Get an iterable object providing each argument in the
+        command line for the compiler invocation as a _CXString.
+
+        Invariants :
+          - the first argument is the compiler executable
+          - the last argument is the file being compiled
+        """
+        length = CompileCommand_getNumArgs(self.cmd)
+        for i in xrange(length):
+          yield CompileCommand_getArg(self.cmd, i)
+
+class CompileCommands(object):
+    """
+    CompileCommands is an iterable object containing all CompileCommand
+    that can be used for building a specific file.
+    """
+    def __init__(self, ccmds):
+        self.ccmds = ccmds
+
+    def __del__(self):
+        CompileCommands_dispose(self.ccmds)
+
+    def __len__(self):
+        return int(CompileCommands_getSize(self.ccmds))
+
+    def __getitem__(self, i):
+        cc = CompileCommands_getCommand(self.ccmds, i)
+        if cc is None:
+            raise IndexError
+        return CompileCommand(cc, self)
+
+    @staticmethod
+    def from_result(res, fn, args):
+        if not res:
+            return None
+        return CompileCommands(res)
+
+class CompilationDatabase(ClangObject):
+    """
+    The CompilationDatabase is a wrapper class around
+    clang::tooling::CompilationDatabase
+
+    It enables querying how a specific source file can be built.
+    """
+
+    def __del__(self):
+        CompilationDatabase_dispose(self)
+
+    @staticmethod
+    def from_result(res, fn, args):
+        if not res:
+            raise CompilationDatabaseError(0,
+                                           "CompilationDatabase loading failed")
+        return CompilationDatabase(res)
+
+    @staticmethod
+    def fromDirectory(buildDir):
+        """Builds a CompilationDatabase from the database found in buildDir"""
+        errorCode = c_uint()
+        try:
+          cdb = CompilationDatabase_fromDirectory(buildDir, byref(errorCode))
+        except CompilationDatabaseError as e:
+          raise CompilationDatabaseError(int(errorCode.value),
+                                         "CompilationDatabase loading failed")
+        return cdb
+
+    def getCompileCommands(self, filename):
+        """
+        Get an iterable object providing all the CompileCommands available to
+        build filename. Raise KeyError if filename is not found in the database.
+        """
+        return CompilationDatabase_getCompileCommands(self, filename)
+
 # Additional Functions and Types
 
 # String Functions
@@ -2463,9 +2577,48 @@ _clang_getCompletionPriority = lib.clang_getCompletionPriority
 _clang_getCompletionPriority.argtypes = [c_void_p]
 _clang_getCompletionPriority.restype = c_int
 
+# Compilation Database
+CompilationDatabase_fromDirectory = lib.clang_tooling_CompilationDatabase_fromDirectory
+CompilationDatabase_fromDirectory.argtypes = [c_char_p, POINTER(c_uint)]
+CompilationDatabase_fromDirectory.restype = c_object_p
+CompilationDatabase_fromDirectory.errcheck = CompilationDatabase.from_result
+
+CompilationDatabase_dispose = lib.clang_tooling_CompilationDatabase_dispose
+CompilationDatabase_dispose.argtypes = [c_object_p]
+
+CompilationDatabase_getCompileCommands = lib.clang_tooling_CompilationDatabase_getCompileCommands
+CompilationDatabase_getCompileCommands.argtypes = [c_object_p, c_char_p]
+CompilationDatabase_getCompileCommands.restype = c_object_p
+CompilationDatabase_getCompileCommands.errcheck = CompileCommands.from_result
+
+CompileCommands_dispose = lib.clang_tooling_CompileCommands_dispose
+CompileCommands_dispose.argtypes = [c_object_p]
+
+CompileCommands_getSize = lib.clang_tooling_CompileCommands_getSize
+CompileCommands_getSize.argtypes = [c_object_p]
+CompileCommands_getSize.restype = c_uint
+
+CompileCommands_getCommand = lib.clang_tooling_CompileCommands_getCommand
+CompileCommands_getCommand.argtypes = [c_object_p, c_uint]
+CompileCommands_getCommand.restype = c_object_p
+
+CompileCommand_getDirectory = lib.clang_tooling_CompileCommand_getDirectory
+CompileCommand_getDirectory.argtypes = [c_object_p]
+CompileCommand_getDirectory.restype = _CXString
+
+CompileCommand_getNumArgs = lib.clang_tooling_CompileCommand_getNumArgs
+CompileCommand_getNumArgs.argtypes = [c_object_p]
+CompileCommand_getNumArgs.restype = c_uint
+
+CompileCommand_getArg = lib.clang_tooling_CompileCommand_getArg
+CompileCommand_getArg.argtypes = [c_object_p, c_uint]
+CompileCommand_getArg.restype = _CXString
 
 __all__ = [
     'CodeCompletionResults',
+    'CompilationDatabase',
+    'CompileCommands',
+    'CompileCommand',
     'CursorKind',
     'Cursor',
     'Diagnostic',
diff --git a/bindings/python/tests/cindex/INPUTS/compile_commands.json b/bindings/python/tests/cindex/INPUTS/compile_commands.json
new file mode 100644
index 0000000..944150b
--- /dev/null
+++ b/bindings/python/tests/cindex/INPUTS/compile_commands.json
@@ -0,0 +1,17 @@
+[
+{
+  "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"
+}
+]
diff --git a/bindings/python/tests/cindex/test_cdb.py b/bindings/python/tests/cindex/test_cdb.py
new file mode 100644
index 0000000..84ac1f8
--- /dev/null
+++ b/bindings/python/tests/cindex/test_cdb.py
@@ -0,0 +1,81 @@
+from clang.cindex import CompilationDatabase
+from clang.cindex import CompilationDatabaseError
+from clang.cindex import CompileCommands
+from clang.cindex import CompileCommand
+import os
+import gc
+
+kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS')
+
+def test_create_fail():
+    """Check we fail loading a database with an assertion"""
+    path = os.path.dirname(__file__)
+    try:
+      cdb = CompilationDatabase.fromDirectory(path)
+    except CompilationDatabaseError as e:
+      assert e.cdb_error == CompilationDatabaseError.ERROR_CANNOTLOADDATABASE
+    else:
+      assert False
+
+def test_create():
+    """Check we can load a compilation database"""
+    cdb = CompilationDatabase.fromDirectory(kInputsDir)
+
+def test_lookup_fail():
+    """Check an assertion is raised when file lookup failed"""
+    cdb = CompilationDatabase.fromDirectory(kInputsDir)
+    assert cdb.getCompileCommands('file_do_not_exist.cpp') == None
+
+def test_lookup_succeed():
+    """Check we get some results if the file exists in the db"""
+    cdb = CompilationDatabase.fromDirectory(kInputsDir)
+    cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
+    assert len(cmds) != 0
+
+def test_1_compilecommand():
+    """Check file with single compile command"""
+    cdb = CompilationDatabase.fromDirectory(kInputsDir)
+    cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
+    assert len(cmds) == 1
+    assert cmds[0].directory == '/home/john.doe/MyProject'
+    expected = [ 'clang++', '-o', 'project.o', '-c',
+                 '/home/john.doe/MyProject/project.cpp']
+    for arg, exp in zip(cmds[0].arguments, expected):
+        assert arg.spelling == exp
+
+def test_2_compilecommand():
+    """Check file with 2 compile commands"""
+    cdb = CompilationDatabase.fromDirectory(kInputsDir)
+    cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project2.cpp')
+    assert len(cmds) == 2
+    expected = [
+        { 'wd': '/home/john.doe/MyProjectA',
+          'line': ['clang++', '-o', 'project2.o', '-c',
+                   '/home/john.doe/MyProject/project2.cpp']},
+        { 'wd': '/home/john.doe/MyProjectB',
+          'line': ['clang++', '-DFEATURE=1', '-o', 'project2-feature.o', '-c',
+                   '/home/john.doe/MyProject/project2.cpp']}
+        ]
+    for i in range(len(cmds)):
+        assert cmds[i].directory == expected[i]['wd']
+        for arg, exp in zip(cmds[i].arguments, expected[i]['line']):
+            assert arg.spelling == exp
+
+def test_compilationDB_references():
+    """Ensure CompilationsCommands are independent of the database"""
+    cdb = CompilationDatabase.fromDirectory(kInputsDir)
+    cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
+    del cdb
+    gc.collect()
+    workingdir = cmds[0].directory
+
+def test_compilationCommands_references():
+    """Ensure CompilationsCommand keeps a reference to CompilationCommands"""
+    cdb = CompilationDatabase.fromDirectory(kInputsDir)
+    cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
+    del cdb
+    cmd0 = cmds[0]
+    del cmds
+    gc.collect()
+    workingdir = cmd0.directory
+
-- 
1.7.8.6

_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to