[gnome-builder] compile-commands: add IdeCompileCommands



commit d686eca35843c2273a2d6b59bff7e2a7c1a9a7fe
Author: Christian Hergert <chergert redhat com>
Date:   Mon Oct 16 16:26:32 2017 -0700

    compile-commands: add IdeCompileCommands
    
    This is a helper object to simplify the process of working with
    clang-style compile_commands.json databases. Some build systems
    such as Meson and CMake can benefit from having access to this
    information in a unified manner.
    
    Now that IdeBuildSystem can automatically translate paths from
    the runtime build dir, this should allow us to remove some code
    from those plugins (and share it in libide instead).
    
    We might still want something to resolve things like -I includes
    based on the relative working directory, but in practice, that
    should match the $builddir of the build pipeline.

 src/libide/buildsystem/ide-compile-commands.c |  412 +++++++++++++++++++++++++
 src/libide/buildsystem/ide-compile-commands.h |   47 +++
 src/libide/buildsystem/meson.build            |    2 +
 src/libide/ide.h                              |    1 +
 src/tests/data/test-ide-compile-commands.json |   12 +
 src/tests/meson.build                         |    8 +
 src/tests/test-ide-compile-commands.c         |   65 ++++
 7 files changed, 547 insertions(+), 0 deletions(-)
