[gnome-builder] compile-commands: locate information on Vala files



commit ab6844f2299e2e7ec2ff522bd98197f72169cdce
Author: Christian Hergert <chergert redhat com>
Date:   Tue Oct 17 01:33:44 2017 -0700

    compile-commands: locate information on Vala files
    
    We need some special fallback code when dealing with Vala and
    how some build systems create compile_commands.json. In
    particular, Meson only generates a single .vala command and we
    won't be able to look it up by directly by file.

 src/libide/buildsystem/ide-compile-commands.c |  150 +++++++++++++++++++++++--
 src/tests/data/test-ide-compile-commands.json |    5 +
 src/tests/test-ide-compile-commands.c         |   12 ++
 3 files changed, 155 insertions(+), 12 deletions(-)
---
diff --git a/src/libide/buildsystem/ide-compile-commands.c b/src/libide/buildsystem/ide-compile-commands.c
index c3a96f2..e55e6f8 100644
--- a/src/libide/buildsystem/ide-compile-commands.c
+++ b/src/libide/buildsystem/ide-compile-commands.c
@@ -22,6 +22,7 @@
 #include <string.h>
 
 #include "ide-debug.h"
+#include "ide-macros.h"
 
 #include "buildsystem/ide-compile-commands.h"
 
@@ -49,9 +50,30 @@
 
 struct _IdeCompileCommands
 {
-  GObject     parent_instance;
+  GObject parent_instance;
+
+  /*
+   * The info_by_file field contains a hsahtable whose keys are GFile
+   * matching the file that is to be compiled. It contains as a value
+   * the CompileInfo struct describing how to compile that file.
+   */
   GHashTable *info_by_file;
-  guint       has_loaded : 1;
+
+  /*
+   * The vala_info field contains an array of every vala like file we've
+   * discovered while parsing the database. This is used so because some
+   * compile_commands.json only have a single valac command which wont
+   * match the file we want to lookup (Notably Meson-based).
+   */
+  GPtrArray *vala_info;
+
+  /*
+   * The has_loaded field determines if we've had a load (async or sync
+   * variant) operation called. We can only do this safely once because
+   * we assign state in the task worker. Callers must discard the object
+   * if the load operation fails.
+   */
+  guint has_loaded : 1;
 };
 
 typedef struct
@@ -83,6 +105,7 @@ ide_compile_commands_finalize (GObject *object)
   IdeCompileCommands *self = (IdeCompileCommands *)object;
 
   g_clear_pointer (&self->info_by_file, g_hash_table_unref);
+  g_clear_pointer (&self->vala_info, g_ptr_array_unref);
 
   G_OBJECT_CLASS (ide_compile_commands_parent_class)->finalize (object);
 }
