[libclang] add CompilationDatabase support

llvm-svn: 159484
This commit is contained in:
Arnaud A. de Grandmaison 2012-06-30 11:27:57 +00:00
parent 8a6290721e
commit 0fe28a1a84
6 changed files with 417 additions and 1 deletions

View File

@ -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

View File

@ -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'

View 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;
}

View File

@ -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"

View 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
)

View 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