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
