From f89f1bb0c62dd9f7cf830533a19193f6d9ca2aef Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 17 Feb 2017 22:49:27 +0000 Subject: [PATCH] clang: Add support for compile command databases Relies on having bear (https://github.com/rizsotto/Bear). Also refactor clang code to only have 1 instance per tab. --- .gitignore | 1 + configure.ac | 28 +++++ src/bin/editor/edi_editor.c | 41 ++----- src/bin/editor/edi_editor.h | 7 +- src/bin/language/edi_language_provider_c.c | 131 +++++++++++++-------- src/lib/Makefile.am | 3 +- src/lib/edi_build_provider_make.c | 8 +- 7 files changed, 130 insertions(+), 89 deletions(-) diff --git a/.gitignore b/.gitignore index 03fb89e..cbbf0c1 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ stamp-h1 ar-lib test-driver test-suite.log +compile_commands.json # gettext crap ABOUT-NLS diff --git a/configure.ac b/configure.ac index 10cea17..68ca4ae 100644 --- a/configure.ac +++ b/configure.ac @@ -31,6 +31,19 @@ AC_ARG_ENABLE([libclang], ], [want_clang="yes"]) +# Build EAR support +AC_ARG_ENABLE([bear], + [AS_HELP_STRING([--disable-bear], [Disable use of BuildEAR. @<:@default=enable@:>@])], + [ + if test "x${enableval}" = "xyes" ; then + want_bear="yes" + else + want_bear="no" + fi + ], + [want_bear="yes"]) + + ### Checks for programs @@ -112,6 +125,12 @@ if test "${want_clang}" = "yes"; then AC_CHECK_HEADERS([clang-c/Index.h], [have_clang="yes"], [have_clang="no"]) fi +if test "${want_bear}" = "yes"; then + AC_MSG_WARN([Checking...]) + AC_CHECK_PROG([have_bear], [bear], [yes]) + AC_MSG_WARN([got ${have_bear}]) +fi + EFL_TESTS([${build_tests}]) # Checks for library functions. @@ -133,6 +152,14 @@ fi CLANG_INCLUDE=`clang -E - -v < /dev/null 2>&1 | grep "^ /usr" | grep clang` AC_SUBST([CLANG_INCLUDE]) +BEAR_COMMAND="" +if test "${have_bear}" = "no"; then + AC_MSG_WARN([Could not find a usable bear command. Please install bear (BuildEAR) to enable better support for complex make projects.]) +else + BEAR_COMMAND="bear" +fi +AC_SUBST([BEAR_COMMAND]) + AC_CONFIG_FILES([ Makefile data/Makefile @@ -168,6 +195,7 @@ echo " CFLAGS.................: $CFLAGS" echo " LDFLAGS................: $LDFLAGS" echo " edje_cc................: ${edje_cc}" echo " highlighting (libclang): ${have_clang}" +echo " compilation db (bear)..: ${have_bear}" echo echo "eolian_gen...............: ${eolian_gen}" echo diff --git a/src/bin/editor/edi_editor.c b/src/bin/editor/edi_editor.c index 8737880..d76adff 100644 --- a/src/bin/editor/edi_editor.c +++ b/src/bin/editor/edi_editor.c @@ -668,15 +668,15 @@ _edi_line_status_set(Edi_Editor *editor, unsigned int number, Elm_Code_Status_Ty static void _clang_load_highlighting(const char *path, Edi_Editor *editor) { - CXFile cfile = clang_getFile(editor->tx_unit, path); + CXFile cfile = clang_getFile(editor->clang_unit, path); CXSourceRange range = clang_getRange( - clang_getLocationForOffset(editor->tx_unit, cfile, 0), - clang_getLocationForOffset(editor->tx_unit, cfile, eina_file_size_get(eina_file_open(path, EINA_FALSE)))); + clang_getLocationForOffset(editor->clang_unit, cfile, 0), + clang_getLocationForOffset(editor->clang_unit, cfile, ecore_file_size(path))); - clang_tokenize(editor->tx_unit, range, &editor->tokens, &editor->token_count); + clang_tokenize(editor->clang_unit, range, &editor->tokens, &editor->token_count); editor->cursors = (CXCursor *) malloc(editor->token_count * sizeof(CXCursor)); - clang_annotateTokens(editor->tx_unit, editor->tokens, editor->token_count, editor->cursors); + clang_annotateTokens(editor->clang_unit, editor->tokens, editor->token_count, editor->cursors); } static void @@ -689,7 +689,7 @@ _clang_show_highlighting(Edi_Editor *editor) Edi_Range range; Elm_Code_Token_Type type = ELM_CODE_TOKEN_TYPE_DEFAULT; - CXSourceRange tkrange = clang_getTokenExtent(editor->tx_unit, editor->tokens[i]); + CXSourceRange tkrange = clang_getTokenExtent(editor->clang_unit, editor->tokens[i]); clang_getSpellingLocation(clang_getRangeStart(tkrange), NULL, &range.start.line, &range.start.col, NULL); clang_getSpellingLocation(clang_getRangeEnd(tkrange), NULL, @@ -742,15 +742,15 @@ static void _clang_free_highlighting(Edi_Editor *editor) { free(editor->cursors); - clang_disposeTokens(editor->tx_unit, editor->tokens, editor->token_count); + clang_disposeTokens(editor->clang_unit, editor->tokens, editor->token_count); } static void -_clang_load_errors(const char *path EINA_UNUSED, Edi_Editor *editor) +_clang_load_errors(Edi_Editor *editor) { Elm_Code *code; const char *filename; - unsigned n = clang_getNumDiagnostics(editor->tx_unit); + unsigned n = clang_getNumDiagnostics(editor->clang_unit); unsigned i = 0; ecore_thread_main_loop_begin(); @@ -758,9 +758,9 @@ _clang_load_errors(const char *path EINA_UNUSED, Edi_Editor *editor) filename = elm_code_file_path_get(code->file); ecore_thread_main_loop_end(); - for(i = 0, n = clang_getNumDiagnostics(editor->tx_unit); i != n; ++i) + for(i = 0, n = clang_getNumDiagnostics(editor->clang_unit); i != n; ++i) { - CXDiagnostic diag = clang_getDiagnostic(editor->tx_unit, i); + CXDiagnostic diag = clang_getDiagnostic(editor->clang_unit, i); CXFile file; unsigned int line; CXString path; @@ -809,9 +809,7 @@ _edi_clang_setup(void *data, Ecore_Thread *thread EINA_UNUSED) { Edi_Editor *editor; Elm_Code *code; - const char *path, *args; - char **clang_argv; - unsigned int clang_argc; + const char *path; ecore_thread_main_loop_begin(); @@ -821,18 +819,7 @@ _edi_clang_setup(void *data, Ecore_Thread *thread EINA_UNUSED) ecore_thread_main_loop_end(); - /* Clang */ - /* FIXME: index should probably be global. */ - args = "-I/usr/inclue/ " EFL_CFLAGS " " CLANG_INCLUDES " -Wall -Wextra"; - clang_argv = eina_str_split_full(args, " ", 0, &clang_argc); - - editor->idx = clang_createIndex(0, 0); - - /* FIXME: Possibly activate more options? */ - editor->tx_unit = clang_parseTranslationUnit(editor->idx, path, (const char *const *)clang_argv, (int)clang_argc, NULL, 0, - clang_defaultEditingTranslationUnitOptions() | CXTranslationUnit_DetailedPreprocessingRecord); - - _clang_load_errors(path, editor); + _clang_load_errors(editor); _clang_load_highlighting(path, editor); _clang_show_highlighting(editor); } @@ -843,8 +830,6 @@ _edi_clang_dispose(void *data, Ecore_Thread *thread EINA_UNUSED) Edi_Editor *editor = (Edi_Editor *)data; _clang_free_highlighting(editor); - clang_disposeTranslationUnit(editor->tx_unit); - clang_disposeIndex(editor->idx); editor->highlight_thread = NULL; editor->highlight_cancel = EINA_FALSE; diff --git a/src/bin/editor/edi_editor.h b/src/bin/editor/edi_editor.h index ca6a497..0c265fd 100644 --- a/src/bin/editor/edi_editor.h +++ b/src/bin/editor/edi_editor.h @@ -51,14 +51,11 @@ struct _Edi_Editor #if HAVE_LIBCLANG /* Clang */ - CXIndex idx; - CXTranslationUnit tx_unit; + CXIndex clang_idx; + CXTranslationUnit clang_unit; CXToken *tokens; CXCursor *cursors; unsigned int token_count; - - CXIndex as_idx; - CXTranslationUnit as_unit; #endif Ecore_Thread *highlight_thread; diff --git a/src/bin/language/edi_language_provider_c.c b/src/bin/language/edi_language_provider_c.c index c85b18b..6d655a2 100644 --- a/src/bin/language/edi_language_provider_c.c +++ b/src/bin/language/edi_language_provider_c.c @@ -4,6 +4,7 @@ #if HAVE_LIBCLANG #include +#include #endif #include @@ -16,35 +17,92 @@ #include "edi_private.h" #if HAVE_LIBCLANG + +static void +_clang_commands_fallback_get(const char ***args, unsigned int *argc) +{ + const char *argstr; + + argstr = "-I/usr/include/ " EFL_CFLAGS " " CLANG_INCLUDES " -Wall -Wextra"; + *args = (const char **) eina_str_split_full(argstr, " ", 0, argc); +} + +static void +_clang_commands_get(const char *path, const char ***args, unsigned int *argc) +{ + CXCompilationDatabase_Error compilationDatabaseError; + CXCompilationDatabase compilationDatabase = clang_CompilationDatabase_fromDirectory(edi_project_get(), &compilationDatabaseError ); + + if ( compilationDatabaseError == CXCompilationDatabase_CanNotLoadDatabase) + { + INF("Could not load compile_commands.json in %s", edi_project_get()); + _clang_commands_fallback_get(args, argc); + return; + } + + CXCompileCommands compileCommands = clang_CompilationDatabase_getCompileCommands( compilationDatabase, path); + CXCompileCommand compileCommand = clang_CompileCommands_getCommand( compileCommands, 0 ); + unsigned int numArguments = clang_CompileCommand_getNumArgs( compileCommand ); + + if (numArguments == 0) + { + INF("File %s not found in compile_commands.json", path); + _clang_commands_fallback_get(args, argc); + return; + } + + const char** arguments = malloc(sizeof(char*) * numArguments); + + arguments[0] = CLANG_INCLUDES; + for( unsigned int i = 1; numArguments > i + 4; i++ ) + { + CXString argument = clang_CompileCommand_getArg( compileCommand, i + 1 ); + const char * strArgument = clang_getCString( argument ); + + arguments[i] = strdup(strArgument); + + clang_disposeString( argument ); + } + + *args = arguments; + *argc = numArguments <= 4 ? 1 : numArguments - 4; + + clang_CompilationDatabase_dispose(compilationDatabase); +} + static void _clang_autosuggest_setup(Edi_Editor *editor) { Elm_Code *code; const char *path; - char **clang_argv; - const char *args; - unsigned int clang_argc; + const char **args; + unsigned int argc, end_row, end_col; + struct CXUnsavedFile unsaved_file; code = elm_code_widget_code_get(editor->entry); path = elm_code_file_path_get(code->file); + end_row = elm_code_file_lines_get(code->file); + end_col = elm_code_file_line_get(code->file, end_row)->length; + + unsaved_file.Filename = path; + unsaved_file.Contents = elm_code_widget_text_between_positions_get( + editor->entry, 1, 1, end_row, end_col); + unsaved_file.Length = strlen(unsaved_file.Contents); + //Initialize Clang - args = "-I/usr/inclue/ " EFL_CFLAGS " " CLANG_INCLUDES " -Wall -Wextra"; - clang_argv = eina_str_split_full(args, " ", 0, &clang_argc); - - editor->as_idx = clang_createIndex(0, 0); - - editor->as_unit = clang_parseTranslationUnit(editor->as_idx, path, - (const char *const *)clang_argv, - (int)clang_argc, NULL, 0, - clang_defaultEditingTranslationUnitOptions()); + _clang_commands_get(path, &args, &argc); + editor->clang_idx = clang_createIndex(0, 0); + editor->clang_unit = clang_parseTranslationUnit(editor->clang_idx, path, + args, argc, NULL, 0, //&unsaved_file, 1, + clang_defaultEditingTranslationUnitOptions() | CXTranslationUnit_DetailedPreprocessingRecord); } static void _clang_autosuggest_dispose(Edi_Editor *editor) { - clang_disposeTranslationUnit(editor->as_unit); - clang_disposeIndex(editor->as_idx); + clang_disposeTranslationUnit(editor->clang_unit); + clang_disposeIndex(editor->clang_idx); } #endif @@ -112,7 +170,7 @@ _edi_language_c_lookup(Edi_Editor *editor, unsigned int row, unsigned int col) Elm_Code *code; const char *path = NULL; - if (!editor->as_unit) + if (!editor->clang_unit) return list; code = elm_code_widget_code_get(editor->entry); @@ -124,7 +182,7 @@ _edi_language_c_lookup(Edi_Editor *editor, unsigned int row, unsigned int col) editor->entry, 1, 1, row, col); unsaved_file.Length = strlen(unsaved_file.Contents); - res = clang_codeCompleteAt(editor->as_unit, path, row, col, + res = clang_codeCompleteAt(editor->clang_unit, path, row, col, &unsaved_file, 1, CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeCodePatterns); @@ -357,42 +415,20 @@ _edi_doc_dump(Edi_Language_Document *doc, CXComment comment, Eina_Strbuf *strbuf } static CXCursor -_edi_doc_cursor_get(Edi_Editor *editor, CXIndex idx, CXTranslationUnit unit, - unsigned int row, unsigned int col) +_edi_doc_cursor_get(Edi_Editor *editor, unsigned int row, unsigned int col) { CXFile cxfile; CXSourceLocation location; CXCursor cursor; - struct CXUnsavedFile unsaved_file; Elm_Code *code; - const char *path, *args; - char **clang_argv; - unsigned int clang_argc, end_row, end_col; + const char *path; code = elm_code_widget_code_get(editor->entry); path = elm_code_file_path_get(code->file); - end_row = elm_code_file_lines_get(code->file); - end_col = elm_code_file_line_get(code->file, end_row)->length; - - unsaved_file.Filename = path; - unsaved_file.Contents = elm_code_widget_text_between_positions_get( - editor->entry, 1, 1, end_row, end_col); - unsaved_file.Length = strlen(unsaved_file.Contents); - - //Initialize Clang - args = "-I/usr/inclue/ " EFL_CFLAGS " " CLANG_INCLUDES " -Wall -Wextra"; - clang_argv = eina_str_split_full(args, " ", 0, &clang_argc); - - idx = clang_createIndex(0, 0); - - unit = clang_parseTranslationUnit(idx, path, (const char *const *)clang_argv, - (int)clang_argc, &unsaved_file, 1, - clang_defaultEditingTranslationUnitOptions()); - - cxfile = clang_getFile(unit, path); - location = clang_getLocation(unit, cxfile, row, col); - cursor = clang_getCursor(unit, location); + cxfile = clang_getFile(editor->clang_unit, path); + location = clang_getLocation(editor->clang_unit, cxfile, row, col); + cursor = clang_getCursor(editor->clang_unit, location); return clang_getCursorReferenced(cursor); } @@ -403,18 +439,14 @@ _edi_language_c_lookup_doc(Edi_Editor *editor, unsigned int row, unsigned int co { Edi_Language_Document *doc = NULL; #if HAVE_LIBCLANG - CXIndex idx = NULL; - CXTranslationUnit unit = NULL; CXCursor cursor; CXComment comment; - cursor = _edi_doc_cursor_get(editor, idx, unit, row, col); + cursor = _edi_doc_cursor_get(editor, row, col); comment = clang_Cursor_getParsedComment(cursor); if (clang_Comment_getKind(comment) == CXComment_Null) { - clang_disposeTranslationUnit(unit); - clang_disposeIndex(idx); return NULL; } @@ -424,9 +456,6 @@ _edi_language_c_lookup_doc(Edi_Editor *editor, unsigned int row, unsigned int co _edi_doc_dump(doc, comment, doc->detail); _edi_doc_title_get(cursor, doc->title); _edi_doc_trim(doc->detail); - - clang_disposeTranslationUnit(unit); - clang_disposeIndex(idx); #endif return doc; } diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index e9b7011..b1f2f37 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -6,7 +6,8 @@ AM_CPPFLAGS = \ -DPACKAGE_LIB_DIR=\"$(libdir)\" \ -DPACKAGE_DATA_DIR=\"$(datadir)/$(PACKAGE)\" \ @EFL_CFLAGS@ \ --DEFL_EDI_BUILD +-DEFL_EDI_BUILD \ +-DBEAR_COMMAND=\"$(BEAR_COMMAND)\" lib_LTLIBRARIES = libedi.la diff --git a/src/lib/edi_build_provider_make.c b/src/lib/edi_build_provider_make.c index 9812bab..82611dd 100644 --- a/src/lib/edi_build_provider_make.c +++ b/src/lib/edi_build_provider_make.c @@ -51,7 +51,7 @@ static void _make_build_make(void) { chdir(edi_project_get()); - ecore_exe_pipe_run("make", ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_READ | + ecore_exe_pipe_run(BEAR_COMMAND " make", ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_ERROR_LINE_BUFFERED | ECORE_EXE_PIPE_ERROR | ECORE_EXE_PIPE_WRITE | ECORE_EXE_USE_SH, NULL); } @@ -60,7 +60,7 @@ static void _make_build_configure(void) { chdir(edi_project_get()); - ecore_exe_pipe_run("./configure && make", + ecore_exe_pipe_run("./configure && " BEAR_COMMAND " make", ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_ERROR_LINE_BUFFERED | ECORE_EXE_PIPE_ERROR | ECORE_EXE_PIPE_WRITE | ECORE_EXE_USE_SH, NULL); @@ -70,7 +70,7 @@ static void _make_build_cmake(void) { chdir(edi_project_get()); - ecore_exe_pipe_run("mkdir -p build && cd build && cmake .. && make && cd ..", + ecore_exe_pipe_run("mkdir -p build && cd build && cmake .. && " BEAR_COMMAND " make && cd ..", ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_ERROR_LINE_BUFFERED | ECORE_EXE_PIPE_ERROR | ECORE_EXE_PIPE_WRITE | ECORE_EXE_USE_SH, NULL); @@ -80,7 +80,7 @@ static void _make_build_autogen(void) { chdir(edi_project_get()); - ecore_exe_pipe_run("./autogen.sh && make", + ecore_exe_pipe_run("./autogen.sh && " BEAR_COMMAND " make", ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_ERROR_LINE_BUFFERED | ECORE_EXE_PIPE_ERROR | ECORE_EXE_PIPE_WRITE | ECORE_EXE_USE_SH, NULL);