---
diff --git a/src/libide/buildsystem/ide-compile-commands.c b/src/libide/buildsystem/ide-compile-commands.c
new file mode 100644
index 0000000..200a44c
--- /dev/null
+++ b/src/libide/buildsystem/ide-compile-commands.c
@@ -0,0 +1,412 @@
+/* ide-compile-commands.c
+ *
+ * Copyright © 2017 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-compile-commands"
+
+#include <json-glib/json-glib.h>
+
+#include "ide-debug.h"
+
+#include "buildsystem/ide-compile-commands.h"
+
+/**
+ * SECTION:ide-compile-commands
+ * @title: IdeCompileCommands
+ * @short_description: Integration with compile_commands.json
+ *
+ * The #IdeCompileCommands object provides a simplified interface to
+ * interact with compile_commands.json files which are generated by a
+ * number of build systems, including Clang tooling, Meson and CMake.
+ *
+ * Create a new #IdeCompileCommands instance, and then asynchronously
+ * load the file using ide_compile_commands_load_async(). After the
+ * database has been loaded, you can access build commands using
+ * ide_compile_commands_lookup().
+ *
+ * Due to the rather unfortunate design of JSON, this file holds on
+ * to a number of strings during the lifetime of the object, for each
+ * of the compile commands. On larger projects, this can be the order
+ * of a couple of megabytes.
+ *
+ * Since: 3.28
+ */
+
+struct _IdeCompileCommands
+{
+  GObject     parent_instance;
+  GHashTable *info_by_file;
+  guint       has_loaded : 1;
+};
+
+typedef struct
+{
+  GFile *directory;
+  GFile *file;
+  gchar *command;
+} CompileInfo;
+
+G_DEFINE_TYPE (IdeCompileCommands, ide_compile_commands, G_TYPE_OBJECT)
+
+static void
+compile_info_free (gpointer data)
+{
+  CompileInfo *info = data;
+
+  if (info != NULL)
+    {
+      g_clear_object (&info->directory);
+      g_clear_object (&info->file);
+      g_clear_pointer (&info->command, g_free);
+      g_slice_free (CompileInfo, info);
+    }
+}
+
+static void
+ide_compile_commands_finalize (GObject *object)
+{
+  IdeCompileCommands *self = (IdeCompileCommands *)object;
+
+  g_clear_pointer (&self->info_by_file, g_hash_table_unref);
+
+  G_OBJECT_CLASS (ide_compile_commands_parent_class)->finalize (object);
+}
+
+static void
+ide_compile_commands_class_init (IdeCompileCommandsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_compile_commands_finalize;
+}
+
+static void
+ide_compile_commands_init (IdeCompileCommands *self)
+{
+}
+
+/**
+ * ide_compile_commands_new:
+ *
+ * Creates a new #IdeCompileCommands object which can be used to parse
+ * clang-style compile commands database files (compile_commands.json).
+ *
+ * Returns: The newly created #IdeCompileCommands
+ *
+ * Since: 3.28
+ */
+IdeCompileCommands *
+ide_compile_commands_new (void)
+{
+  return g_object_new (IDE_TYPE_COMPILE_COMMANDS, NULL);
+}
+
+static void
+ide_compile_commands_load_worker (GTask        *task,
+                                  gpointer      source_object,
+                                  gpointer      task_data,
+                                  GCancellable *cancellable)
+{
+  IdeCompileCommands *self = source_object;
+  GFile *gfile = task_data;
+  g_autoptr(JsonParser) parser = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GHashTable) info_by_file = NULL;
+  g_autoptr(GHashTable) directories_by_path = NULL;
+  g_autofree gchar *contents = NULL;
+  JsonNode *root;
+  JsonArray *ar;
+  gsize len = 0;
+  guint n_items;
+
+  IDE_ENTRY;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (IDE_IS_COMPILE_COMMANDS (self));
+  g_assert (G_IS_FILE (gfile));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  parser = json_parser_new ();
+
+  if (!g_file_load_contents (gfile, cancellable, &contents, &len, NULL, &error) ||
+      !json_parser_load_from_data (parser, contents, len, &error))
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
+    }
+
+  if (NULL == (root = json_parser_get_root (parser)) ||
+      !JSON_NODE_HOLDS_ARRAY (root) ||
+      NULL == (ar = json_node_get_array (root)))
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_INVALID_DATA,
+                               "Failed to extract commands, invalid json");
+      IDE_EXIT;
+    }
+
+  info_by_file = g_hash_table_new_full (g_file_hash,
+                                        (GEqualFunc)g_file_equal,
+                                        NULL,
+                                        compile_info_free);
+
+  directories_by_path = g_hash_table_new_full (g_str_hash,
+                                               g_str_equal,
+                                               NULL,
+                                               g_object_unref);
+
+  n_items = json_array_get_length (ar);
+
+  for (guint i = 0; i < n_items; i++)
+    {
+      CompileInfo *info;
+      JsonNode *item;
+      JsonNode *value;
+      JsonObject *obj;
+      GFile *dir;
+      const gchar *directory = NULL;
+      const gchar *file = NULL;
+      const gchar *command = NULL;
+
+      item = json_array_get_element (ar, i);
+
+      /* Skip past this node if its invalid for some reason, so we
+       * can try to be tolerante of errors created by broken tooling.
+       */
+      if (item == NULL ||
+          !JSON_NODE_HOLDS_OBJECT (item) ||
+          NULL == (obj = json_node_get_object (item)))
+        continue;
+
+      if (json_object_has_member (obj, "file") &&
+          NULL != (value = json_object_get_member (obj, "file")) &&
+          JSON_NODE_HOLDS_VALUE (value))
+        file = json_node_get_string (value);
+
+      if (json_object_has_member (obj, "directory") &&
+          NULL != (value = json_object_get_member (obj, "directory")) &&
+          JSON_NODE_HOLDS_VALUE (value))
+        directory = json_node_get_string (value);
+
+      if (json_object_has_member (obj, "command") &&
+          NULL != (value = json_object_get_member (obj, "command")) &&
+          JSON_NODE_HOLDS_VALUE (value))
+        command = json_node_get_string (value);
+
+      /* Ignore items that are missing something or other */
+      if (file == NULL || command == NULL || directory == NULL)
+        continue;
+
+      /* Try to reduce the number of GFile we have for directories */
+      if (NULL == (dir = g_hash_table_lookup (directories_by_path, directory)))
+        {
+          dir = g_file_new_for_path (directory);
+          g_hash_table_insert (directories_by_path, (gchar *)directory, dir);
+        }
+
+      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_hash_table_insert (info_by_file, info->file, info);
+    }
+
+  self->info_by_file = g_steal_pointer (&info_by_file);
+
+  g_task_return_boolean (task, TRUE);
+
+  IDE_EXIT;
+}
+
+/**
+ * ide_compile_commands_load:
+ * @self: An #IdeCompileCommands
+ * @file: A #GFile
+ * @cancellable: (nullable): A #GCancellable, or %NULL
+ * @error: A location for a #GError, or %NULL
+ *
+ * Synchronously loads the contents of the requested @file and parses
+ * the JSON command database contained within.
+ *
+ * You may only call this function once on an #IdeCompileCommands object.
+ * If there is a failure, you must create a new #IdeCompileCommands instance
+ * instead of calling this function again.
+ *
+ * See also: ide_compile_commands_load_async()
+ *
+ * Returns: %TRUE if successful; otherwise %FALSE and @error is set.
+ *
+ * Since: 3.28
+ */
+gboolean
+ide_compile_commands_load (IdeCompileCommands  *self,
+                           GFile               *file,
+                           GCancellable        *cancellable,
+                           GError             **error)
+{
+  g_autoptr(GTask) task = NULL;
+  gboolean ret;
+
+  IDE_ENTRY;
+
+  g_return_val_if_fail (IDE_IS_COMPILE_COMMANDS (self), FALSE);
+  g_return_val_if_fail (self->has_loaded == FALSE, FALSE);
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+
+  self->has_loaded = TRUE;
+
+  task = g_task_new (self, cancellable, NULL, NULL);
+  g_task_set_priority (task, G_PRIORITY_LOW);
+  g_task_set_source_tag (task, ide_compile_commands_load);
+  g_task_set_task_data (task, g_object_ref (file), g_object_unref);
+  g_task_run_in_thread_sync (task, ide_compile_commands_load_worker);
+
+  ret = g_task_propagate_boolean (task, error);
+
+  IDE_RETURN (ret);
+}
+
+/**
+ * ide_compile_commands_load_async:
+ * @self: An #IdeCompileCommands
+ * @file: A #GFile
+ * @cancellable: (nullable): A #GCancellable, or %NULL
+ * @callback: the callback for the async operation
+ * @user_data: user data for @callback
+ *
+ * Asynchronously loads the contents of the requested @file and parses
+ * the JSON command database contained within.
+ *
+ * You may only call this function once on an #IdeCompileCommands object.
+ * If there is a failure, you must create a new #IdeCompileCommands instance
+ * instead of calling this function again.
+ *
+ * See also: ide_compile_commands_load_finish()
+ *
+ * Since: 3.28
+ */
+void
+ide_compile_commands_load_async (IdeCompileCommands  *self,
+                                 GFile               *file,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+
+  IDE_ENTRY;
+
+  g_return_if_fail (IDE_IS_COMPILE_COMMANDS (self));
+  g_return_if_fail (self->has_loaded == FALSE);
+  g_return_if_fail (G_IS_FILE (file));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  self->has_loaded = TRUE;
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_priority (task, G_PRIORITY_LOW);
+  g_task_set_source_tag (task, ide_compile_commands_load_async);
+  g_task_set_task_data (task, g_object_ref (file), g_object_unref);
+  g_task_run_in_thread (task, ide_compile_commands_load_worker);
+
+  IDE_EXIT;
+}
+
+/**
+ * ide_compile_commands_load_finish:
+ * @self: An #IdeCompileCommands
+ * @result: A #GAsyncResult provided to the callback
+ * @error: A location for a #GError, or %NULL
+ *
+ * Completes an asynchronous request to ide_compile_commands_load_async().
+ *
+ * See also: ide_compile_commands_load_async()
+ *
+ * Returns: %TRUE if the file was loaded successfully; otherwise %FALSE
+ *   and @error is set.
+ *
+ * Since: 3.28
+ */
+gboolean
+ide_compile_commands_load_finish (IdeCompileCommands  *self,
+                                  GAsyncResult        *result,
+                                  GError             **error)
+{
+  gboolean ret;
+
+  IDE_ENTRY;
+
+  g_return_val_if_fail (IDE_IS_COMPILE_COMMANDS (self), FALSE);
+  g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+  ret = g_task_propagate_boolean (G_TASK (result), error);
+
+  IDE_RETURN (ret);
+}
+
+/**
+ * ide_compile_commands_lookup:
+ * @self: An #IdeCompileCommands
+ * @file: A #GFile representing the file to lookup
+ * @directory: (out) (optional) (transfer full): A location for a #GFile, or %NULL
+ * @error: A location for a #GError, or %NULL
+ *
+ * Locates the commands to compile the @file requested.
+ *
+ * If @directory is non-NULL, then the directory to run the command from
+ * is placed in @directory.
+ *
+ * Returns: (nullable) (transfer full): A string array or %NULL if
+ *   there was a failure to locate or parse the command.
+ *
+ * Since: 3.28
+ */
+gchar **
+ide_compile_commands_lookup (IdeCompileCommands  *self,
+                             GFile               *file,
+                             GFile              **directory,
+                             GError             **error)
+{
+  CompileInfo *info;
+  g_auto(GStrv) argv = NULL;
+  gint argc = 0;
+
+  g_return_val_if_fail (IDE_IS_COMPILE_COMMANDS (self), NULL);
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  if (self->info_by_file != NULL &&
+      NULL != (info = g_hash_table_lookup (self->info_by_file, file)))
+    {
+      if (!g_shell_parse_argv (info->command, &argc, &argv, error))
+        return NULL;
+
+      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,
+                       "Failed to locate command for requested file");
+
+  return NULL;
+}
diff --git a/src/libide/buildsystem/ide-compile-commands.h b/src/libide/buildsystem/ide-compile-commands.h
new file mode 100644
index 0000000..1a77305
--- /dev/null
+++ b/src/libide/buildsystem/ide-compile-commands.h
@@ -0,0 +1,47 @@
+/* ide-compile-commands.h
+ *
+ * Copyright © 2017 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_COMPILE_COMMANDS (ide_compile_commands_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeCompileCommands, ide_compile_commands, IDE, COMPILE_COMMANDS, GObject)
+
+IdeCompileCommands  *ide_compile_commands_new         (void);
+gboolean             ide_compile_commands_load        (IdeCompileCommands   *self,
+                                                       GFile                *file,
+                                                       GCancellable         *cancellable,
+                                                       GError              **error);
+void                 ide_compile_commands_load_async  (IdeCompileCommands   *self,
+                                                       GFile                *file,
+                                                       GCancellable         *cancellable,
+                                                       GAsyncReadyCallback   callback,
+                                                       gpointer              user_data);
+gboolean             ide_compile_commands_load_finish (IdeCompileCommands   *self,
+                                                       GAsyncResult         *result,
+                                                       GError              **error);
+gchar              **ide_compile_commands_lookup      (IdeCompileCommands   *self,
+                                                       GFile                *file,
+                                                       GFile               **directory,
+                                                       GError              **error);
+
+G_END_DECLS
diff --git a/src/libide/buildsystem/meson.build b/src/libide/buildsystem/meson.build
index 9a90d16..3fc55d4 100644
--- a/src/libide/buildsystem/meson.build
+++ b/src/libide/buildsystem/meson.build
@@ -11,6 +11,7 @@ buildsystem_headers = [
   'ide-build-system.h',
   'ide-build-target.h',
   'ide-build-utils.h',
+  'ide-compile-commands.h',
   'ide-configuration-manager.h',
   'ide-configuration-provider.h',
   'ide-configuration.h',
@@ -30,6 +31,7 @@ buildsystem_sources = [
   'ide-build-system.c',
   'ide-build-target.c',
   'ide-build-utils.c',
+  'ide-compile-commands.c',
   'ide-configuration-manager.c',
   'ide-configuration-provider.c',
   'ide-configuration.c',
diff --git a/src/libide/ide.h b/src/libide/ide.h
index 2958a44..a12d853 100644
--- a/src/libide/ide.h
+++ b/src/libide/ide.h
@@ -57,6 +57,7 @@ G_BEGIN_DECLS
 #include "buildsystem/ide-build-system.h"
 #include "buildsystem/ide-build-system-discovery.h"
 #include "buildsystem/ide-build-target.h"
+#include "buildsystem/ide-compile-commands.h"
 #include "buildsystem/ide-configuration-manager.h"
 #include "buildsystem/ide-configuration.h"
 #include "buildsystem/ide-configuration-provider.h"
diff --git a/src/tests/data/test-ide-compile-commands.json b/src/tests/data/test-ide-compile-commands.json
new file mode 100644
index 0000000..81acde3
--- /dev/null
+++ b/src/tests/data/test-ide-compile-commands.json
@@ -0,0 +1,12 @@
+[
+  {
+    "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-type
 s-catalog.c.o' -MF 'subprojects/libgd/libgd/gd@sha/gd-types-catalog.c.o.d' -o 
'subprojects/libgd/libgd/gd@sha/gd-types-catalog.c.o' -c ../subprojects/libgd/libgd/gd-types-catalog.c",
+    "file": "../subprojects/libgd/libgd/gd-types-catalog.c"
+  },
+  {
+    "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"
+  }
+]
diff --git a/src/tests/meson.build b/src/tests/meson.build
index 255422c..dddfebe 100644
--- a/src/tests/meson.build
+++ b/src/tests/meson.build
@@ -20,6 +20,14 @@ ide_test_deps = [
   gnome_builder_plugins_dep,
 ]
 
+
+ide_compile_commands = executable('test-ide-compile-commands', 'test-ide-compile-commands.c',
+        c_args: ide_test_cflags,
+  dependencies: [ ide_test_deps ],
+)
+test('test-ide-compile-commands', ide_compile_commands, env: ide_test_env)
+
+
 ide_context = executable('test-ide-context',
   'test-ide-context.c',
   c_args: ide_test_cflags,
diff --git a/src/tests/test-ide-compile-commands.c b/src/tests/test-ide-compile-commands.c
new file mode 100644
index 0000000..8c6b86e
--- /dev/null
+++ b/src/tests/test-ide-compile-commands.c
@@ -0,0 +1,65 @@
+/* test-ide-compile-commands.c
+ *
+ * Copyright © 2017 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <ide.h>
+
+static void
+test_compile_commands_basic (void)
+{
+  g_autoptr(IdeCompileCommands) commands = NULL;
+  g_autoptr(GFile) missing = g_file_new_for_path ("missing");
+  g_autoptr(GFile) data_file = NULL;
+  g_autoptr(GFile) expected_file = NULL;
+  g_autoptr(GFile) dir = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autofree gchar *data_path = NULL;
+  g_autofree gchar *dir_path = NULL;
+  g_auto(GStrv) cmdstrv = NULL;
+  gboolean r;
+
+  commands = ide_compile_commands_new ();
+
+  /* Test missing info before we've loaded */
+  g_assert (NULL == ide_compile_commands_lookup (commands, missing, NULL, NULL));
+
+  /* Now load our test file */
+  data_path = g_build_filename (TEST_DATA_DIR, "test-ide-compile-commands.json", NULL);
+  data_file = g_file_new_for_path (data_path);
+  r = ide_compile_commands_load (commands, data_file, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_cmpint (r, ==, TRUE);
+
+  /* Now lookup a file that should exist in the database */
+  expected_file = g_file_new_for_path ("/build/gnome-builder/subprojects/libgd/libgd/gd-types-catalog.c");
+  cmdstrv = ide_compile_commands_lookup (commands, expected_file, &dir, &error);
+  g_assert_no_error (error);
+  g_assert (cmdstrv != NULL);
+  g_assert_cmpstr (cmdstrv[0], ==, "cc");
+  g_assert_cmpstr (cmdstrv[1], ==, "-Isubprojects/libgd/libgd/gd@sha");
+  dir_path = g_file_get_path (dir);
+  g_assert_cmpstr (dir_path, ==, "/build/gnome-builder/build");
+}
+
+gint
+main (gint argc,
+      gchar *argv[])
+{
+  g_test_init (&argc, &argv, NULL);
+  g_test_add_func ("/Ide/CompileCommands/basic", test_compile_commands_basic);
+  return g_test_run ();
+}


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