[gnome-builder] build-result: add basic errorformat extraction



commit 2dcaa242ad975e4aab1f400bdd25c67e9f1de32b
Author: Christian Hergert <chergert redhat com>
Date:   Fri Dec 18 03:59:04 2015 -0800

    build-result: add basic errorformat extraction
    
    This needs to be abstractd into log observers, but this lets us make
    forward progress on the build panel for error viewing.

 libide/ide-build-result.c |  217 ++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 204 insertions(+), 13 deletions(-)
---
diff --git a/libide/ide-build-result.c b/libide/ide-build-result.c
index 0daa132..ca37b64 100644
--- a/libide/ide-build-result.c
+++ b/libide/ide-build-result.c
@@ -19,8 +19,11 @@
 #include <gio/gunixoutputstream.h>
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
+#include <libpeas/peas.h>
 
 #include "ide-build-result.h"
+#include "ide-file.h"
+#include "ide-source-location.h"
 
 typedef struct
 {
@@ -35,9 +38,17 @@ typedef struct
   GTimer        *timer;
   gchar         *mode;
 
+  gchar         *current_dir;
+
   guint          running : 1;
 } IdeBuildResultPrivate;
 
+typedef struct
+{
+  IdeBuildResult *self;
+  GOutputStream  *writer;
+} Tail;
+
 G_DEFINE_TYPE_WITH_PRIVATE (IdeBuildResult, ide_build_result, IDE_TYPE_OBJECT)
 
 enum {
@@ -54,6 +65,164 @@ enum {
 
 static GParamSpec *properties [LAST_PROP];
 static guint signals [LAST_SIGNAL];
+static GPtrArray *errorformats;
+
+static void
+ide_build_result_add_error_format (const gchar *format)
+{
+  g_autoptr(GError) error = NULL;
+  GRegex *regex;
+
+  g_assert (format != NULL);
+
+  regex = g_regex_new (format, G_REGEX_OPTIMIZE | G_REGEX_CASELESS, 0, &error);
+
+  if (regex == NULL)
+    g_warning ("%s", error->message);
+  else
+    g_ptr_array_add (errorformats, regex);
+}
+
+static IdeDiagnosticSeverity
+parse_severity (const gchar *str)
+{
+  g_autofree gchar *lower = NULL;
+
+  if (str == NULL)
+    return IDE_DIAGNOSTIC_WARNING;
+
+  lower = g_utf8_strdown (str, -1);
+
+  if (strstr (lower, "fatal") != NULL)
+    return IDE_DIAGNOSTIC_FATAL;
+
+  if (strstr (lower, "error") != NULL)
+    return IDE_DIAGNOSTIC_ERROR;
+
+  if (strstr (lower, "warning") != NULL)
+    return IDE_DIAGNOSTIC_WARNING;
+
+  if (strstr (lower, "ignored") != NULL)
+    return IDE_DIAGNOSTIC_IGNORED;
+
+  if (strstr (lower, "deprecated") != NULL)
+    return IDE_DIAGNOSTIC_DEPRECATED;
+
+  if (strstr (lower, "note") != NULL)
+    return IDE_DIAGNOSTIC_NOTE;
+
+  return IDE_DIAGNOSTIC_WARNING;
+}
+
+static IdeDiagnostic *
+ide_build_result_create_diagnostic (IdeBuildResult *self,
+                                    GMatchInfo     *match_info)
+{
+  IdeBuildResultPrivate *priv = ide_build_result_get_instance_private (self);
+  g_autofree gchar *filename = NULL;
+  g_autofree gchar *line = NULL;
+  g_autofree gchar *column = NULL;
+  g_autofree gchar *message = NULL;
+  g_autofree gchar *level = NULL;
+  g_autoptr(IdeFile) file = NULL;
+  g_autoptr(IdeSourceLocation) location = NULL;
+  IdeDiagnostic *diagnostic;
+  IdeContext *context;
+  struct {
+    gint64 line;
+    gint64 column;
+    IdeDiagnosticSeverity severity;
+  } parsed;
+
+  g_assert (IDE_IS_BUILD_RESULT (self));
+  g_assert (match_info != NULL);
+
+  filename = g_match_info_fetch_named (match_info, "filename");
+  line = g_match_info_fetch_named (match_info, "line");
+  column = g_match_info_fetch_named (match_info, "column");
+  message = g_match_info_fetch_named (match_info, "message");
+  level = g_match_info_fetch_named (match_info, "level");
+
+  parsed.line = g_ascii_strtoll (line, NULL, 10);
+  if (parsed.line < 1 || parsed.line > G_MAXINT32)
+    return NULL;
+  parsed.line--;
+
+  parsed.column = g_ascii_strtoll (column, NULL, 10);
+  if (parsed.column < 1 || parsed.column > G_MAXINT32)
+    return NULL;
+  parsed.column--;
+
+  parsed.severity = parse_severity (level);
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+
+  if (priv->current_dir)
+    {
+      gchar *path;
+
+      path = g_build_filename (priv->current_dir, filename, NULL);
+      g_free (filename);
+      filename = path;
+    }
+
+  file = ide_file_new_for_path (context, filename);
+  location = ide_source_location_new (file, parsed.line, parsed.column, 0);
+  diagnostic = ide_diagnostic_new (parsed.severity, message, location);
+
+  return diagnostic;
+}
+
+static void
+_ide_build_result_extract (IdeBuildResult *self,
+                           const gchar    *line)
+{
+  IdeBuildResultPrivate *priv = ide_build_result_get_instance_private (self);
+  const gchar *enterdir;
+  guint i;
+
+  g_assert (IDE_IS_BUILD_RESULT (self));
+  g_assert (line != NULL);
+
+  /*
+   * TODO: This should all be abstracted into log observers.  Various build
+   *       systems will need to do different tricks to track the current dir,
+   *       as well as errorformat extractions.
+   */
+
+  if ((enterdir = strstr (line, "Entering directory '")))
+    {
+      gsize len;
+
+      enterdir += IDE_LITERAL_LENGTH ("Entering directory '");
+      len = strlen (enterdir);
+
+      if (len > 0)
+        {
+          g_free (priv->current_dir);
+          priv->current_dir = g_strndup (enterdir, len - 1);
+        }
+    }
+
+  for (i = 0; i < errorformats->len; i++)
+    {
+      GRegex *regex = g_ptr_array_index (errorformats, i);
+      GMatchInfo *match_info = NULL;
+
+      if (g_regex_match (regex, line, 0, &match_info))
+        {
+          IdeDiagnostic *diagnostic;
+
+          if ((diagnostic = ide_build_result_create_diagnostic (self, match_info)))
+            {
+              g_signal_emit (self, signals [DIAGNOSTIC], 0, diagnostic);
+              ide_diagnostic_unref (diagnostic);
+            }
+        }
+
+      g_match_info_free (match_info);
+    }
+}
 
 static gboolean
 _ide_build_result_open_log (IdeBuildResult  *self,
@@ -86,13 +255,16 @@ _ide_build_result_open_log (IdeBuildResult  *self,
 }
 
 static void
-_ide_build_result_log (GOutputStream  *stream,
+_ide_build_result_log (IdeBuildResult *self,
+                       GOutputStream  *stream,
                        const gchar    *message)
 {
   g_autofree gchar *buffer = NULL;
 
-  g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
-  g_return_if_fail (message);
+  g_assert (G_IS_OUTPUT_STREAM (stream));
+  g_assert (message != NULL);
+
+  _ide_build_result_extract (self, message);
 
   /*
    * TODO: Is there a better way we can do this to just add a newline
@@ -125,7 +297,7 @@ ide_build_result_log_stdout (IdeBuildResult *self,
       msg = g_strdup_vprintf (format, args);
       va_end (args);
 
-      _ide_build_result_log (priv->stdout_writer, msg);
+      _ide_build_result_log (self, priv->stdout_writer, msg);
     }
 }
 
@@ -147,7 +319,7 @@ ide_build_result_log_stderr (IdeBuildResult *self,
       msg = g_strdup_vprintf (format, args);
       va_end (args);
 
-      _ide_build_result_log (priv->stderr_writer, msg);
+      _ide_build_result_log (self, priv->stderr_writer, msg);
     }
 }
 
@@ -217,25 +389,31 @@ ide_build_result_tail_cb (GObject      *object,
                           gpointer      user_data)
 {
   GDataInputStream *reader = (GDataInputStream *)object;
-  g_autoptr(GOutputStream) writer = user_data;
   g_autofree gchar *line = NULL;
   g_autoptr(GError) error = NULL;
+  Tail *tail = user_data;
   gsize n_read;
 
-  g_return_if_fail (G_IS_INPUT_STREAM (reader));
-  g_return_if_fail (G_IS_OUTPUT_STREAM (writer));
+  g_assert (G_IS_INPUT_STREAM (reader));
+  g_assert (tail != NULL);
+  g_assert (G_IS_OUTPUT_STREAM (tail->writer));
 
-  line = g_data_input_stream_read_line_finish_utf8 (reader, result, &n_read,
-                                                    &error);
+  line = g_data_input_stream_read_line_finish_utf8 (reader, result, &n_read, &error);
 
   if (line)
     {
-      _ide_build_result_log (writer, line);
+      _ide_build_result_log (tail->self, tail->writer, line);
       g_data_input_stream_read_line_async (reader,
                                            G_PRIORITY_DEFAULT,
                                            NULL,
                                            ide_build_result_tail_cb,
-                                           g_object_ref (writer));
+                                           tail);
+    }
+  else
+    {
+      g_object_unref (tail->self);
+      g_object_unref (tail->writer);
+      g_slice_free1 (sizeof *tail, tail);
     }
 }
 
@@ -245,6 +423,7 @@ ide_build_result_tail_into (IdeBuildResult *self,
                             GOutputStream  *writer)
 {
   g_autoptr(GDataInputStream) data_reader = NULL;
+  Tail *tail;
 
   g_return_if_fail (IDE_IS_BUILD_RESULT (self));
   g_return_if_fail (G_IS_INPUT_STREAM (reader));
@@ -252,11 +431,15 @@ ide_build_result_tail_into (IdeBuildResult *self,
 
   data_reader = g_data_input_stream_new (reader);
 
+  tail = g_slice_alloc0 (sizeof *tail);
+  tail->self = g_object_ref (self);
+  tail->writer = g_object_ref (writer);
+
   g_data_input_stream_read_line_async (data_reader,
                                        G_PRIORITY_DEFAULT,
                                        NULL,
                                        ide_build_result_tail_cb,
-                                       g_object_ref (writer));
+                                       tail);
 }
 
 void
@@ -381,6 +564,14 @@ ide_build_result_class_init (IdeBuildResultClass *klass)
                   G_STRUCT_OFFSET (IdeBuildResultClass, diagnostic),
                   NULL, NULL, NULL,
                   G_TYPE_NONE, 1, IDE_TYPE_DIAGNOSTIC);
+
+  errorformats = g_ptr_array_new ();
+
+  ide_build_result_add_error_format ("(?<filename>[a-zA-Z0-9\\-\\.]+):"
+                                     "(?<line>\\d+):"
+                                     "(?<column>\\d+): "
+                                     "(?<level>[\\w\\s]+): "
+                                     "(?<message>.*)");
 }
 
 static void


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