[libclang] add CompilationDatabase support
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159484 91177308-0d34-0410-b5e6-96231b3b80d8
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 @@
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 @@
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 @@
" 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 @@
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 @@
CIndex.cpp
CIndexCXX.cpp
CIndexCodeCompletion.cpp
+ CIndexCompilationDB.cpp
CIndexDiagnostic.cpp
CIndexDiagnostic.h
CIndexHigh.cpp
@@ -47,6 +48,7 @@
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_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