@@ -128,6 +151,7 @@ ide_compile_commands_load_worker (GTask        *task,
   g_autoptr(GError) error = NULL;
   g_autoptr(GHashTable) info_by_file = NULL;
   g_autoptr(GHashTable) directories_by_path = NULL;
+  g_autoptr(GPtrArray) vala_info = NULL;
   g_autofree gchar *contents = NULL;
   JsonNode *root;
   JsonArray *ar;
@@ -171,6 +195,8 @@ ide_compile_commands_load_worker (GTask        *task,
                                                NULL,
                                                g_object_unref);
 
+  vala_info = g_ptr_array_new_with_free_func (compile_info_free);
+
   n_items = json_array_get_length (ar);
 
   for (guint i = 0; i < n_items; i++)
@@ -224,11 +250,26 @@ ide_compile_commands_load_worker (GTask        *task,
       info->file = g_file_resolve_relative_path (dir, file);
       info->directory = g_object_ref (dir);
       info->command = g_strdup (command);
-
       g_hash_table_insert (info_by_file, info->file, info);
+
+      /*
+       * We might need to keep a special copy of this for resolving .vala
+       * builds which won't be able ot be matched based on the filename. We
+       * keep all of them around right now in case we want to later on find
+       * the closest match based on directory.
+       */
+      if (g_str_has_suffix (file, ".vala"))
+        {
+          info = g_slice_new (CompileInfo);
+          info->file = g_file_resolve_relative_path (dir, file);
+          info->directory = g_object_ref (dir);
+          info->command = g_strdup (command);
+          g_ptr_array_add (vala_info, info);
+        }
     }
 
   self->info_by_file = g_steal_pointer (&info_by_file);
+  self->vala_info = g_steal_pointer (&vala_info);
 
   g_task_return_boolean (task, TRUE);
 
@@ -417,6 +458,9 @@ ide_compile_commands_filter_c (IdeCompileCommands   *self,
   g_assert (info != NULL);
   g_assert (argv != NULL);
 
+  if (*argv == NULL)
+    return;
+
   ar = g_ptr_array_new ();
 
   for (guint i = 0; (*argv)[i] != NULL; i++)
@@ -466,12 +510,67 @@ ide_compile_commands_filter_c (IdeCompileCommands   *self,
 
 static void
 ide_compile_commands_filter_vala (IdeCompileCommands   *self,
+                                  const CompileInfo    *info,
                                   gchar              ***argv)
 {
+  GPtrArray *ar;
+
   g_assert (IDE_IS_COMPILE_COMMANDS (self));
+  g_assert (info != NULL);
   g_assert (argv != NULL);
 
-  /* TODO: Filter Vala Commands */
+  if (*argv == NULL)
+    return;
+
+  ar = g_ptr_array_new ();
+
+  for (guint i = 0; (*argv)[i] != NULL; i++)
+    {
+      const gchar *param = (*argv)[i];
+      const gchar *next = (*argv)[i+1];
+
+      if (g_str_has_prefix (param, "--pkg=") ||
+          g_str_has_prefix (param, "--target-glib=") ||
+          !!strstr (param, ".vapi"))
+        {
+          g_ptr_array_add (ar, g_strdup (param));
+        }
+      else if (g_str_has_prefix (param, "--vapidir=") ||
+               g_str_has_prefix (param, "--girdir=") ||
+               g_str_has_prefix (param, "--metadatadir="))
+        {
+          g_autofree gchar *resolved = NULL;
+          gchar *eq = strchr (param, '=');
+
+          next = eq + 1;
+          *eq = '\0';
+
+          resolved = ide_compile_commands_resolve (self, info, next);
+          g_ptr_array_add (ar, g_strdup_printf ("%s=%s", param, resolved));
+        }
+      else if (next != NULL &&
+               (g_str_has_prefix (param, "--pkg") ||
+                g_str_has_prefix (param, "--target-glib")))
+        {
+          g_ptr_array_add (ar, g_strdup (param));
+          g_ptr_array_add (ar, g_strdup (next));
+          i++;
+        }
+      else if (next != NULL &&
+               (g_str_has_prefix (param, "--vapidir") ||
+                g_str_has_prefix (param, "--girdir") ||
+                g_str_has_prefix (param, "--metadatadir")))
+        {
+          g_ptr_array_add (ar, g_strdup (param));
+          g_ptr_array_add (ar, ide_compile_commands_resolve (self, info, next));
+          i++;
+        }
+    }
+
+  g_free (*argv);
+
+  g_ptr_array_add (ar, NULL);
+  *argv = (gchar **)g_ptr_array_free (ar, FALSE);
 }
 
 /**
@@ -497,29 +596,29 @@ ide_compile_commands_lookup (IdeCompileCommands  *self,
                              GFile              **directory,
                              GError             **error)
 {
-  g_auto(GStrv) argv = NULL;
   g_autofree gchar *base = NULL;
-  CompileInfo *info;
-  gint argc = 0;
+  const CompileInfo *info;
+  const gchar *dot;
 
   g_return_val_if_fail (IDE_IS_COMPILE_COMMANDS (self), NULL);
   g_return_val_if_fail (G_IS_FILE (file), NULL);
 
+  base = g_file_get_basename (file);
+  dot = strrchr (base, '.');
+
   if (self->info_by_file != NULL &&
       NULL != (info = g_hash_table_lookup (self->info_by_file, file)))
     {
-      const gchar *dot;
+      g_auto(GStrv) argv = NULL;
+      gint argc = 0;
 
       if (!g_shell_parse_argv (info->command, &argc, &argv, error))
         return NULL;
 
-      base = g_file_get_basename (file);
-      dot = strrchr (base, '.');
-
       if (suffix_is_c_like (dot))
         ide_compile_commands_filter_c (self, info, &argv);
       else if (suffix_is_vala (dot))
-        ide_compile_commands_filter_vala (self, &argv);
+        ide_compile_commands_filter_vala (self, info, &argv);
 
       if (directory != NULL)
         *directory = g_object_ref (info->directory);
@@ -527,6 +626,33 @@ ide_compile_commands_lookup (IdeCompileCommands  *self,
       return g_steal_pointer (&argv);
     }
 
+  /*
+   * Some compile-commands databases will give us info about .vala, but there
+   * may only be a single valac command to run. While we parsed the JSON
+   * document we stored information about each of the Vala files in a special
+   * list for exactly this purpose.
+   */
+  if (ide_str_equal0 (dot, ".vala") && self->vala_info != NULL)
+    {
+      for (guint i = 0; i < self->vala_info->len; i++)
+        {
+          g_auto(GStrv) argv = NULL;
+          gint argc = 0;
+
+          info = g_ptr_array_index (self->vala_info, i);
+
+          if (!g_shell_parse_argv (info->command, &argc, &argv, NULL))
+            continue;
+
+          ide_compile_commands_filter_vala (self, info, &argv);
+
+          if (directory != NULL)
+            *directory = g_object_ref (info->directory);
+
+          return g_steal_pointer (&argv);
+        }
+    }
+
   g_set_error_literal (error,
                        G_IO_ERROR,
                        G_IO_ERROR_NOT_FOUND,
diff --git a/src/tests/data/test-ide-compile-commands.json b/src/tests/data/test-ide-compile-commands.json
index 81acde3..5652427 100644
--- a/src/tests/data/test-ide-compile-commands.json
+++ b/src/tests/data/test-ide-compile-commands.json
@@ -8,5 +8,10 @@
     "directory": "/build/gnome-builder/build",
     "command": "cc  -Isubprojects/libgd/libgd/gd@sha -Isubprojects/libgd/libgd -I../subprojects/libgd/libgd 
-Isubprojects/libgd -I../subprojects/libgd -I/opt/gnome/include/gtk-3.0 -I/opt/gnome/include/pango-1.0 
-I/opt/gnome/include/glib-2.0 -I/opt/gnome/lib/glib-2.0/include -I/usr/include/cairo -I/usr/include/pixman-1 
-I/usr/include/freetype2 -I/usr/include/libpng16 -I/opt/gnome/include/harfbuzz 
-I/opt/gnome/include/gdk-pixbuf-2.0 -I/opt/gnome/include/gio-unix-2.0/ -I/opt/gnome/include/atk-1.0 
-I/opt/gnome/include/at-spi2-atk/2.0 -I/opt/gnome/include/at-spi-2.0 -I/usr/include/dbus-1.0 
-I/usr/lib64/dbus-1.0/include -I/home/christian/Projects/gnome-builder/build -fdiagnostics-color=always -pipe 
-D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wextra -std=gnu11 -O0 -g -DHAVE_CONFIG_H -D_GNU_SOURCE 
-DIDE_COMPILATION -ggdb -O0 -fno-omit-frame-pointer -fPIC -pthread -DLIBGD_TAGGED_ENTRY=1 
'-DG_LOG_DOMAIN=\"libgd\"' -DG_DISABLE_DEPRECATED -MMD -MQ 'subprojects/libgd/libgd/gd@sha/gd-tagg
 ed-entry.c.o' -MF 'subprojects/libgd/libgd/gd@sha/gd-tagged-entry.c.o.d' -o 
'subprojects/libgd/libgd/gd@sha/gd-tagged-entry.c.o' -c ../subprojects/libgd/libgd/gd-tagged-entry.c",
     "file": "../subprojects/libgd/libgd/gd-tagged-entry.c"
+  },
+  {
+    "directory": "/build/gnome-builder/build",
+    "command": "valac -C --debug --pkg json-glib-1.0 --pkg gtksourceview-3.0 --pkg gtk+-3.0 --pkg gio-2.0",
+    "file": "../no/such/file.vala"
   }
 ]
diff --git a/src/tests/test-ide-compile-commands.c b/src/tests/test-ide-compile-commands.c
index a6d15f8..86f926e 100644
--- a/src/tests/test-ide-compile-commands.c
+++ b/src/tests/test-ide-compile-commands.c
@@ -26,10 +26,12 @@ test_compile_commands_basic (void)
   g_autoptr(GFile) data_file = NULL;
   g_autoptr(GFile) expected_file = NULL;
   g_autoptr(GFile) dir = NULL;
+  g_autoptr(GFile) vala = NULL;
   g_autoptr(GError) error = NULL;
   g_autofree gchar *data_path = NULL;
   g_autofree gchar *dir_path = NULL;
   g_auto(GStrv) cmdstrv = NULL;
+  g_auto(GStrv) valastrv = NULL;
   gboolean r;
 
   commands = ide_compile_commands_new ();
@@ -54,6 +56,16 @@ test_compile_commands_basic (void)
   g_assert_cmpstr (cmdstrv[0], ==, "-I/build/gnome-builder/build/subprojects/libgd/libgd/gd@sha");
   dir_path = g_file_get_path (dir);
   g_assert_cmpstr (dir_path, ==, "/build/gnome-builder/build");
+
+  /* Vala files don't need to match on exact filename, just something dot vala */
+  vala = g_file_new_for_path ("whatever.vala");
+  valastrv = ide_compile_commands_lookup (commands, vala, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (valastrv != NULL);
+  g_assert_cmpstr (valastrv[0], ==, "--pkg");
+  g_assert_cmpstr (valastrv[1], ==, "json-glib-1.0");
+  g_assert_cmpstr (valastrv[2], ==, "--pkg");
+  g_assert_cmpstr (valastrv[3], ==, "gtksourceview-3.0");
 }
 
 gint


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]